From e1dfeb818453323470f11241fe7c5256cc9efc91 Mon Sep 17 00:00:00 2001 From: Ford Date: Thu, 15 Jan 2026 13:56:16 -0800 Subject: [PATCH 01/28] graph: Add LogStore trait and core types Introduces the foundation for the log store system with: - LogStore trait for querying logs from backends - LogLevel enum with FromStr trait implementation - LogEntry and LogQuery types for structured log data - LogStoreFactory for creating backend instances - NoOpLogStore as default (disabled) implementation --- graph/src/components/log_store/mod.rs | 333 ++++++++++++++++++++++++++ graph/src/components/mod.rs | 3 + 2 files changed, 336 insertions(+) create mode 100644 graph/src/components/log_store/mod.rs diff --git a/graph/src/components/log_store/mod.rs b/graph/src/components/log_store/mod.rs new file mode 100644 index 00000000000..e951c37d1df --- /dev/null +++ b/graph/src/components/log_store/mod.rs @@ -0,0 +1,333 @@ +pub mod config; +pub mod elasticsearch; +pub mod file; +pub mod loki; + +use async_trait::async_trait; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::Arc; +use thiserror::Error; + +use crate::prelude::DeploymentHash; + +#[derive(Error, Debug)] +pub enum LogStoreError { + #[error("log store query failed: {0}")] + QueryFailed(#[from] anyhow::Error), + + #[error("log store is unavailable")] + Unavailable, + + #[error("log store initialization failed: {0}")] + InitializationFailed(anyhow::Error), + + #[error("log store configuration error: {0}")] + ConfigurationError(anyhow::Error), +} + +/// Configuration for different log store backends +#[derive(Debug, Clone)] +pub enum LogStoreConfig { + /// No logging - returns empty results + Disabled, + + /// Elasticsearch backend + Elasticsearch { + endpoint: String, + username: Option, + password: Option, + index: String, + timeout_secs: u64, + }, + + /// Loki (Grafana's log aggregation system) + Loki { + endpoint: String, + tenant_id: Option, + }, + + /// File-based logs (JSON lines format) + File { + directory: PathBuf, + max_file_size: u64, + retention_days: u32, + }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LogLevel { + Critical, + Error, + Warning, + Info, + Debug, +} + +impl LogLevel { + pub fn as_str(&self) -> &'static str { + match self { + LogLevel::Critical => "critical", + LogLevel::Error => "error", + LogLevel::Warning => "warning", + LogLevel::Info => "info", + LogLevel::Debug => "debug", + } + } +} + +impl FromStr for LogLevel { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.trim().to_lowercase().as_str() { + "critical" => Ok(LogLevel::Critical), + "error" => Ok(LogLevel::Error), + "warning" => Ok(LogLevel::Warning), + "info" => Ok(LogLevel::Info), + "debug" => Ok(LogLevel::Debug), + _ => Err(format!("Invalid log level: {}", s)), + } + } +} + +#[derive(Debug, Clone)] +pub struct LogMeta { + pub module: String, + pub line: i64, + pub column: i64, +} + +#[derive(Debug, Clone)] +pub struct LogEntry { + pub id: String, + pub subgraph_id: DeploymentHash, + pub timestamp: String, + pub level: LogLevel, + pub text: String, + pub arguments: Vec<(String, String)>, + pub meta: LogMeta, +} + +#[derive(Debug, Clone)] +pub struct LogQuery { + pub subgraph_id: DeploymentHash, + pub level: Option, + pub from: Option, + pub to: Option, + pub search: Option, + pub first: u32, + pub skip: u32, +} + +#[async_trait] +pub trait LogStore: Send + Sync + 'static { + async fn query_logs(&self, query: LogQuery) -> Result, LogStoreError>; + fn is_available(&self) -> bool; +} + +/// Factory for creating LogStore instances from configuration +pub struct LogStoreFactory; + +impl LogStoreFactory { + /// Create a LogStore from configuration + pub fn from_config(config: LogStoreConfig) -> Result, LogStoreError> { + match config { + LogStoreConfig::Disabled => Ok(Arc::new(NoOpLogStore)), + + LogStoreConfig::Elasticsearch { + endpoint, + username, + password, + index, + timeout_secs, + } => { + let timeout = std::time::Duration::from_secs(timeout_secs); + let client = reqwest::Client::builder() + .timeout(timeout) + .build() + .map_err(|e| LogStoreError::InitializationFailed(e.into()))?; + + let config = crate::log::elastic::ElasticLoggingConfig { + endpoint, + username, + password, + client, + }; + + Ok(Arc::new(elasticsearch::ElasticsearchLogStore::new( + config, index, timeout, + ))) + } + + LogStoreConfig::Loki { + endpoint, + tenant_id, + } => Ok(Arc::new(loki::LokiLogStore::new(endpoint, tenant_id)?)), + + LogStoreConfig::File { + directory, + max_file_size, + retention_days, + } => Ok(Arc::new(file::FileLogStore::new( + directory, + max_file_size, + retention_days, + )?)), + } + } + + /// Parse configuration from environment variables + /// + /// Supports both new (GRAPH_LOG_STORE_*) and old (deprecated) environment variable names + /// for backward compatibility. The new keys take precedence when both are set. + pub fn from_env() -> Result { + // Logger for deprecation warnings + let logger = crate::log::logger(false); + + // Read backend selector with backward compatibility + let backend = config::read_env_with_default( + &logger, + "GRAPH_LOG_STORE_BACKEND", + "GRAPH_LOG_STORE", + "disabled", + ); + + match backend.to_lowercase().as_str() { + "disabled" | "none" => Ok(LogStoreConfig::Disabled), + + "elasticsearch" | "elastic" | "es" => { + let endpoint = config::read_env_with_fallback( + &logger, + "GRAPH_LOG_STORE_ELASTICSEARCH_URL", + "GRAPH_ELASTICSEARCH_URL", + ) + .ok_or_else(|| { + LogStoreError::ConfigurationError(anyhow::anyhow!( + "Elasticsearch endpoint not set. Use GRAPH_LOG_STORE_ELASTICSEARCH_URL environment variable" + )) + })?; + + let username = config::read_env_with_fallback( + &logger, + "GRAPH_LOG_STORE_ELASTICSEARCH_USER", + "GRAPH_ELASTICSEARCH_USER", + ); + + let password = config::read_env_with_fallback( + &logger, + "GRAPH_LOG_STORE_ELASTICSEARCH_PASSWORD", + "GRAPH_ELASTICSEARCH_PASSWORD", + ); + + let index = config::read_env_with_default( + &logger, + "GRAPH_LOG_STORE_ELASTICSEARCH_INDEX", + "GRAPH_ELASTIC_SEARCH_INDEX", + "subgraph", + ); + + // Default: 10 seconds query timeout + // Configurable via GRAPH_LOG_STORE_ELASTICSEARCH_TIMEOUT environment variable + let timeout_secs = config::read_u64_with_fallback( + &logger, + "GRAPH_LOG_STORE_ELASTICSEARCH_TIMEOUT", + "GRAPH_ELASTICSEARCH_TIMEOUT", + 10, + ); + + Ok(LogStoreConfig::Elasticsearch { + endpoint, + username, + password, + index, + timeout_secs, + }) + } + + "loki" => { + let endpoint = config::read_env_with_fallback( + &logger, + "GRAPH_LOG_STORE_LOKI_URL", + "GRAPH_LOG_LOKI_ENDPOINT", + ) + .ok_or_else(|| { + LogStoreError::ConfigurationError(anyhow::anyhow!( + "Loki endpoint not set. Use GRAPH_LOG_STORE_LOKI_URL environment variable" + )) + })?; + + let tenant_id = config::read_env_with_fallback( + &logger, + "GRAPH_LOG_STORE_LOKI_TENANT_ID", + "GRAPH_LOG_LOKI_TENANT", + ); + + Ok(LogStoreConfig::Loki { + endpoint, + tenant_id, + }) + } + + "file" | "files" => { + let directory = config::read_env_with_fallback( + &logger, + "GRAPH_LOG_STORE_FILE_DIR", + "GRAPH_LOG_FILE_DIR", + ) + .ok_or_else(|| { + LogStoreError::ConfigurationError(anyhow::anyhow!( + "File log directory not set. Use GRAPH_LOG_STORE_FILE_DIR environment variable" + )) + }) + .map(PathBuf::from)?; + + // Default: 100MB per file (104857600 bytes) + // Configurable via GRAPH_LOG_STORE_FILE_MAX_SIZE environment variable + let max_file_size = config::read_u64_with_fallback( + &logger, + "GRAPH_LOG_STORE_FILE_MAX_SIZE", + "GRAPH_LOG_FILE_MAX_SIZE", + 100 * 1024 * 1024, + ); + + // Default: 30 days retention + // Configurable via GRAPH_LOG_STORE_FILE_RETENTION_DAYS environment variable + let retention_days = config::read_u32_with_fallback( + &logger, + "GRAPH_LOG_STORE_FILE_RETENTION_DAYS", + "GRAPH_LOG_FILE_RETENTION_DAYS", + 30, + ); + + Ok(LogStoreConfig::File { + directory, + max_file_size, + retention_days, + }) + } + + _ => Err(LogStoreError::ConfigurationError(anyhow::anyhow!( + "Unknown log store backend: {}. Valid options: disabled, elasticsearch, loki, file", + backend + ))), + } + } +} + +/// A no-op LogStore that returns empty results. +/// +/// Used when log storage is disabled (the default). Note that subgraph logs +/// still appear in stdout/stderr - they're just not stored in a queryable format. +pub struct NoOpLogStore; + +#[async_trait] +impl LogStore for NoOpLogStore { + async fn query_logs(&self, _query: LogQuery) -> Result, LogStoreError> { + Ok(vec![]) + } + + fn is_available(&self) -> bool { + false + } +} diff --git a/graph/src/components/mod.rs b/graph/src/components/mod.rs index 8abdc96f0b0..2dfc34f6373 100644 --- a/graph/src/components/mod.rs +++ b/graph/src/components/mod.rs @@ -50,6 +50,9 @@ pub mod server; /// Components dealing with storing entities. pub mod store; +/// Components dealing with log storage. +pub mod log_store; + pub mod link_resolver; pub mod trigger_processor; From 45358b445f16b8aea891fed57858cd74b287dcbc Mon Sep 17 00:00:00 2001 From: Ford Date: Thu, 15 Jan 2026 13:56:25 -0800 Subject: [PATCH 02/28] graph: Add LogStore backend implementations Implements three log storage backends for querying logs: - FileLogStore: Streams JSON Lines files with bounded memory usage - ElasticsearchLogStore: Queries Elasticsearch indices with full-text search - LokiLogStore: Queries Grafana Loki using LogQL All backends implement the LogStore trait and support: - Filtering by log level, timestamp range, and text search - Pagination via first/skip parameters - Returning structured LogEntry objects Dependencies added: reqwest, serde_json for HTTP clients. --- Cargo.lock | 935 +++++++++++------- graph/Cargo.toml | 1 + .../src/components/log_store/elasticsearch.rs | 217 ++++ graph/src/components/log_store/file.rs | 363 +++++++ graph/src/components/log_store/loki.rs | 283 ++++++ 5 files changed, 1434 insertions(+), 365 deletions(-) create mode 100644 graph/src/components/log_store/elasticsearch.rs create mode 100644 graph/src/components/log_store/file.rs create mode 100644 graph/src/components/log_store/loki.rs diff --git a/Cargo.lock b/Cargo.lock index 145f76b0463..037038ddea0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,9 +73,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4973038846323e4e69a433916522195dce2947770076c03078fc21c80ea0f1c4" +checksum = "50ab0cd8afe573d1f7dc2353698a51b1f93aec362c8211e28cfd3948c6adba39" dependencies = [ "alloy-consensus", "alloy-contract", @@ -100,9 +100,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.2.29" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef3a72a2247c34a8545ee99e562b1b9b69168e5000567257ae51e91b4e6b1193" +checksum = "f4e9e31d834fe25fe991b8884e4b9f0e59db4a97d86e05d1464d6899c013cd62" dependencies = [ "alloy-primitives", "num_enum", @@ -111,9 +111,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c0dc44157867da82c469c13186015b86abef209bf0e41625e4b68bac61d728" +checksum = "7f16daaf7e1f95f62c6c3bf8a3fc3d78b08ae9777810c0bb5e94966c7cd57ef0" dependencies = [ "alloy-eips", "alloy-primitives", @@ -139,9 +139,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4cdb42df3871cd6b346d6a938ec2ba69a9a0f49d1f82714bc5c48349268434" +checksum = "118998d9015332ab1b4720ae1f1e3009491966a0349938a1f43ff45a8a4c6299" dependencies = [ "alloy-consensus", "alloy-eips", @@ -154,9 +154,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca63b7125a981415898ffe2a2a696c83696c9c6bdb1671c8a912946bbd8e49e7" +checksum = "7ac9e0c34dc6bce643b182049cdfcca1b8ce7d9c260cbdd561f511873b7e26cd" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -173,6 +173,7 @@ dependencies = [ "futures-util", "serde_json", "thiserror 2.0.18", + "tracing", ] [[package]] @@ -190,9 +191,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.5.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d48a9101f4a67c22fae57489f1ddf3057b8ab4a368d8eac3be088b6e9d9c9d9" +checksum = "cc2db5c583aaef0255aa63a4fe827f826090142528bba48d1bf4119b62780cad" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -253,9 +254,9 @@ dependencies = [ [[package]] name = "alloy-eip7928" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3231de68d5d6e75332b7489cfcc7f4dfabeba94d990a10e4b923af0e6623540" +checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -266,9 +267,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f7ef09f21bd1e9cb8a686f168cb4a206646804567f0889eadb8dcc4c9288c8" +checksum = "e6ef28c9fdad22d4eec52d894f5f2673a0895f1e5ef196734568e68c0f6caca8" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -286,14 +287,13 @@ dependencies = [ "serde", "serde_with", "sha2 0.10.9", - "thiserror 2.0.18", ] [[package]] name = "alloy-genesis" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9cf3b99f46615fbf7dc1add0c96553abb7bf88fc9ec70dfbe7ad0b47ba7fe8" +checksum = "bbf9480307b09d22876efb67d30cadd9013134c21f3a17ec9f93fd7536d38024" dependencies = [ "alloy-eips", "alloy-primitives", @@ -306,9 +306,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.5.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9914c147bb9b25f440eca68a31dc29f5c22298bfa7754aa802965695384122b0" +checksum = "e9dbe713da0c737d9e5e387b0ba790eb98b14dd207fe53eef50e19a5a8ec3dac" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -318,9 +318,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff42cd777eea61f370c0b10f2648a1c81e0b783066cd7269228aa993afd487f7" +checksum = "422d110f1c40f1f8d0e5562b0b649c35f345fccb7093d9f02729943dcd1eef71" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -333,9 +333,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cbca04f9b410fdc51aaaf88433cbac761213905a65fe832058bcf6690585762" +checksum = "7197a66d94c4de1591cdc16a9bcea5f8cccd0da81b865b49aef97b1b4016e0fa" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -359,9 +359,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d6d15e069a8b11f56bef2eccbad2a873c6dd4d4c81d04dda29710f5ea52f04" +checksum = "eb82711d59a43fdfd79727c99f270b974c784ec4eb5728a0d0d22f26716c87ef" dependencies = [ "alloy-consensus", "alloy-eips", @@ -372,9 +372,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.5.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db950a29746be9e2f2c6288c8bd7a6202a81f999ce109a2933d2379970ec0fa" +checksum = "de3b431b4e72cd8bd0ec7a50b4be18e73dab74de0dba180eef171055e5d5926e" dependencies = [ "alloy-rlp", "arbitrary", @@ -384,27 +384,26 @@ dependencies = [ "derive_more", "foldhash 0.2.0", "hashbrown 0.16.1", - "indexmap 2.11.4", + "indexmap 2.13.0", "itoa", "k256", "keccak-asm", "paste", "proptest", - "proptest-derive 0.6.0", + "proptest-derive 0.7.0", "rand 0.9.2", "rapidhash", "ruint", - "rustc-hash 2.1.1", + "rustc-hash", "serde", "sha3", - "tiny-keccak 2.0.2", ] [[package]] name = "alloy-provider" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d181c8cc7cf4805d7e589bf4074d56d55064fa1a979f005a45a62b047616d870" +checksum = "bf6b18b929ef1d078b834c3631e9c925177f3b23ddc6fa08a722d13047205876" dependencies = [ "alloy-chains", "alloy-consensus", @@ -436,7 +435,7 @@ dependencies = [ "lru", "parking_lot", "pin-project", - "reqwest", + "reqwest 0.13.2", "serde", "serde_json", "thiserror 2.0.18", @@ -448,9 +447,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8bd82953194dec221aa4cbbbb0b1e2df46066fe9d0333ac25b43a311e122d13" +checksum = "5ad54073131e7292d4e03e1aa2287730f737280eb160d8b579fb31939f558c11" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -463,7 +462,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.2", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", "wasmtimer", ] @@ -492,9 +491,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2792758a93ae32a32e9047c843d536e1448044f78422d71bf7d7c05149e103f" +checksum = "94fcc9604042ca80bd37aa5e232ea1cd851f337e31e2babbbb345bc0b1c30de3" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -505,12 +504,12 @@ dependencies = [ "alloy-transport-ws", "futures 0.3.31", "pin-project", - "reqwest", + "reqwest 0.13.2", "serde", "serde_json", "tokio", "tokio-stream", - "tower 0.5.2", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", "url", "wasmtimer", @@ -518,9 +517,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bdcbf9dfd5eea8bfeb078b1d906da8cd3a39c4d4dbe7a628025648e323611f6" +checksum = "4faad925d3a669ffc15f43b3deec7fbdf2adeb28a4d6f9cf4bc661698c0f8f4b" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -535,9 +534,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a3100b76987c1b1dc81f3abe592b7edc29e92b1242067a69d65e0030b35cf9" +checksum = "47df51bedb3e6062cb9981187a51e86d0d64a4de66eb0855e9efe6574b044ddf" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -547,9 +546,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd720b63f82b457610f2eaaf1f32edf44efffe03ae25d537632e7d23e7929e1a" +checksum = "3823026d1ed239a40f12364fac50726c8daf1b6ab8077a97212c5123910429ed" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -558,9 +557,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b21e1ad18ff1b31ff1030e046462ab8168cf8894e6778cd805c8bdfe2bd649" +checksum = "2145138f3214928f08cd13da3cb51ef7482b5920d8ac5a02ecd4e38d1a8f6d1e" dependencies = [ "alloy-primitives", "derive_more", @@ -570,9 +569,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ac61f03f1edabccde1c687b5b25fff28f183afee64eaa2e767def3929e4457" +checksum = "bb9b97b6e7965679ad22df297dda809b11cebc13405c1b537e5cffecc95834fa" dependencies = [ "alloy-consensus", "alloy-eips", @@ -588,9 +587,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2dc411f13092f237d2bf6918caf80977fc2f51485f9b90cb2a2f956912c8c9" +checksum = "59c095f92c4e1ff4981d89e9aa02d5f98c762a1980ab66bec49c44be11349da2" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -610,9 +609,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad79f1e27e161943b5a4f99fe5534ef0849876214be411e0032c12f38e94daa" +checksum = "2e5a4d010f86cd4e01e5205ec273911e538e1738e76d8bafe9ecd245910ea5a3" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -624,9 +623,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d459f902a2313737bc66d18ed094c25d2aeb268b74d98c26bbbda2aa44182ab0" +checksum = "942d26a2ca8891b26de4a8529d21091e21c1093e27eb99698f1a86405c76b1ff" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -636,9 +635,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2ce1e0dbf7720eee747700e300c99aac01b1a95bb93f493a01e78ee28bb1a37" +checksum = "11ece63b89294b8614ab3f483560c08d016930f842bf36da56bf0b764a15c11e" dependencies = [ "alloy-primitives", "arbitrary", @@ -648,9 +647,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2425c6f314522c78e8198979c8cbf6769362be4da381d4152ea8eefce383535d" +checksum = "43f447aefab0f1c0649f71edc33f590992d4e122bc35fb9cdbbf67d4421ace85" dependencies = [ "alloy-primitives", "async-trait", @@ -663,9 +662,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ecb71ee53d8d9c3fa7bac17542c8116ebc7a9726c91b1bf333ec3d04f5a789" +checksum = "f721f4bf2e4812e5505aaf5de16ef3065a8e26b9139ac885862d00b5a55a659a" dependencies = [ "alloy-consensus", "alloy-network", @@ -679,9 +678,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "1.5.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b96d5f5890605ba9907ce1e2158e2701587631dc005bfa582cf92dd6f21147" +checksum = "ab81bab693da9bb79f7a95b64b394718259fdd7e41dceeced4cad57cb71c4f6a" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -693,28 +692,28 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "1.5.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8247b7cca5cde556e93f8b3882b01dbd272f527836049083d240c57bf7b4c15" +checksum = "489f1620bb7e2483fb5819ed01ab6edc1d2f93939dce35a5695085a1afd1d699" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck 0.5.0", - "indexmap 2.11.4", + "indexmap 2.13.0", "proc-macro-error2", "proc-macro2", "quote", + "sha3", "syn 2.0.117", "syn-solidity", - "tiny-keccak 2.0.2", ] [[package]] name = "alloy-sol-macro-input" -version = "1.5.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd54f38512ac7bae10bbc38480eefb1b9b398ca2ce25db9cc0c048c6411c4f1" +checksum = "56cef806ad22d4392c5fc83cf8f2089f988eb99c7067b4e0c6f1971fc1cca318" dependencies = [ "alloy-json-abi", "const-hex", @@ -730,9 +729,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af67a0b0dcebe14244fc92002cd8d96ecbf65db4639d479f5fcd5805755a4c27" +checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" dependencies = [ "serde", "winnow 0.7.13", @@ -740,9 +739,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.5.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1038284171df8bfd48befc0c7b78f667a7e2be162f45f07bd1c378078ebe58" +checksum = "64612d29379782a5dde6f4b6570d9c756d734d760c0c94c254d361e678a6591f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -752,9 +751,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa186e560d523d196580c48bf00f1bf62e63041f28ecf276acc22f8b27bb9f53" +checksum = "8098f965442a9feb620965ba4b4be5e2b320f4ec5a3fff6bfa9e1ff7ef42bed1" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -767,7 +766,7 @@ dependencies = [ "serde_json", "thiserror 2.0.18", "tokio", - "tower 0.5.2", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", "url", "wasmtimer", @@ -775,25 +774,25 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa501ad58dd20acddbfebc65b52e60f05ebf97c52fa40d1b35e91f5e2da0ad0e" +checksum = "e8597d36d546e1dab822345ad563243ec3920e199322cb554ce56c8ef1a1e2e7" dependencies = [ "alloy-json-rpc", "alloy-transport", "itertools 0.14.0", - "reqwest", + "reqwest 0.13.2", "serde_json", - "tower 0.5.2", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", "url", ] [[package]] name = "alloy-transport-ipc" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ef85688e5ac2da72afc804e0a1f153a1f309f05a864b1998bbbed7804dbaab" +checksum = "a1bd98c3870b8a44b79091dde5216a81d58ffbc1fd8ed61b776f9fee0f3bdf20" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -811,18 +810,20 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f00445db69d63298e2b00a0ea1d859f00e6424a3144ffc5eba9c31da995e16" +checksum = "ec3ab7a72b180992881acc112628b7668337a19ce15293ee974600ea7b693691" dependencies = [ "alloy-pubsub", "alloy-transport", "futures 0.3.31", "http 1.4.0", + "rustls", "serde_json", "tokio", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite", "tracing", + "url", "ws_stream_wasm", ] @@ -848,11 +849,11 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.7.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa0c53e8c1e1ef4d01066b01c737fb62fc9397ab52c6e7bb5669f97d281b9bc" +checksum = "d69722eddcdf1ce096c3ab66cf8116999363f734eb36fe94a148f4f71c85da84" dependencies = [ - "darling 0.21.3", + "darling 0.23.0", "proc-macro2", "quote", "syn 2.0.117", @@ -938,7 +939,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.1", + "windows-sys 0.60.2", ] [[package]] @@ -949,18 +950,18 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arbitrary" -version = "1.4.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" dependencies = [ "derive_arbitrary", ] [[package]] name = "arc-swap" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5" +checksum = "a07d1f37ff60921c83bdfc7407723bdefe89b44b98a9b772f225c8f9d67141a6" dependencies = [ "rustversion", ] @@ -1350,7 +1351,7 @@ dependencies = [ "arrow-schema", "chrono", "half", - "indexmap 2.11.4", + "indexmap 2.13.0", "itoa", "lexical-core", "memchr", @@ -1442,14 +1443,12 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.27" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" +checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" dependencies = [ - "brotli", - "flate2", - "futures-core", - "memchr", + "compression-codecs", + "compression-core", "pin-project-lite", "tokio", ] @@ -1474,7 +1473,7 @@ dependencies = [ "futures-util", "handlebars", "http 1.4.0", - "indexmap 2.11.4", + "indexmap 2.13.0", "mime", "multer", "num-traits", @@ -1541,7 +1540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e3ef112905abea9dea592fc868a6873b10ebd3f983e83308f995d6284e9ba41" dependencies = [ "bytes", - "indexmap 2.11.4", + "indexmap 2.13.0", "serde", "serde_json", ] @@ -1678,6 +1677,28 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "aws-lc-rs" +version = "1.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "axum" version = "0.8.8" @@ -1707,8 +1728,8 @@ dependencies = [ "sha1 0.10.6", "sync_wrapper", "tokio", - "tokio-tungstenite 0.28.0", - "tower 0.5.2", + "tokio-tungstenite", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", @@ -1840,9 +1861,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "bitvec" @@ -1873,16 +1894,15 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.4" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", "arrayvec 0.7.4", "cc", "cfg-if 1.0.0", - "constant_time_eq 0.4.2", - "cpufeatures 0.3.0", + "constant_time_eq 0.3.1", ] [[package]] @@ -1917,19 +1937,20 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" dependencies = [ "borsh-derive", + "bytes", "cfg_aliases", ] [[package]] name = "borsh-derive" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" dependencies = [ "once_cell", "proc-macro-crate", @@ -2032,9 +2053,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.43" +version = "1.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" dependencies = [ "find-msvc-tools", "jobserver", @@ -2042,6 +2063,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "0.1.10" @@ -2147,6 +2174,15 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + [[package]] name = "cobs" version = "0.2.3" @@ -2173,6 +2209,24 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "compression-codecs" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +dependencies = [ + "brotli", + "compression-core", + "flate2", + "memchr", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -2259,9 +2313,9 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "constant_time_eq" -version = "0.4.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "convert_case" @@ -2391,7 +2445,7 @@ dependencies = [ "log", "pulley-interpreter", "regalloc2", - "rustc-hash 2.1.1", + "rustc-hash", "serde", "smallvec", "target-lexicon", @@ -2558,7 +2612,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "crossterm_winapi", "derive_more", "document-features", @@ -2701,7 +2755,6 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "serde", "strsim", "syn 2.0.117", ] @@ -2715,6 +2768,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", + "serde", "strsim", "syn 2.0.117", ] @@ -2857,12 +2911,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", - "serde_core", + "serde", ] [[package]] @@ -2948,7 +3002,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8496eeb328dce26ee9d9b73275d396d9bddb433fa30106cf6056dd8c3c2764c" dependencies = [ "bigdecimal 0.3.1", - "bitflags 2.9.0", + "bitflags 2.11.0", "byteorder", "chrono", "diesel_derives", @@ -3141,9 +3195,9 @@ dependencies = [ [[package]] name = "doctest-file" -version = "1.0.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" +checksum = "c2db04e74f0a9a93103b50e90b96024c9b2bdca8bce6a632ec71b88736d3d359" [[package]] name = "document-features" @@ -3291,9 +3345,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" dependencies = [ "log", "regex", @@ -3439,9 +3493,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "firestorm" @@ -3479,7 +3533,7 @@ version = "25.12.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "rustc_version 0.4.0", ] @@ -3536,6 +3590,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "fsevent-sys" version = "4.1.0" @@ -3678,9 +3738,9 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25234f20a3ec0a962a61770cfe39ecf03cb529a6e474ad8cff025ed497eda557" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "debugid", - "rustc-hash 2.1.1", + "rustc-hash", "serde", "serde_derive", "serde_json", @@ -3745,7 +3805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" dependencies = [ "fallible-iterator 0.3.0", - "indexmap 2.11.4", + "indexmap 2.13.0", "stable_deref_trait", ] @@ -3819,7 +3879,7 @@ dependencies = [ "pgtemp", "pq-sys", "regex", - "reqwest", + "reqwest 0.12.23", "semver 1.0.27", "serde", "serde_json", @@ -3830,7 +3890,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-util", - "tower 0.5.2", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "url", "walkdir", "wasmparser 0.118.2", @@ -3894,7 +3954,7 @@ dependencies = [ "rand 0.9.2", "redis", "regex", - "reqwest", + "reqwest 0.12.23", "semver 1.0.27", "serde", "serde_derive", @@ -3912,13 +3972,14 @@ dependencies = [ "stable-hash 0.3.4", "stable-hash 0.4.4", "strum_macros 0.28.0", + "tempfile", "thiserror 2.0.18", "tiny-keccak 1.5.0", "tokio", "tokio-retry", "tokio-stream", "tokio-util", - "toml 1.1.0+spec-1.1.0", + "toml 1.1.1+spec-1.1.0", "tonic", "tonic-prost", "tonic-prost-build", @@ -3961,7 +4022,7 @@ dependencies = [ "tokio", "tokio-stream", "tonic-prost-build", - "tower 0.5.2", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4007,7 +4068,7 @@ dependencies = [ "tokio", "tokio-retry", "tokio-util", - "tower 0.5.3", + "tower 0.5.2 (git+https://github.com/tower-rs/tower.git)", "tower-test", "wiremock", ] @@ -4120,7 +4181,7 @@ name = "graph-server-index-node" version = "0.42.1" dependencies = [ "async-trait", - "blake3 1.8.4", + "blake3 1.8.2", "git-testament", "graph", "graph-chain-ethereum", @@ -4156,7 +4217,7 @@ dependencies = [ "anyhow", "arrow", "async-trait", - "blake3 1.8.4", + "blake3 1.8.2", "chrono", "clap", "deadpool 0.13.0", @@ -4258,7 +4319,7 @@ dependencies = [ "graphman", "graphman-store", "lazy_static", - "reqwest", + "reqwest 0.12.23", "serde", "serde_json", "slog", @@ -4281,9 +4342,9 @@ dependencies = [ [[package]] name = "graphql-tools" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ae57fa544e67a661c97805be3e36b97e7d15d65d67bb34beac24d940f987e" +checksum = "90d90a4b9177dee43c94150e40e134e989ec725604f305f07d55db0d9fd2fe29" dependencies = [ "combine", "itoa", @@ -4293,6 +4354,7 @@ dependencies = [ "serde_json", "serde_with", "thiserror 2.0.18", + "xxhash-rust", ] [[package]] @@ -4318,7 +4380,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.4.0", - "indexmap 2.11.4", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -4525,9 +4587,9 @@ checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "hybrid-array" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a79f2aff40c18ab8615ddc5caa9eb5b96314aef18fe5823090f204ad988e813" +checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" dependencies = [ "typenum", ] @@ -4571,7 +4633,6 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.26.11", ] [[package]] @@ -4605,13 +4666,14 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.20" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", + "futures-core", "futures-util", "http 1.4.0", "http-body", @@ -4620,7 +4682,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2", "system-configuration", "tokio", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4848,9 +4910,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "arbitrary", "equivalent", @@ -4887,7 +4949,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "inotify-sys", "libc", ] @@ -4907,7 +4969,7 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6654738b8024300cf062d04a1c13c10c8e2cea598ec1c47dc9b6641159429756" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "crossterm", "dyn-clone", "fuzzy-matcher", @@ -4923,9 +4985,9 @@ checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" [[package]] name = "interprocess" -version = "2.2.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d" +checksum = "6be5e5c847dbdb44564bd85294740d031f4f8aeb3464e5375ef7141f7538db69" dependencies = [ "doctest-file", "futures-core", @@ -5017,9 +5079,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "ittapi" @@ -5065,6 +5127,50 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if 1.0.0", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.61", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "jobserver" version = "0.1.31" @@ -5127,18 +5233,18 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures 0.2.12", ] [[package]] name = "keccak-asm" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +checksum = "fa468878266ad91431012b3e5ef1bf9b170eab22883503a318d46857afa4579a" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -5280,7 +5386,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "libc", ] @@ -5417,7 +5523,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36c791ecdf977c99f45f23280405d7723727470f6689a5e6dbf513ac547ae10d" dependencies = [ "serde", - "toml 0.9.12+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", ] [[package]] @@ -5542,7 +5648,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "fsevent-sys", "inotify", "kqueue", @@ -5593,9 +5699,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" @@ -5643,9 +5749,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ "num_enum_derive", "rustversion", @@ -5653,9 +5759,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro2", "quote", @@ -5685,7 +5791,7 @@ checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "crc32fast", "hashbrown 0.15.2", - "indexmap 2.11.4", + "indexmap 2.13.0", "memchr", ] @@ -5712,7 +5818,7 @@ dependencies = [ "percent-encoding", "quick-xml", "rand 0.10.0", - "reqwest", + "reqwest 0.12.23", "ring", "rustls-pki-types", "serde", @@ -5756,7 +5862,7 @@ version = "0.10.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "cfg-if 1.0.0", "foreign-types", "libc", @@ -5984,7 +6090,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", "hashbrown 0.15.2", - "indexmap 2.11.4", + "indexmap 2.13.0", "serde", ] @@ -6244,7 +6350,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93980406f12d9f8140ed5abe7155acb10bb1e69ea55c88960b9c2f117445ef96" dependencies = [ "equivalent", - "indexmap 2.11.4", + "indexmap 2.13.0", "serde", ] @@ -6312,7 +6418,7 @@ dependencies = [ "memchr", "parking_lot", "protobuf", - "reqwest", + "reqwest 0.12.23", "thiserror 2.0.18", ] @@ -6324,7 +6430,7 @@ checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.0", + "bitflags 2.11.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -6348,9 +6454,9 @@ dependencies = [ [[package]] name = "proptest-derive" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" +checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" dependencies = [ "proc-macro2", "quote", @@ -6428,7 +6534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973" dependencies = [ "anyhow", - "indexmap 2.11.4", + "indexmap 2.13.0", "log", "protobuf", "protobuf-support", @@ -6457,11 +6563,11 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.13.0" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" +checksum = "7c3a14896dfa883796f1cb410461aef38810ea05f2b2c33c5aded3649095fdad" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "memchr", "unicase", ] @@ -6516,19 +6622,22 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.2" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", + "cfg_aliases", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 1.1.0", + "rustc-hash", "rustls", - "thiserror 1.0.61", + "socket2", + "thiserror 2.0.18", "tokio", "tracing", + "web-time", ] [[package]] @@ -6537,12 +6646,13 @@ version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ + "aws-lc-rs", "bytes", "getrandom 0.3.1", "lru-slab", "rand 0.9.2", "ring", - "rustc-hash 2.1.1", + "rustc-hash", "rustls", "rustls-pki-types", "slab", @@ -6554,22 +6664,23 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.2" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ + "cfg_aliases", "libc", "once_cell", - "socket2 0.5.7", + "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -6687,9 +6798,9 @@ dependencies = [ [[package]] name = "rapidhash" -version = "4.2.1" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8b5b858a440a0bc02625b62dd95131b9201aa9f69f411195dd4a7cfb1de3d7" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" dependencies = [ "rustversion", ] @@ -6761,7 +6872,7 @@ dependencies = [ "pin-project-lite", "ryu", "sha1_smol", - "socket2 0.6.0", + "socket2", "tokio", "tokio-util", "url", @@ -6783,7 +6894,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", ] [[package]] @@ -6807,15 +6918,15 @@ dependencies = [ "bumpalo", "hashbrown 0.15.2", "log", - "rustc-hash 2.1.1", + "rustc-hash", "smallvec", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -6880,7 +6991,7 @@ dependencies = [ "tokio-native-tls", "tokio-rustls", "tokio-util", - "tower 0.5.2", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tower-http", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "url", @@ -6888,7 +6999,43 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.5", +] + +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "http 1.4.0", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tower-http", + "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] @@ -6968,15 +7115,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc-hex" @@ -7008,7 +7149,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys 0.4.14", @@ -7021,7 +7162,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys 0.9.4", @@ -7034,6 +7175,7 @@ version = "0.23.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" dependencies = [ + "aws-lc-rs", "log", "once_cell", "ring", @@ -7065,7 +7207,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.2.0", + "security-framework 3.7.0", ] [[package]] @@ -7087,12 +7229,40 @@ dependencies = [ "web-time", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.0", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs 0.8.1", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework 3.7.0", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.60.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -7118,9 +7288,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -7206,7 +7376,7 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -7215,11 +7385,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.2.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "core-foundation 0.10.0", "core-foundation-sys", "libc", @@ -7228,9 +7398,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -7278,9 +7448,9 @@ checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" [[package]] name = "serde" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -7288,18 +7458,18 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -7357,9 +7527,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -7386,7 +7556,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.4", + "indexmap 2.13.0", "serde", "serde_derive", "serde_json", @@ -7412,7 +7582,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.13.0", "itoa", "ryu", "serde", @@ -7491,9 +7661,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +checksum = "59cbb88c189d6352cc8ae96a39d19c7ecad8f7330b29461187f2587fdc2988d5" dependencies = [ "cc", "cfg-if 1.0.0", @@ -7556,9 +7726,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "simdutf8" @@ -7677,16 +7847,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.0" @@ -7905,9 +8065,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f92d01b5de07eaf324f7fca61cc6bd3d82bbc1de5b6c963e6fe79e86f36580d" +checksum = "53f425ae0b12e2f5ae65542e00898d500d4d318b4baf09f40fd0d410454e9947" dependencies = [ "paste", "proc-macro2", @@ -7937,11 +8097,11 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.7.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -7976,15 +8136,14 @@ checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if 1.0.0", "fastrand", - "once_cell", "rustix 0.38.34", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8110,30 +8269,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde_core", + "serde", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -8194,16 +8353,16 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.0", + "socket2", "tokio-macros", "windows-sys 0.61.1", ] [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", @@ -8252,7 +8411,7 @@ dependencies = [ "postgres-protocol", "postgres-types", "rand 0.9.2", - "socket2 0.6.0", + "socket2", "tokio", "tokio-util", "whoami", @@ -8306,9 +8465,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -8316,22 +8475,10 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite 0.26.2", + "tungstenite", "webpki-roots 0.26.11", ] -[[package]] -name = "tokio-tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.28.0", -] - [[package]] name = "tokio-util" version = "0.7.18" @@ -8360,12 +8507,12 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.12+spec-1.1.0" +version = "0.9.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ "serde_core", - "serde_spanned 1.1.0", + "serde_spanned 1.1.1", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "winnow 0.7.13", @@ -8373,17 +8520,17 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc" +checksum = "994b95d9e7bae62b34bab0e2a4510b801fa466066a6a8b2b57361fa1eba068ee" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.13.0", "serde_core", - "serde_spanned 1.1.0", - "toml_datetime 1.1.0+spec-1.1.0", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.0", + "winnow 1.0.1", ] [[package]] @@ -8406,9 +8553,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] @@ -8419,7 +8566,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.13.0", "toml_datetime 0.6.6", "winnow 0.5.40", ] @@ -8430,7 +8577,7 @@ version = "0.22.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.13.0", "serde", "serde_spanned 0.6.6", "toml_datetime 0.6.6", @@ -8439,18 +8586,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +checksum = "39ca317ebc49f06bd748bfba29533eac9485569dc9bf80b849024b025e814fb9" dependencies = [ - "winnow 1.0.0", + "winnow 1.0.1", ] [[package]] name = "toml_writer" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tonic" @@ -8473,12 +8620,12 @@ dependencies = [ "percent-encoding", "pin-project", "rustls-native-certs 0.8.1", - "socket2 0.6.0", + "socket2", "sync_wrapper", "tokio", "tokio-rustls", "tokio-stream", - "tower 0.5.2", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", @@ -8486,9 +8633,9 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27aac809edf60b741e2d7db6367214d078856b8a5bff0087e94ff330fb97b6fc" +checksum = "1882ac3bf5ef12877d7ed57aad87e75154c11931c2ba7e6cde5e22d63522c734" dependencies = [ "prettyplease", "proc-macro2", @@ -8532,7 +8679,7 @@ dependencies = [ "futures-core", "futures-util", "hdrhistogram", - "indexmap 2.11.4", + "indexmap 2.13.0", "pin-project-lite", "slab", "sync_wrapper", @@ -8545,13 +8692,13 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.3" -source = "git+https://github.com/tower-rs/tower.git#251296dc54a044383dffd16d2179b443e2615672" +version = "0.5.2" +source = "git+https://github.com/tower-rs/tower.git#1992ebd196467deffe193d5a073db655492ce168" dependencies = [ "futures-core", "futures-util", "hdrhistogram", - "indexmap 2.11.4", + "indexmap 2.13.0", "pin-project-lite", "slab", "sync_wrapper", @@ -8568,14 +8715,14 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "bytes", "futures-util", "http 1.4.0", "http-body", "iri-string", "pin-project-lite", - "tower 0.5.2", + "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -8589,7 +8736,7 @@ checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-layer" version = "0.3.3" -source = "git+https://github.com/tower-rs/tower.git#251296dc54a044383dffd16d2179b443e2615672" +source = "git+https://github.com/tower-rs/tower.git#1992ebd196467deffe193d5a073db655492ce168" [[package]] name = "tower-service" @@ -8600,12 +8747,12 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tower-service" version = "0.3.3" -source = "git+https://github.com/tower-rs/tower.git#251296dc54a044383dffd16d2179b443e2615672" +source = "git+https://github.com/tower-rs/tower.git#1992ebd196467deffe193d5a073db655492ce168" [[package]] name = "tower-test" version = "0.4.1" -source = "git+https://github.com/tower-rs/tower.git#251296dc54a044383dffd16d2179b443e2615672" +source = "git+https://github.com/tower-rs/tower.git#1992ebd196467deffe193d5a073db655492ce168" dependencies = [ "pin-project-lite", "tokio", @@ -8652,25 +8799,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "tungstenite" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" -dependencies = [ - "bytes", - "data-encoding", - "http 1.4.0", - "httparse", - "log", - "rand 0.9.2", - "rustls", - "rustls-pki-types", - "sha1 0.10.6", - "thiserror 2.0.18", - "utf-8", -] - [[package]] name = "tungstenite" version = "0.28.0" @@ -8683,6 +8811,8 @@ dependencies = [ "httparse", "log", "rand 0.9.2", + "rustls", + "rustls-pki-types", "sha1 0.10.6", "thiserror 2.0.18", "utf-8", @@ -9054,7 +9184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.11.4", + "indexmap 2.13.0", "wasm-encoder 0.244.0", "wasmparser 0.244.0", ] @@ -9078,7 +9208,7 @@ version = "0.118.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77f1154f1ab868e2a01d9834a805faca7bf8b50d041b4ca714d005d0dab1c50c" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.13.0", "semver 1.0.27", ] @@ -9088,9 +9218,9 @@ version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "hashbrown 0.15.2", - "indexmap 2.11.4", + "indexmap 2.13.0", "semver 1.0.27", "serde", ] @@ -9101,9 +9231,9 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", "hashbrown 0.15.2", - "indexmap 2.11.4", + "indexmap 2.13.0", "semver 1.0.27", ] @@ -9127,7 +9257,7 @@ dependencies = [ "addr2line", "anyhow", "async-trait", - "bitflags 2.9.0", + "bitflags 2.11.0", "bumpalo", "cc", "cfg-if 1.0.0", @@ -9135,7 +9265,7 @@ dependencies = [ "fxprof-processed-profile", "gimli", "hashbrown 0.15.2", - "indexmap 2.11.4", + "indexmap 2.13.0", "ittapi", "libc", "log", @@ -9183,7 +9313,7 @@ dependencies = [ "cranelift-bitset", "cranelift-entity", "gimli", - "indexmap 2.11.4", + "indexmap 2.13.0", "log", "object", "postcard", @@ -9371,9 +9501,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f758625553fe33fdce0713f63bb7784c4f5fecb7f7cd4813414519ec24b6a4c" dependencies = [ "anyhow", - "bitflags 2.9.0", + "bitflags 2.11.0", "heck 0.5.0", - "indexmap 2.11.4", + "indexmap 2.13.0", "wit-parser 0.239.0", ] @@ -9433,20 +9563,29 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.5", + "webpki-roots 1.0.6", ] [[package]] name = "webpki-roots" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" dependencies = [ "rustls-pki-types", ] @@ -9581,6 +9720,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -9626,6 +9774,21 @@ dependencies = [ "windows-link 0.2.0", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -9674,6 +9837,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -9692,6 +9861,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -9710,6 +9885,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -9740,6 +9921,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -9758,6 +9945,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -9776,6 +9969,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -9794,6 +9993,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -9841,9 +10046,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" [[package]] name = "wiremock" @@ -9894,7 +10099,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.11.0", ] [[package]] @@ -9905,7 +10110,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck 0.5.0", - "indexmap 2.11.4", + "indexmap 2.13.0", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -9935,8 +10140,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.9.0", - "indexmap 2.11.4", + "bitflags 2.11.0", + "indexmap 2.13.0", "log", "serde", "serde_derive", @@ -9955,7 +10160,7 @@ checksum = "55c92c939d667b7bf0c6bf2d1f67196529758f99a2a45a3355cc56964fd5315d" dependencies = [ "anyhow", "id-arena", - "indexmap 2.11.4", + "indexmap 2.13.0", "log", "semver 1.0.27", "serde", @@ -9973,7 +10178,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.11.4", + "indexmap 2.13.0", "log", "semver 1.0.27", "serde", @@ -10025,9 +10230,9 @@ dependencies = [ [[package]] name = "xxhash-rust" -version = "0.8.11" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63658493314859b4dfdf3fb8c1defd61587839def09582db50b8a4e93afca6bb" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" [[package]] name = "yansi" @@ -10144,9 +10349,9 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c745c48e1007337ed136dc99df34128b9faa6ed542d80a1c673cf55a6d7236c8" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" [[package]] name = "zstd" diff --git a/graph/Cargo.toml b/graph/Cargo.toml index fcbfe1ee5a2..dfbe15ad286 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -102,6 +102,7 @@ clap.workspace = true maplit = "1.0.2" hex-literal = "1.1" wiremock = "0.6.5" +tempfile = "3.8" [build-dependencies] tonic-prost-build = { workspace = true } diff --git a/graph/src/components/log_store/elasticsearch.rs b/graph/src/components/log_store/elasticsearch.rs new file mode 100644 index 00000000000..1b3a30e1019 --- /dev/null +++ b/graph/src/components/log_store/elasticsearch.rs @@ -0,0 +1,217 @@ +use async_trait::async_trait; +use reqwest::Client; +use serde::Deserialize; +use serde_json::json; +use std::collections::HashMap; +use std::time::Duration; + +use crate::log::elastic::ElasticLoggingConfig; +use crate::prelude::DeploymentHash; + +use super::{LogEntry, LogMeta, LogQuery, LogStore, LogStoreError}; + +pub struct ElasticsearchLogStore { + endpoint: String, + username: Option, + password: Option, + client: Client, + index: String, + timeout: Duration, +} + +impl ElasticsearchLogStore { + pub fn new(config: ElasticLoggingConfig, index: String, timeout: Duration) -> Self { + Self { + endpoint: config.endpoint, + username: config.username, + password: config.password, + client: config.client, + index, + timeout, + } + } + + fn build_query(&self, query: &LogQuery) -> serde_json::Value { + let mut must_clauses = Vec::new(); + + // Filter by subgraph ID + must_clauses.push(json!({ + "term": { + "subgraphId": query.subgraph_id.to_string() + } + })); + + // Filter by log level + if let Some(level) = &query.level { + must_clauses.push(json!({ + "term": { + "level": level.as_str() + } + })); + } + + // Filter by time range + if query.from.is_some() || query.to.is_some() { + let mut range = serde_json::Map::new(); + if let Some(from) = &query.from { + range.insert("gte".to_string(), json!(from)); + } + if let Some(to) = &query.to { + range.insert("lte".to_string(), json!(to)); + } + must_clauses.push(json!({ + "range": { + "timestamp": range + } + })); + } + + // Filter by text search + if let Some(search) = &query.search { + must_clauses.push(json!({ + "match": { + "text": search + } + })); + } + + json!({ + "query": { + "bool": { + "must": must_clauses + } + }, + "from": query.skip, + "size": query.first, + "sort": [ + { "timestamp": { "order": "desc" } } + ] + }) + } + + async fn execute_search( + &self, + query_body: serde_json::Value, + ) -> Result, LogStoreError> { + let url = format!("{}/{}/_search", self.endpoint, self.index); + + let mut request = self + .client + .post(&url) + .json(&query_body) + .timeout(self.timeout); + + // Add basic auth if credentials provided + if let (Some(username), Some(password)) = (&self.username, &self.password) { + request = request.basic_auth(username, Some(password)); + } + + let response = request.send().await.map_err(|e| { + LogStoreError::QueryFailed( + anyhow::Error::from(e).context("Elasticsearch request failed"), + ) + })?; + + if !response.status().is_success() { + let status = response.status(); + // Include response body in error context for debugging + // The body is part of the error chain but not the main error message to avoid + // leaking sensitive Elasticsearch internals in logs + let body_text = response + .text() + .await + .unwrap_or_else(|_| "".to_string()); + return Err(LogStoreError::QueryFailed( + anyhow::anyhow!("Elasticsearch query failed with status {}", status) + .context(format!("Response body: {}", body_text)), + )); + } + + let response_body: ElasticsearchResponse = response.json().await.map_err(|e| { + LogStoreError::QueryFailed( + anyhow::Error::from(e).context( + "failed to parse Elasticsearch search response: response format may have changed or be invalid", + ), + ) + })?; + + let entries = response_body + .hits + .hits + .into_iter() + .filter_map(|hit| self.parse_log_entry(hit.source)) + .collect(); + + Ok(entries) + } + + fn parse_log_entry(&self, source: ElasticsearchLogDocument) -> Option { + let level = source.level.parse().ok()?; + let subgraph_id = DeploymentHash::new(&source.subgraph_id).ok()?; + + // Convert arguments HashMap to Vec<(String, String)> + let arguments: Vec<(String, String)> = source.arguments.into_iter().collect(); + + Some(LogEntry { + id: source.id, + subgraph_id, + timestamp: source.timestamp, + level, + text: source.text, + arguments, + meta: LogMeta { + module: source.meta.module, + line: source.meta.line, + column: source.meta.column, + }, + }) + } +} + +#[async_trait] +impl LogStore for ElasticsearchLogStore { + async fn query_logs(&self, query: LogQuery) -> Result, LogStoreError> { + let query_body = self.build_query(&query); + self.execute_search(query_body).await + } + + fn is_available(&self) -> bool { + true + } +} + +// Elasticsearch response types +#[derive(Debug, Deserialize)] +struct ElasticsearchResponse { + hits: ElasticsearchHits, +} + +#[derive(Debug, Deserialize)] +struct ElasticsearchHits { + hits: Vec, +} + +#[derive(Debug, Deserialize)] +struct ElasticsearchHit { + #[serde(rename = "_source")] + source: ElasticsearchLogDocument, +} + +#[derive(Debug, Deserialize)] +struct ElasticsearchLogDocument { + id: String, + #[serde(rename = "subgraphId")] + subgraph_id: String, + timestamp: String, + level: String, + text: String, + arguments: HashMap, + meta: ElasticsearchLogMeta, +} + +#[derive(Debug, Deserialize)] +struct ElasticsearchLogMeta { + module: String, + line: i64, + column: i64, +} diff --git a/graph/src/components/log_store/file.rs b/graph/src/components/log_store/file.rs new file mode 100644 index 00000000000..af40ce41329 --- /dev/null +++ b/graph/src/components/log_store/file.rs @@ -0,0 +1,363 @@ +use async_trait::async_trait; +use serde::{Deserialize, Serialize}; +use std::cmp::Reverse; +use std::collections::BinaryHeap; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::path::PathBuf; + +use crate::prelude::DeploymentHash; + +use super::{LogEntry, LogMeta, LogQuery, LogStore, LogStoreError}; + +pub struct FileLogStore { + directory: PathBuf, + // TODO: Implement log rotation when file exceeds max_file_size + #[allow(dead_code)] + max_file_size: u64, + // TODO: Implement automatic cleanup of logs older than retention_days + #[allow(dead_code)] + retention_days: u32, +} + +impl FileLogStore { + pub fn new( + directory: PathBuf, + max_file_size: u64, + retention_days: u32, + ) -> Result { + // Create directory if it doesn't exist + std::fs::create_dir_all(&directory) + .map_err(|e| LogStoreError::InitializationFailed(e.into()))?; + + Ok(Self { + directory, + max_file_size, + retention_days, + }) + } + + /// Get log file path for a subgraph + fn log_file_path(&self, subgraph_id: &DeploymentHash) -> PathBuf { + self.directory.join(format!("{}.jsonl", subgraph_id)) + } + + /// Parse a JSON line into a LogEntry + fn parse_line(&self, line: &str) -> Option { + let doc: FileLogDocument = serde_json::from_str(line).ok()?; + + let level = doc.level.parse().ok()?; + let subgraph_id = DeploymentHash::new(&doc.subgraph_id).ok()?; + + Some(LogEntry { + id: doc.id, + subgraph_id, + timestamp: doc.timestamp, + level, + text: doc.text, + arguments: doc.arguments, + meta: LogMeta { + module: doc.meta.module, + line: doc.meta.line, + column: doc.meta.column, + }, + }) + } + + /// Check if an entry matches the query filters + fn matches_filters(&self, entry: &LogEntry, query: &LogQuery) -> bool { + // Level filter + if let Some(level) = query.level { + if entry.level != level { + return false; + } + } + + // Time range filters + if let Some(ref from) = query.from { + if entry.timestamp < *from { + return false; + } + } + + if let Some(ref to) = query.to { + if entry.timestamp > *to { + return false; + } + } + + // Text search (case-insensitive) + if let Some(ref search) = query.search { + if !entry.text.to_lowercase().contains(&search.to_lowercase()) { + return false; + } + } + + true + } +} + +/// Helper struct to enable timestamp-based comparisons for BinaryHeap +/// Implements Ord based on timestamp field for maintaining a min-heap of recent entries +struct TimestampedEntry { + entry: LogEntry, +} + +impl PartialEq for TimestampedEntry { + fn eq(&self, other: &Self) -> bool { + self.entry.timestamp == other.entry.timestamp + } +} + +impl Eq for TimestampedEntry {} + +impl PartialOrd for TimestampedEntry { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for TimestampedEntry { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.entry.timestamp.cmp(&other.entry.timestamp) + } +} + +#[async_trait] +impl LogStore for FileLogStore { + async fn query_logs(&self, query: LogQuery) -> Result, LogStoreError> { + let file_path = self.log_file_path(&query.subgraph_id); + + if !file_path.exists() { + return Ok(vec![]); + } + + let file = File::open(&file_path).map_err(|e| LogStoreError::QueryFailed(e.into()))?; + let reader = BufReader::new(file); + + // Calculate how many entries we need to keep in memory + // We need skip + first entries to handle pagination + let needed_entries = (query.skip + query.first) as usize; + + // Use a min-heap (via Reverse) to maintain only the top N most recent entries + // This bounds memory usage to O(skip + first) instead of O(total_log_entries) + let mut top_entries: BinaryHeap> = + BinaryHeap::with_capacity(needed_entries + 1); + + // Stream through the file line-by-line, applying filters and maintaining bounded collection + for line in reader.lines() { + // Skip malformed lines + let line = match line { + Ok(l) => l, + Err(_) => continue, + }; + + // Parse the line into a LogEntry + let entry = match self.parse_line(&line) { + Some(e) => e, + None => continue, + }; + + // Apply filters early to avoid keeping filtered-out entries in memory + if !self.matches_filters(&entry, &query) { + continue; + } + + let timestamped = TimestampedEntry { entry }; + + // Maintain only the top N most recent entries by timestamp + // BinaryHeap with Reverse creates a min-heap, so we can efficiently + // keep the N largest (most recent) timestamps + if top_entries.len() < needed_entries { + top_entries.push(Reverse(timestamped)); + } else if let Some(Reverse(oldest)) = top_entries.peek() { + // If this entry is more recent than the oldest in our heap, replace it + if timestamped.entry.timestamp > oldest.entry.timestamp { + top_entries.pop(); + top_entries.push(Reverse(timestamped)); + } + } + } + + // Convert heap to sorted vector (most recent first) + let mut result: Vec = top_entries + .into_iter() + .map(|Reverse(te)| te.entry) + .collect(); + + // Sort by timestamp descending (most recent first) + result.sort_by(|a, b| b.timestamp.cmp(&a.timestamp)); + + // Apply skip and take to get the final page + Ok(result + .into_iter() + .skip(query.skip as usize) + .take(query.first as usize) + .collect()) + } + + fn is_available(&self) -> bool { + self.directory.exists() && self.directory.is_dir() + } +} + +// File log document format (JSON Lines) +#[derive(Debug, Serialize, Deserialize)] +struct FileLogDocument { + id: String, + #[serde(rename = "subgraphId")] + subgraph_id: String, + timestamp: String, + level: String, + text: String, + arguments: Vec<(String, String)>, + meta: FileLogMeta, +} + +#[derive(Debug, Serialize, Deserialize)] +struct FileLogMeta { + module: String, + line: i64, + column: i64, +} + +#[cfg(test)] +mod tests { + use super::super::LogLevel; + use super::*; + use std::io::Write; + use tempfile::TempDir; + + #[test] + fn test_file_log_store_initialization() { + let temp_dir = TempDir::new().unwrap(); + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 1024 * 1024, 30); + assert!(store.is_ok()); + + let store = store.unwrap(); + assert!(store.is_available()); + } + + #[test] + fn test_log_file_path() { + let temp_dir = TempDir::new().unwrap(); + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 1024 * 1024, 30).unwrap(); + + let subgraph_id = DeploymentHash::new("QmTest").unwrap(); + let path = store.log_file_path(&subgraph_id); + + assert_eq!(path, temp_dir.path().join("QmTest.jsonl")); + } + + #[tokio::test] + async fn test_query_nonexistent_file() { + let temp_dir = TempDir::new().unwrap(); + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 1024 * 1024, 30).unwrap(); + + let query = LogQuery { + subgraph_id: DeploymentHash::new("QmNonexistent").unwrap(), + level: None, + from: None, + to: None, + search: None, + first: 100, + skip: 0, + }; + + let result = store.query_logs(query).await; + assert!(result.is_ok()); + assert_eq!(result.unwrap().len(), 0); + } + + #[tokio::test] + async fn test_query_with_sample_data() { + let temp_dir = TempDir::new().unwrap(); + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 1024 * 1024, 30).unwrap(); + + let subgraph_id = DeploymentHash::new("QmTest").unwrap(); + let file_path = store.log_file_path(&subgraph_id); + + // Write some test data + let mut file = File::create(&file_path).unwrap(); + let log_entry = FileLogDocument { + id: "log-1".to_string(), + subgraph_id: "QmTest".to_string(), + timestamp: "2024-01-15T10:30:00Z".to_string(), + level: "error".to_string(), + text: "Test error message".to_string(), + arguments: vec![], + meta: FileLogMeta { + module: "test.ts".to_string(), + line: 42, + column: 10, + }, + }; + writeln!(file, "{}", serde_json::to_string(&log_entry).unwrap()).unwrap(); + + // Query + let query = LogQuery { + subgraph_id, + level: None, + from: None, + to: None, + search: None, + first: 100, + skip: 0, + }; + + let result = store.query_logs(query).await; + assert!(result.is_ok()); + + let entries = result.unwrap(); + assert_eq!(entries.len(), 1); + assert_eq!(entries[0].id, "log-1"); + assert_eq!(entries[0].text, "Test error message"); + assert_eq!(entries[0].level, LogLevel::Error); + } + + #[tokio::test] + async fn test_query_with_level_filter() { + let temp_dir = TempDir::new().unwrap(); + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 1024 * 1024, 30).unwrap(); + + let subgraph_id = DeploymentHash::new("QmTest").unwrap(); + let file_path = store.log_file_path(&subgraph_id); + + // Write test data with different levels + let mut file = File::create(&file_path).unwrap(); + for (id, level) in [("log-1", "error"), ("log-2", "info"), ("log-3", "error")] { + let log_entry = FileLogDocument { + id: id.to_string(), + subgraph_id: "QmTest".to_string(), + timestamp: format!("2024-01-15T10:30:{}Z", id), + level: level.to_string(), + text: format!("Test {} message", level), + arguments: vec![], + meta: FileLogMeta { + module: "test.ts".to_string(), + line: 42, + column: 10, + }, + }; + writeln!(file, "{}", serde_json::to_string(&log_entry).unwrap()).unwrap(); + } + + // Query for errors only + let query = LogQuery { + subgraph_id, + level: Some(LogLevel::Error), + from: None, + to: None, + search: None, + first: 100, + skip: 0, + }; + + let result = store.query_logs(query).await; + assert!(result.is_ok()); + + let entries = result.unwrap(); + assert_eq!(entries.len(), 2); + assert!(entries.iter().all(|e| e.level == LogLevel::Error)); + } +} diff --git a/graph/src/components/log_store/loki.rs b/graph/src/components/log_store/loki.rs new file mode 100644 index 00000000000..e06405feb55 --- /dev/null +++ b/graph/src/components/log_store/loki.rs @@ -0,0 +1,283 @@ +use async_trait::async_trait; +use reqwest::Client; +use serde::Deserialize; +use std::collections::HashMap; +use std::time::Duration; + +use crate::prelude::DeploymentHash; + +use super::{LogEntry, LogMeta, LogQuery, LogStore, LogStoreError}; + +pub struct LokiLogStore { + endpoint: String, + tenant_id: Option, + client: Client, +} + +impl LokiLogStore { + pub fn new(endpoint: String, tenant_id: Option) -> Result { + let client = Client::builder() + .timeout(Duration::from_secs(10)) + .build() + .map_err(|e| LogStoreError::InitializationFailed(e.into()))?; + + Ok(Self { + endpoint, + tenant_id, + client, + }) + } + + fn build_logql_query(&self, query: &LogQuery) -> String { + let mut selectors = vec![format!("subgraphId=\"{}\"", query.subgraph_id)]; + + // Add log level selector if specified + if let Some(level) = &query.level { + selectors.push(format!("level=\"{}\"", level.as_str())); + } + + // Base selector + let selector = format!("{{{}}}", selectors.join(",")); + + // Add line filter for text search if specified + let query_str = if let Some(search) = &query.search { + format!("{} |~ \"(?i){}\"", selector, regex::escape(search)) + } else { + selector + }; + + query_str + } + + async fn execute_query( + &self, + query_str: &str, + from: &str, + to: &str, + limit: u32, + ) -> Result, LogStoreError> { + let url = format!("{}/loki/api/v1/query_range", self.endpoint); + + let mut request = self + .client + .get(&url) + .query(&[ + ("query", query_str), + ("start", from), + ("end", to), + ("limit", &limit.to_string()), + ("direction", "backward"), // Most recent first + ]) + .timeout(Duration::from_secs(10)); + + // Add X-Scope-OrgID header for multi-tenancy if configured + if let Some(tenant_id) = &self.tenant_id { + request = request.header("X-Scope-OrgID", tenant_id); + } + + let response = request.send().await.map_err(|e| { + LogStoreError::QueryFailed(anyhow::Error::from(e).context("Loki request failed")) + })?; + + if !response.status().is_success() { + let status = response.status(); + return Err(LogStoreError::QueryFailed(anyhow::anyhow!( + "Loki query failed with status {}", + status + ))); + } + + let response_body: LokiResponse = response.json().await.map_err(|e| { + LogStoreError::QueryFailed( + anyhow::Error::from(e) + .context("failed to parse Loki response: response format may have changed"), + ) + })?; + + if response_body.status != "success" { + return Err(LogStoreError::QueryFailed(anyhow::anyhow!( + "Loki query failed with status: {}", + response_body.status + ))); + } + + // Parse results + let entries = response_body + .data + .result + .into_iter() + .flat_map(|stream| { + let stream_labels = stream.stream; // Take ownership + stream + .values + .into_iter() + .filter_map(move |value| self.parse_log_entry(value, &stream_labels)) + }) + .collect(); + + Ok(entries) + } + + fn parse_log_entry( + &self, + value: LokiValue, + _labels: &HashMap, + ) -> Option { + // value is [timestamp_ns, log_line] + // We expect the log line to be JSON with our log entry structure + let log_data: LokiLogDocument = serde_json::from_str(&value.1).ok()?; + + let level = log_data.level.parse().ok()?; + let subgraph_id = DeploymentHash::new(&log_data.subgraph_id).ok()?; + + Some(LogEntry { + id: log_data.id, + subgraph_id, + timestamp: log_data.timestamp, + level, + text: log_data.text, + arguments: log_data.arguments.into_iter().collect(), + meta: LogMeta { + module: log_data.meta.module, + line: log_data.meta.line, + column: log_data.meta.column, + }, + }) + } +} + +#[async_trait] +impl LogStore for LokiLogStore { + async fn query_logs(&self, query: LogQuery) -> Result, LogStoreError> { + let logql_query = self.build_logql_query(&query); + + // Calculate time range + let from = query.from.as_deref().unwrap_or("now-1h"); + let to = query.to.as_deref().unwrap_or("now"); + + // Execute query with limit + skip to handle pagination + let limit = query.first + query.skip; + + let mut entries = self.execute_query(&logql_query, from, to, limit).await?; + + // Apply skip/first pagination + if query.skip > 0 { + entries = entries.into_iter().skip(query.skip as usize).collect(); + } + entries.truncate(query.first as usize); + + Ok(entries) + } + + fn is_available(&self) -> bool { + true + } +} + +// Loki response types +#[derive(Debug, Deserialize)] +struct LokiResponse { + status: String, + data: LokiData, +} + +#[derive(Debug, Deserialize)] +struct LokiData { + // Part of Loki API response, required for deserialization + #[allow(dead_code)] + #[serde(rename = "resultType")] + result_type: String, + result: Vec, +} + +#[derive(Debug, Deserialize)] +struct LokiStream { + stream: HashMap, // Labels + values: Vec, +} + +#[derive(Debug, Deserialize)] +struct LokiValue( + // Timestamp in nanoseconds since epoch (part of Loki API, not currently used) + #[allow(dead_code)] String, + // Log line (JSON document) + String, +); + +#[derive(Debug, Deserialize)] +struct LokiLogDocument { + id: String, + #[serde(rename = "subgraphId")] + subgraph_id: String, + timestamp: String, + level: String, + text: String, + arguments: HashMap, + meta: LokiLogMeta, +} + +#[derive(Debug, Deserialize)] +struct LokiLogMeta { + module: String, + line: i64, + column: i64, +} + +#[cfg(test)] +mod tests { + use super::super::LogLevel; + use super::*; + + #[test] + fn test_build_logql_query_basic() { + let store = LokiLogStore::new("http://localhost:3100".to_string(), None).unwrap(); + let query = LogQuery { + subgraph_id: DeploymentHash::new("QmTest").unwrap(), + level: None, + from: None, + to: None, + search: None, + first: 100, + skip: 0, + }; + + let logql = store.build_logql_query(&query); + assert_eq!(logql, "{subgraphId=\"QmTest\"}"); + } + + #[test] + fn test_build_logql_query_with_level() { + let store = LokiLogStore::new("http://localhost:3100".to_string(), None).unwrap(); + let query = LogQuery { + subgraph_id: DeploymentHash::new("QmTest").unwrap(), + level: Some(LogLevel::Error), + from: None, + to: None, + search: None, + first: 100, + skip: 0, + }; + + let logql = store.build_logql_query(&query); + assert_eq!(logql, "{subgraphId=\"QmTest\",level=\"error\"}"); + } + + #[test] + fn test_build_logql_query_with_text_filter() { + let store = LokiLogStore::new("http://localhost:3100".to_string(), None).unwrap(); + let query = LogQuery { + subgraph_id: DeploymentHash::new("QmTest").unwrap(), + level: None, + from: None, + to: None, + search: Some("transaction failed".to_string()), + first: 100, + skip: 0, + }; + + let logql = store.build_logql_query(&query); + assert!(logql.contains("{subgraphId=\"QmTest\"}")); + assert!(logql.contains("|~")); + assert!(logql.contains("transaction failed")); + } +} From 7cb45b43373ce98b814ba23d2cce2f1d23dd1dbc Mon Sep 17 00:00:00 2001 From: Ford Date: Thu, 15 Jan 2026 13:56:34 -0800 Subject: [PATCH 03/28] graph: Add log drains for writing to backends Implements slog drains for capturing and writing logs: - FileDrain: Writes logs to JSON Lines files (one file per subgraph) - LokiDrain: Writes logs to Grafana Loki via HTTP push API Both drains: - Capture structured log entries with metadata (module, line, column) - Format logs with timestamp, level, text, and arguments - Use efficient serialization with custom KVSerializers --- graph/src/log/file.rs | 337 ++++++++++++++++++++++++++++++++ graph/src/log/loki.rs | 434 ++++++++++++++++++++++++++++++++++++++++++ graph/src/log/mod.rs | 2 + 3 files changed, 773 insertions(+) create mode 100644 graph/src/log/file.rs create mode 100644 graph/src/log/loki.rs diff --git a/graph/src/log/file.rs b/graph/src/log/file.rs new file mode 100644 index 00000000000..4bdaddd2948 --- /dev/null +++ b/graph/src/log/file.rs @@ -0,0 +1,337 @@ +use std::fmt; +use std::fmt::Write as FmtWrite; +use std::fs::{File, OpenOptions}; +use std::io::{BufWriter, Write}; +use std::path::PathBuf; +use std::sync::{Arc, Mutex}; + +use chrono::prelude::{SecondsFormat, Utc}; +use serde::Serialize; +use slog::*; + +/// Configuration for `FileDrain`. +#[derive(Clone, Debug)] +pub struct FileDrainConfig { + /// Directory where log files will be stored + pub directory: PathBuf, + /// The subgraph ID used for the log filename + pub subgraph_id: String, + /// Maximum file size in bytes + pub max_file_size: u64, + /// Retention period in days + pub retention_days: u32, +} + +/// Log document structure for JSON Lines format +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct FileLogDocument { + id: String, + subgraph_id: String, + timestamp: String, + level: String, + text: String, + arguments: Vec<(String, String)>, + meta: FileLogMeta, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct FileLogMeta { + module: String, + line: i64, + column: i64, +} + +/// Serializer for extracting key-value pairs into a Vec +struct VecKVSerializer { + kvs: Vec<(String, String)>, +} + +impl VecKVSerializer { + fn new() -> Self { + Self { kvs: Vec::new() } + } + + fn finish(self) -> Vec<(String, String)> { + self.kvs + } +} + +impl Serializer for VecKVSerializer { + fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { + self.kvs.push((key.into(), val.to_string())); + Ok(()) + } +} + +/// Serializer for concatenating key-value arguments into a string +struct SimpleKVSerializer { + kvs: Vec<(String, String)>, +} + +impl SimpleKVSerializer { + fn new() -> Self { + Self { kvs: Vec::new() } + } + + fn finish(self) -> (usize, String) { + ( + self.kvs.len(), + self.kvs + .iter() + .map(|(k, v)| format!("{}: {}", k, v)) + .collect::>() + .join(", "), + ) + } +} + +impl Serializer for SimpleKVSerializer { + fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { + self.kvs.push((key.into(), val.to_string())); + Ok(()) + } +} + +/// An slog `Drain` for logging to local files in JSON Lines format. +/// +/// Each subgraph gets its own .jsonl file with log entries. +/// Format: One JSON object per line +/// ```jsonl +/// {"id":"QmXxx-2024-01-15T10:30:00Z","subgraphId":"QmXxx","timestamp":"2024-01-15T10:30:00Z","level":"error","text":"Error message","arguments":[],"meta":{"module":"test.rs","line":42,"column":10}} +/// ``` +pub struct FileDrain { + config: FileDrainConfig, + error_logger: Logger, + writer: Arc>>, +} + +impl FileDrain { + /// Creates a new `FileDrain`. + pub fn new(config: FileDrainConfig, error_logger: Logger) -> std::io::Result { + std::fs::create_dir_all(&config.directory)?; + + let path = config + .directory + .join(format!("{}.jsonl", config.subgraph_id)); + let file = OpenOptions::new().create(true).append(true).open(path)?; + + Ok(FileDrain { + config, + error_logger, + writer: Arc::new(Mutex::new(BufWriter::new(file))), + }) + } +} + +impl Drain for FileDrain { + type Ok = (); + type Err = Never; + + fn log(&self, record: &Record, values: &OwnedKVList) -> std::result::Result<(), Never> { + // Don't write `trace` logs to file + if record.level() == Level::Trace { + return Ok(()); + } + + let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Nanos, true); + let id = format!("{}-{}", self.config.subgraph_id, timestamp); + + let level = match record.level() { + Level::Critical => "critical", + Level::Error => "error", + Level::Warning => "warning", + Level::Info => "info", + Level::Debug => "debug", + Level::Trace => "trace", + }; + + // Serialize logger arguments + let mut serializer = SimpleKVSerializer::new(); + record + .kv() + .serialize(record, &mut serializer) + .expect("failed to serialize logger arguments"); + let (n_logger_kvs, logger_kvs) = serializer.finish(); + + // Serialize log message arguments + let mut serializer = SimpleKVSerializer::new(); + values + .serialize(record, &mut serializer) + .expect("failed to serialize log message arguments"); + let (n_value_kvs, value_kvs) = serializer.finish(); + + // Serialize arguments into vec for storage + let mut serializer = VecKVSerializer::new(); + record + .kv() + .serialize(record, &mut serializer) + .expect("failed to serialize log message arguments into vec"); + let arguments = serializer.finish(); + + // Build text with all key-value pairs + let mut text = format!("{}", record.msg()); + if n_logger_kvs > 0 { + write!(text, ", {}", logger_kvs).unwrap(); + } + if n_value_kvs > 0 { + write!(text, ", {}", value_kvs).unwrap(); + } + + // Build log document + let log_doc = FileLogDocument { + id, + subgraph_id: self.config.subgraph_id.clone(), + timestamp, + level: level.to_string(), + text, + arguments, + meta: FileLogMeta { + module: record.module().into(), + line: record.line() as i64, + column: record.column() as i64, + }, + }; + + // Write JSON line (synchronous, buffered) + let mut writer = self.writer.lock().unwrap(); + if let Err(e) = serde_json::to_writer(&mut *writer, &log_doc) { + error!(self.error_logger, "Failed to serialize log to JSON: {}", e); + return Ok(()); + } + + if let Err(e) = writeln!(&mut *writer) { + error!(self.error_logger, "Failed to write newline: {}", e); + return Ok(()); + } + + // Flush to ensure durability + if let Err(e) = writer.flush() { + error!(self.error_logger, "Failed to flush log file: {}", e); + } + + Ok(()) + } +} + +/// Creates a new asynchronous file logger. +/// +/// Uses `error_logger` to print any file logging errors, +/// so they don't go unnoticed. +pub fn file_logger(config: FileDrainConfig, error_logger: Logger) -> Logger { + let file_drain = match FileDrain::new(config, error_logger.clone()) { + Ok(drain) => drain, + Err(e) => { + error!(error_logger, "Failed to create FileDrain: {}", e); + // Return a logger that discards all logs + return Logger::root(slog::Discard, o!()); + } + }; + + let async_drain = slog_async::Async::new(file_drain.fuse()) + .chan_size(20000) + .overflow_strategy(slog_async::OverflowStrategy::Block) + .build() + .fuse(); + Logger::root(async_drain, o!()) +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::TempDir; + + #[test] + fn test_file_drain_creation() { + let temp_dir = TempDir::new().unwrap(); + let error_logger = Logger::root(slog::Discard, o!()); + + let config = FileDrainConfig { + directory: temp_dir.path().to_path_buf(), + subgraph_id: "QmTest".to_string(), + max_file_size: 1024 * 1024, + retention_days: 30, + }; + + let drain = FileDrain::new(config, error_logger); + assert!(drain.is_ok()); + + // Verify file was created + let file_path = temp_dir.path().join("QmTest.jsonl"); + assert!(file_path.exists()); + } + + #[test] + fn test_log_entry_format() { + let arguments = vec![ + ("key1".to_string(), "value1".to_string()), + ("key2".to_string(), "value2".to_string()), + ]; + + let doc = FileLogDocument { + id: "test-id".to_string(), + subgraph_id: "QmTest".to_string(), + timestamp: "2024-01-15T10:30:00Z".to_string(), + level: "error".to_string(), + text: "Test error message".to_string(), + arguments, + meta: FileLogMeta { + module: "test.rs".to_string(), + line: 42, + column: 10, + }, + }; + + let json = serde_json::to_string(&doc).unwrap(); + assert!(json.contains("\"id\":\"test-id\"")); + assert!(json.contains("\"subgraphId\":\"QmTest\"")); + assert!(json.contains("\"level\":\"error\"")); + assert!(json.contains("\"text\":\"Test error message\"")); + assert!(json.contains("\"arguments\"")); + } + + #[test] + fn test_file_drain_writes_jsonl() { + use std::io::{BufRead, BufReader}; + + let temp_dir = TempDir::new().unwrap(); + let error_logger = Logger::root(slog::Discard, o!()); + + let config = FileDrainConfig { + directory: temp_dir.path().to_path_buf(), + subgraph_id: "QmTest".to_string(), + max_file_size: 1024 * 1024, + retention_days: 30, + }; + + let drain = FileDrain::new(config.clone(), error_logger).unwrap(); + + // Create a test record + let logger = Logger::root(drain, o!()); + info!(logger, "Test message"; "key" => "value"); + + // Give async drain time to write (in real test we'd use proper sync) + std::thread::sleep(std::time::Duration::from_millis(100)); + + // Read the file + let file_path = temp_dir.path().join("QmTest.jsonl"); + let file = File::open(file_path).unwrap(); + let reader = BufReader::new(file); + + let lines: Vec = reader.lines().map_while(|r| r.ok()).collect(); + + // Should have written at least one line + assert!(!lines.is_empty()); + + // Each line should be valid JSON + for line in lines { + let parsed: serde_json::Value = serde_json::from_str(&line).unwrap(); + assert!(parsed.get("id").is_some()); + assert!(parsed.get("subgraphId").is_some()); + assert!(parsed.get("timestamp").is_some()); + assert!(parsed.get("level").is_some()); + assert!(parsed.get("text").is_some()); + } + } +} diff --git a/graph/src/log/loki.rs b/graph/src/log/loki.rs new file mode 100644 index 00000000000..fcc3fe01e63 --- /dev/null +++ b/graph/src/log/loki.rs @@ -0,0 +1,434 @@ +use std::collections::HashMap; +use std::fmt; +use std::fmt::Write as FmtWrite; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +use chrono::prelude::{SecondsFormat, Utc}; +use reqwest::Client; +use serde::Serialize; +use serde_json::json; +use slog::*; + +/// Configuration for `LokiDrain`. +#[derive(Clone, Debug)] +pub struct LokiDrainConfig { + pub endpoint: String, + pub tenant_id: Option, + pub flush_interval: Duration, + pub subgraph_id: String, +} + +/// A log entry to be sent to Loki +#[derive(Clone, Debug)] +struct LokiLogEntry { + timestamp_ns: String, // Nanoseconds since epoch as string + line: String, // JSON-serialized log entry + labels: HashMap, // Stream labels (subgraphId, level, etc.) +} + +/// Log document structure for JSON serialization +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct LokiLogDocument { + id: String, + subgraph_id: String, + timestamp: String, + level: String, + text: String, + arguments: HashMap, + meta: LokiLogMeta, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct LokiLogMeta { + module: String, + line: i64, + column: i64, +} + +/// Serializer for extracting key-value pairs into a HashMap +struct HashMapKVSerializer { + kvs: Vec<(String, String)>, +} + +impl HashMapKVSerializer { + fn new() -> Self { + HashMapKVSerializer { + kvs: Default::default(), + } + } + + fn finish(self) -> HashMap { + let mut map = HashMap::new(); + self.kvs.into_iter().for_each(|(k, v)| { + map.insert(k, v); + }); + map + } +} + +impl Serializer for HashMapKVSerializer { + fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { + self.kvs.push((key.into(), format!("{}", val))); + Ok(()) + } +} + +/// Serializer for concatenating key-value arguments into a string +struct SimpleKVSerializer { + kvs: Vec<(String, String)>, +} + +impl SimpleKVSerializer { + fn new() -> Self { + SimpleKVSerializer { + kvs: Default::default(), + } + } + + fn finish(self) -> (usize, String) { + ( + self.kvs.len(), + self.kvs + .iter() + .map(|(k, v)| format!("{}: {}", k, v)) + .collect::>() + .join(", "), + ) + } +} + +impl Serializer for SimpleKVSerializer { + fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { + self.kvs.push((key.into(), format!("{}", val))); + Ok(()) + } +} + +/// A slog `Drain` for logging to Loki. +/// +/// Loki expects logs in the following format: +/// ```json +/// { +/// "streams": [ +/// { +/// "stream": {"subgraphId": "QmXxx", "level": "error"}, +/// "values": [ +/// ["", ""], +/// ["", ""] +/// ] +/// } +/// ] +/// } +/// ``` +pub struct LokiDrain { + config: LokiDrainConfig, + client: Client, + error_logger: Logger, + logs: Arc>>, +} + +impl LokiDrain { + /// Creates a new `LokiDrain`. + pub fn new(config: LokiDrainConfig, error_logger: Logger) -> Self { + let client = Client::builder() + .timeout(Duration::from_secs(30)) + .build() + .expect("failed to create HTTP client for LokiDrain"); + + let drain = LokiDrain { + config, + client, + error_logger, + logs: Arc::new(Mutex::new(vec![])), + }; + drain.periodically_flush_logs(); + drain + } + + fn periodically_flush_logs(&self) { + let flush_logger = self.error_logger.clone(); + let logs = self.logs.clone(); + let config = self.config.clone(); + let client = self.client.clone(); + let mut interval = tokio::time::interval(self.config.flush_interval); + + crate::tokio::spawn(async move { + loop { + interval.tick().await; + + let logs_to_send = { + let mut logs = logs.lock().unwrap(); + let logs_to_send = (*logs).clone(); + logs.clear(); + logs_to_send + }; + + // Do nothing if there are no logs to flush + if logs_to_send.is_empty() { + continue; + } + + // Group logs by labels (Loki streams) + let streams = group_by_labels(logs_to_send); + + // Build Loki push request body + let streams_json: Vec<_> = streams + .into_iter() + .map(|(labels, entries)| { + json!({ + "stream": labels, + "values": entries.into_iter() + .map(|e| vec![e.timestamp_ns, e.line]) + .collect::>() + }) + }) + .collect(); + + let body = json!({ + "streams": streams_json + }); + + let url = format!("{}/loki/api/v1/push", config.endpoint); + + let mut request = client + .post(&url) + .json(&body) + .timeout(Duration::from_secs(30)); + + if let Some(ref tenant_id) = config.tenant_id { + request = request.header("X-Scope-OrgID", tenant_id); + } + + match request.send().await { + Ok(resp) if resp.status().is_success() => { + // Success + } + Ok(resp) => { + error!( + flush_logger, + "Loki push failed with status: {}", + resp.status() + ); + } + Err(e) => { + error!(flush_logger, "Failed to send logs to Loki: {}", e); + } + } + } + }); + } +} + +impl Drain for LokiDrain { + type Ok = (); + type Err = (); + + fn log(&self, record: &Record, values: &OwnedKVList) -> std::result::Result<(), ()> { + // Don't send `trace` logs to Loki + if record.level() == Level::Trace { + return Ok(()); + } + + let now = Utc::now(); + let timestamp = now.to_rfc3339_opts(SecondsFormat::Nanos, true); + let timestamp_ns = now.timestamp_nanos_opt().unwrap().to_string(); + let id = format!("{}-{}", self.config.subgraph_id, timestamp); + + let level = match record.level() { + Level::Critical => "critical", + Level::Error => "error", + Level::Warning => "warning", + Level::Info => "info", + Level::Debug => "debug", + Level::Trace => "trace", + }; + + // Serialize logger arguments + let mut serializer = SimpleKVSerializer::new(); + record + .kv() + .serialize(record, &mut serializer) + .expect("failed to serialize logger arguments"); + let (n_logger_kvs, logger_kvs) = serializer.finish(); + + // Serialize log message arguments + let mut serializer = SimpleKVSerializer::new(); + values + .serialize(record, &mut serializer) + .expect("failed to serialize log message arguments"); + let (n_value_kvs, value_kvs) = serializer.finish(); + + // Serialize arguments into hash map + let mut serializer = HashMapKVSerializer::new(); + record + .kv() + .serialize(record, &mut serializer) + .expect("failed to serialize log message arguments into hash map"); + let arguments = serializer.finish(); + + // Build text with all key-value pairs + let mut text = format!("{}", record.msg()); + if n_logger_kvs > 0 { + write!(text, ", {}", logger_kvs).unwrap(); + } + if n_value_kvs > 0 { + write!(text, ", {}", value_kvs).unwrap(); + } + + // Build log document + let log_doc = LokiLogDocument { + id, + subgraph_id: self.config.subgraph_id.clone(), + timestamp: timestamp.clone(), + level: level.to_string(), + text, + arguments, + meta: LokiLogMeta { + module: record.module().into(), + line: record.line() as i64, + column: record.column() as i64, + }, + }; + + // Serialize to JSON line + let line = match serde_json::to_string(&log_doc) { + Ok(l) => l, + Err(e) => { + error!(self.error_logger, "Failed to serialize log to JSON: {}", e); + return Ok(()); + } + }; + + // Build labels for Loki stream + let mut labels = HashMap::new(); + labels.insert("subgraphId".to_string(), self.config.subgraph_id.clone()); + labels.insert("level".to_string(), level.to_string()); + + // Create log entry + let entry = LokiLogEntry { + timestamp_ns, + line, + labels, + }; + + // Push to buffer + let mut logs = self.logs.lock().unwrap(); + logs.push(entry); + + Ok(()) + } +} + +/// Groups log entries by their labels to create Loki streams +/// Returns a HashMap where the key is the labels and the value is a vec of entries +fn group_by_labels( + entries: Vec, +) -> Vec<(HashMap, Vec)> { + let mut streams: HashMap, Vec)> = HashMap::new(); + for entry in entries { + // Create a deterministic string key from the labels + let label_key = serde_json::to_string(&entry.labels).unwrap_or_default(); + + streams + .entry(label_key) + .or_insert_with(|| (entry.labels.clone(), Vec::new())) + .1 + .push(entry); + } + + // Convert to a vec of (labels, entries) tuples + streams.into_values().collect() +} + +/// Creates a new asynchronous Loki logger. +/// +/// Uses `error_logger` to print any Loki logging errors, +/// so they don't go unnoticed. +pub fn loki_logger(config: LokiDrainConfig, error_logger: Logger) -> Logger { + let loki_drain = LokiDrain::new(config, error_logger).fuse(); + let async_drain = slog_async::Async::new(loki_drain) + .chan_size(20000) + .overflow_strategy(slog_async::OverflowStrategy::Block) + .build() + .fuse(); + Logger::root(async_drain, o!()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_group_by_labels() { + let mut labels1 = HashMap::new(); + labels1.insert("subgraphId".to_string(), "QmTest".to_string()); + labels1.insert("level".to_string(), "error".to_string()); + + let mut labels2 = HashMap::new(); + labels2.insert("subgraphId".to_string(), "QmTest".to_string()); + labels2.insert("level".to_string(), "info".to_string()); + + let entries = vec![ + LokiLogEntry { + timestamp_ns: "1000000000".to_string(), + line: "log1".to_string(), + labels: labels1.clone(), + }, + LokiLogEntry { + timestamp_ns: "2000000000".to_string(), + line: "log2".to_string(), + labels: labels1.clone(), + }, + LokiLogEntry { + timestamp_ns: "3000000000".to_string(), + line: "log3".to_string(), + labels: labels2.clone(), + }, + ]; + + let streams = group_by_labels(entries); + + // Should have 2 streams (one for each unique label set) + assert_eq!(streams.len(), 2); + + // Find streams by label and verify counts + for (labels, entries) in streams { + if labels.get("level") == Some(&"error".to_string()) { + assert_eq!(entries.len(), 2, "Error stream should have 2 entries"); + } else if labels.get("level") == Some(&"info".to_string()) { + assert_eq!(entries.len(), 1, "Info stream should have 1 entry"); + } else { + panic!("Unexpected label combination"); + } + } + } + + #[test] + fn test_loki_log_document_serialization() { + let mut arguments = HashMap::new(); + arguments.insert("key1".to_string(), "value1".to_string()); + + let doc = LokiLogDocument { + id: "test-id".to_string(), + subgraph_id: "QmTest".to_string(), + timestamp: "2024-01-15T10:30:00Z".to_string(), + level: "error".to_string(), + text: "Test error".to_string(), + arguments, + meta: LokiLogMeta { + module: "test.rs".to_string(), + line: 42, + column: 10, + }, + }; + + let json = serde_json::to_string(&doc).unwrap(); + assert!(json.contains("\"id\":\"test-id\"")); + assert!(json.contains("\"subgraphId\":\"QmTest\"")); + assert!(json.contains("\"level\":\"error\"")); + assert!(json.contains("\"text\":\"Test error\"")); + } +} diff --git a/graph/src/log/mod.rs b/graph/src/log/mod.rs index 083306216a6..4c58af34bac 100644 --- a/graph/src/log/mod.rs +++ b/graph/src/log/mod.rs @@ -32,6 +32,8 @@ use crate::prelude::ENV_VARS; pub mod codes; pub mod elastic; pub mod factory; +pub mod file; +pub mod loki; pub mod split; pub fn logger(show_debug: bool) -> Logger { From 62be0964c3d647930391abc07226d85747bc4046 Mon Sep 17 00:00:00 2001 From: Ford Date: Thu, 15 Jan 2026 13:56:42 -0800 Subject: [PATCH 04/28] graph,node: Add log store configuration system Adds a configuration layer for selecting and configuring log backends: - LogStoreConfig enum with variants: Disabled, File, Elasticsearch, Loki - LogConfigProvider for loading config from environment variables and CLI args - Unified GRAPH_LOG_STORE_* environment variable naming - CLI arguments with --log-store-backend and backend-specific options - Configuration precedence: CLI args > env vars > defaults - Deprecation warnings for old config variables Supported configuration: - Backend selection (disabled, file, elasticsearch, loki) - File: directory, max size, retention days - Elasticsearch: endpoint, credentials, index, timeout - Loki: endpoint, tenant ID --- graph/src/components/log_store/config.rs | 216 +++++++++++++++++++++ node/src/lib.rs | 1 + node/src/log_config_provider.rs | 232 +++++++++++++++++++++++ node/src/opt.rs | 80 +++++++- 4 files changed, 526 insertions(+), 3 deletions(-) create mode 100644 graph/src/components/log_store/config.rs create mode 100644 node/src/log_config_provider.rs diff --git a/graph/src/components/log_store/config.rs b/graph/src/components/log_store/config.rs new file mode 100644 index 00000000000..84118ca4864 --- /dev/null +++ b/graph/src/components/log_store/config.rs @@ -0,0 +1,216 @@ +use slog::{warn, Logger}; +use std::env; + +/// Read environment variable with fallback to deprecated key +/// +/// This helper function implements backward compatibility for environment variables. +/// It first tries the new key, then falls back to the old (deprecated) key with a warning. +/// +/// # Arguments +/// * `logger` - Logger for emitting deprecation warnings +/// * `new_key` - The new environment variable name +/// * `old_key` - The deprecated environment variable name +/// +/// # Returns +/// The value of the environment variable if found, or None if neither key is set +pub fn read_env_with_fallback(logger: &Logger, new_key: &str, old_key: &str) -> Option { + // Try new key first + if let Ok(value) = env::var(new_key) { + return Some(value); + } + + // Fall back to old key with deprecation warning + if let Ok(value) = env::var(old_key) { + warn!( + logger, + "Using deprecated environment variable '{}', please use '{}' instead", old_key, new_key + ); + return Some(value); + } + + None +} + +/// Read environment variable with default value and fallback +/// +/// Similar to `read_env_with_fallback`, but returns a default value if neither key is set. +/// +/// # Arguments +/// * `logger` - Logger for emitting deprecation warnings +/// * `new_key` - The new environment variable name +/// * `old_key` - The deprecated environment variable name +/// * `default` - Default value to return if neither key is set +/// +/// # Returns +/// The value of the environment variable, or the default if neither key is set +pub fn read_env_with_default( + logger: &Logger, + new_key: &str, + old_key: &str, + default: &str, +) -> String { + read_env_with_fallback(logger, new_key, old_key).unwrap_or_else(|| default.to_string()) +} + +/// Parse u64 from environment variable with fallback +/// +/// Reads an environment variable with fallback support and parses it as a u64. +/// Returns the default value if the variable is not set or cannot be parsed. +/// +/// # Arguments +/// * `logger` - Logger for emitting deprecation warnings +/// * `new_key` - The new environment variable name +/// * `old_key` - The deprecated environment variable name +/// * `default` - Default value to return if parsing fails or neither key is set +/// +/// # Returns +/// The parsed u64 value, or the default if parsing fails or neither key is set +pub fn read_u64_with_fallback(logger: &Logger, new_key: &str, old_key: &str, default: u64) -> u64 { + read_env_with_fallback(logger, new_key, old_key) + .and_then(|s| s.parse().ok()) + .unwrap_or(default) +} + +/// Parse u32 from environment variable with fallback +/// +/// Reads an environment variable with fallback support and parses it as a u32. +/// Returns the default value if the variable is not set or cannot be parsed. +/// +/// # Arguments +/// * `logger` - Logger for emitting deprecation warnings +/// * `new_key` - The new environment variable name +/// * `old_key` - The deprecated environment variable name +/// * `default` - Default value to return if parsing fails or neither key is set +/// +/// # Returns +/// The parsed u32 value, or the default if parsing fails or neither key is set +pub fn read_u32_with_fallback(logger: &Logger, new_key: &str, old_key: &str, default: u32) -> u32 { + read_env_with_fallback(logger, new_key, old_key) + .and_then(|s| s.parse().ok()) + .unwrap_or(default) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_read_new_key_takes_precedence() { + let logger = crate::log::logger(true); + std::env::set_var("NEW_KEY_PRECEDENCE", "new_value"); + std::env::set_var("OLD_KEY_PRECEDENCE", "old_value"); + + let result = read_env_with_fallback(&logger, "NEW_KEY_PRECEDENCE", "OLD_KEY_PRECEDENCE"); + assert_eq!(result, Some("new_value".to_string())); + + std::env::remove_var("NEW_KEY_PRECEDENCE"); + std::env::remove_var("OLD_KEY_PRECEDENCE"); + } + + #[test] + fn test_read_old_key_when_new_not_present() { + let logger = crate::log::logger(true); + std::env::remove_var("NEW_KEY_FALLBACK"); + std::env::set_var("OLD_KEY_FALLBACK", "old_value"); + + let result = read_env_with_fallback(&logger, "NEW_KEY_FALLBACK", "OLD_KEY_FALLBACK"); + assert_eq!(result, Some("old_value".to_string())); + + std::env::remove_var("OLD_KEY_FALLBACK"); + } + + #[test] + fn test_read_returns_none_when_neither_present() { + let logger = crate::log::logger(true); + std::env::remove_var("NEW_KEY_NONE"); + std::env::remove_var("OLD_KEY_NONE"); + + let result = read_env_with_fallback(&logger, "NEW_KEY_NONE", "OLD_KEY_NONE"); + assert_eq!(result, None); + } + + #[test] + fn test_read_with_default() { + let logger = crate::log::logger(true); + std::env::remove_var("NEW_KEY_DEFAULT"); + std::env::remove_var("OLD_KEY_DEFAULT"); + + let result = read_env_with_default( + &logger, + "NEW_KEY_DEFAULT", + "OLD_KEY_DEFAULT", + "default_value", + ); + assert_eq!(result, "default_value"); + + std::env::remove_var("NEW_KEY_DEFAULT"); + std::env::remove_var("OLD_KEY_DEFAULT"); + } + + #[test] + fn test_read_u64_with_fallback() { + let logger = crate::log::logger(true); + std::env::set_var("NEW_KEY_U64", "12345"); + + let result = read_u64_with_fallback(&logger, "NEW_KEY_U64", "OLD_KEY_U64", 999); + assert_eq!(result, 12345); + + std::env::remove_var("NEW_KEY_U64"); + + // Test with old key + std::env::set_var("OLD_KEY_U64", "67890"); + let result = read_u64_with_fallback(&logger, "NEW_KEY_U64", "OLD_KEY_U64", 999); + assert_eq!(result, 67890); + + std::env::remove_var("OLD_KEY_U64"); + + // Test with default + let result = read_u64_with_fallback(&logger, "NEW_KEY_U64", "OLD_KEY_U64", 999); + assert_eq!(result, 999); + } + + #[test] + fn test_read_u32_with_fallback() { + let logger = crate::log::logger(true); + std::env::set_var("NEW_KEY_U32", "123"); + + let result = read_u32_with_fallback(&logger, "NEW_KEY_U32", "OLD_KEY_U32", 999); + assert_eq!(result, 123); + + std::env::remove_var("NEW_KEY_U32"); + + // Test with old key + std::env::set_var("OLD_KEY_U32", "456"); + let result = read_u32_with_fallback(&logger, "NEW_KEY_U32", "OLD_KEY_U32", 999); + assert_eq!(result, 456); + + std::env::remove_var("OLD_KEY_U32"); + + // Test with default + let result = read_u32_with_fallback(&logger, "NEW_KEY_U32", "OLD_KEY_U32", 999); + assert_eq!(result, 999); + } + + #[test] + fn test_invalid_u64_uses_default() { + let logger = crate::log::logger(true); + std::env::set_var("NEW_KEY_INVALID", "not_a_number"); + + let result = read_u64_with_fallback(&logger, "NEW_KEY_INVALID", "OLD_KEY_INVALID", 999); + assert_eq!(result, 999); + + std::env::remove_var("NEW_KEY_INVALID"); + } + + #[test] + fn test_invalid_u32_uses_default() { + let logger = crate::log::logger(true); + std::env::set_var("NEW_KEY_INVALID_U32", "not_a_number"); + + let result = + read_u32_with_fallback(&logger, "NEW_KEY_INVALID_U32", "OLD_KEY_INVALID_U32", 999); + assert_eq!(result, 999); + + std::env::remove_var("NEW_KEY_INVALID_U32"); + } +} diff --git a/node/src/lib.rs b/node/src/lib.rs index a0fe189f1f7..7344fc89a04 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -9,6 +9,7 @@ pub mod chain; pub mod config; mod helpers; pub mod launcher; +pub mod log_config_provider; pub mod manager; pub mod network_setup; pub mod opt; diff --git a/node/src/log_config_provider.rs b/node/src/log_config_provider.rs new file mode 100644 index 00000000000..133be35af14 --- /dev/null +++ b/node/src/log_config_provider.rs @@ -0,0 +1,232 @@ +use graph::components::log_store::{LogStore, LogStoreConfig, LogStoreFactory, NoOpLogStore}; +use graph::prelude::*; +use slog::{info, warn, Logger}; +use std::sync::Arc; + +/// Configuration sources for log store resolution +pub struct LogStoreConfigSources { + /// Log store config from CLI arguments (any backend) + pub cli_config: Option, +} + +/// Provider for resolving log store configuration from multiple sources +/// +/// It handles multi-source configuration with the following priority: +/// 1. GRAPH_LOG_STORE environment variable (supports all backends) +/// 2. CLI configuration (any backend) +/// 3. NoOp/None (disabled) +pub struct LogStoreConfigProvider { + sources: LogStoreConfigSources, +} + +impl LogStoreConfigProvider { + /// Create a new provider with given configuration sources + pub fn new(sources: LogStoreConfigSources) -> Self { + Self { sources } + } + + /// Resolve and create a LogStore for querying logs + /// + /// Priority: GRAPH_LOG_STORE env var → CLI config → NoOp + pub fn resolve_log_store(&self, logger: &Logger) -> Arc { + // Try GRAPH_LOG_STORE environment variable + match LogStoreFactory::from_env() { + Ok(config) => match LogStoreFactory::from_config(config) { + Ok(store) => { + info!( + logger, + "Log store initialized from GRAPH_LOG_STORE environment variable" + ); + return store; + } + Err(e) => { + warn!( + logger, + "Failed to initialize log store from GRAPH_LOG_STORE: {}, falling back to CLI config", + e + ); + // Fall through to CLI fallback + } + }, + Err(_) => { + // No GRAPH_LOG_STORE env var, fall through to CLI config + } + } + + // Try CLI config + if let Some(cli_store) = self.resolve_cli_store(logger) { + return cli_store; + } + + // Default to NoOp + info!( + logger, + "No log store configured, queries will return empty results" + ); + Arc::new(NoOpLogStore) + } + + /// Resolve LogStoreConfig for drain selection (write side) + /// + /// Priority: GRAPH_LOG_STORE env var → CLI config → None + pub fn resolve_log_store_config(&self, _logger: &Logger) -> Option { + // Try GRAPH_LOG_STORE environment variable + // Note: from_env() returns Ok(Disabled) when GRAPH_LOG_STORE is not set, + // so we need to check if it's actually configured + if let Ok(config) = LogStoreFactory::from_env() { + if !matches!(config, LogStoreConfig::Disabled) { + return Some(config); + } + } + + // Fallback to CLI config (any backend) + self.sources.cli_config.clone() + } + + /// Convenience method: Resolve both log store and config at once + /// + /// This is the primary entry point for most callers, as it resolves both + /// the LogStore (for querying) and LogStoreConfig (for drain selection) + /// in a single call. + pub fn resolve(&self, logger: &Logger) -> (Arc, Option) { + let store = self.resolve_log_store(logger); + let config = self.resolve_log_store_config(logger); + + if let Some(ref cfg) = config { + info!(logger, "Log drain initialized"; "backend" => format!("{:?}", cfg)); + } + + (store, config) + } + + /// Helper: Try to create log store from CLI config (any backend) + fn resolve_cli_store(&self, logger: &Logger) -> Option> { + self.sources.cli_config.as_ref().map(|config| { + match LogStoreFactory::from_config(config.clone()) { + Ok(store) => { + info!(logger, "Log store initialized from CLI configuration"); + store + } + Err(e) => { + warn!( + logger, + "Failed to initialize log store from CLI config: {}, using NoOp", e + ); + Arc::new(NoOpLogStore) + } + } + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_no_config_returns_noop() { + std::env::remove_var("GRAPH_LOG_STORE"); + + let logger = graph::log::logger(true); + let provider = LogStoreConfigProvider::new(LogStoreConfigSources { cli_config: None }); + + let store = provider.resolve_log_store(&logger); + assert!(!store.is_available()); + + let config = provider.resolve_log_store_config(&logger); + assert!(config.is_none()); + } + + #[test] + fn test_elastic_from_cli() { + std::env::remove_var("GRAPH_LOG_STORE"); + + let logger = graph::log::logger(true); + let cli_config = LogStoreConfig::Elasticsearch { + endpoint: "http://localhost:9200".to_string(), + username: Some("user".to_string()), + password: Some("pass".to_string()), + index: "test-index".to_string(), + timeout_secs: 10, + }; + + let provider = LogStoreConfigProvider::new(LogStoreConfigSources { + cli_config: Some(cli_config), + }); + + let config = provider.resolve_log_store_config(&logger); + assert!(config.is_some()); + + if let Some(LogStoreConfig::Elasticsearch { + endpoint, + username, + password, + index, + .. + }) = config + { + assert_eq!(endpoint, "http://localhost:9200"); + assert_eq!(username, Some("user".to_string())); + assert_eq!(password, Some("pass".to_string())); + assert_eq!(index, "test-index"); + } else { + panic!("Expected Elasticsearch config"); + } + } + + #[test] + fn test_resolve_convenience_method() { + std::env::remove_var("GRAPH_LOG_STORE"); + + let logger = graph::log::logger(true); + let cli_config = LogStoreConfig::Elasticsearch { + endpoint: "http://localhost:9200".to_string(), + username: None, + password: None, + index: "test-index".to_string(), + timeout_secs: 10, + }; + + let provider = LogStoreConfigProvider::new(LogStoreConfigSources { + cli_config: Some(cli_config), + }); + + let (_store, config) = provider.resolve(&logger); + assert!(config.is_some()); + + if let Some(LogStoreConfig::Elasticsearch { endpoint, .. }) = config { + assert_eq!(endpoint, "http://localhost:9200"); + } else { + panic!("Expected Elasticsearch config"); + } + } + + #[test] + fn test_loki_from_cli() { + std::env::remove_var("GRAPH_LOG_STORE"); + + let logger = graph::log::logger(true); + let cli_config = LogStoreConfig::Loki { + endpoint: "http://localhost:3100".to_string(), + tenant_id: Some("test-tenant".to_string()), + }; + + let provider = LogStoreConfigProvider::new(LogStoreConfigSources { + cli_config: Some(cli_config), + }); + + let config = provider.resolve_log_store_config(&logger); + assert!(config.is_some()); + + if let Some(LogStoreConfig::Loki { + endpoint, + tenant_id, + }) = config + { + assert_eq!(endpoint, "http://localhost:3100"); + assert_eq!(tenant_id, Some("test-tenant".to_string())); + } else { + panic!("Expected Loki config"); + } + } +} diff --git a/node/src/opt.rs b/node/src/opt.rs index 9372d4f1472..98c4223efe4 100644 --- a/node/src/opt.rs +++ b/node/src/opt.rs @@ -165,18 +165,92 @@ pub struct Opt { #[clap(long, help = "Enable debug logging")] pub debug: bool, + // ============================================ + // Log Store Configuration - NEW GENERIC ARGS + // ============================================ + #[clap( + long = "log-store-backend", + value_name = "BACKEND", + help = "Log store backend to use (disabled, elasticsearch, loki, file)" + )] + pub log_store_backend: Option, + + // --- Elasticsearch Configuration --- + #[clap( + long = "log-store-elasticsearch-url", + value_name = "URL", + help = "Elasticsearch URL for log storage" + )] + pub log_store_elasticsearch_url: Option, + #[clap( + long = "log-store-elasticsearch-user", + value_name = "USER", + help = "Elasticsearch username for authentication" + )] + pub log_store_elasticsearch_user: Option, + #[clap( + long = "log-store-elasticsearch-password", + value_name = "PASSWORD", + hide_env_values = true, + help = "Elasticsearch password for authentication" + )] + pub log_store_elasticsearch_password: Option, + #[clap( + long = "log-store-elasticsearch-index", + value_name = "INDEX", + help = "Elasticsearch index name (default: subgraph)" + )] + pub log_store_elasticsearch_index: Option, + + // --- Loki Configuration --- + #[clap( + long = "log-store-loki-url", + value_name = "URL", + help = "Loki URL for log storage" + )] + pub log_store_loki_url: Option, + #[clap( + long = "log-store-loki-tenant-id", + value_name = "TENANT_ID", + help = "Loki tenant ID for multi-tenancy" + )] + pub log_store_loki_tenant_id: Option, + + // --- File Configuration --- + #[clap( + long = "log-store-file-dir", + value_name = "DIR", + help = "Directory for file-based log storage" + )] + pub log_store_file_dir: Option, + #[clap( + long = "log-store-file-max-size", + value_name = "BYTES", + help = "Maximum log file size in bytes (default: 104857600 = 100MB)" + )] + pub log_store_file_max_size: Option, + #[clap( + long = "log-store-file-retention-days", + value_name = "DAYS", + help = "Number of days to retain log files (default: 30)" + )] + pub log_store_file_retention_days: Option, + + // ================================================ + // DEPRECATED - OLD ELASTICSEARCH-SPECIFIC ARGS + // ================================================ #[clap( long, value_name = "URL", env = "ELASTICSEARCH_URL", - help = "Elasticsearch service to write subgraph logs to" + help = "DEPRECATED: Use --log-store-elasticsearch-url instead. Elasticsearch service to write subgraph logs to" )] pub elasticsearch_url: Option, #[clap( long, value_name = "USER", env = "ELASTICSEARCH_USER", - help = "User to use for Elasticsearch logging" + help = "DEPRECATED: Use --log-store-elasticsearch-user instead. User to use for Elasticsearch logging" )] pub elasticsearch_user: Option, #[clap( @@ -184,7 +258,7 @@ pub struct Opt { value_name = "PASSWORD", env = "ELASTICSEARCH_PASSWORD", hide_env_values = true, - help = "Password to use for Elasticsearch logging" + help = "DEPRECATED: Use --log-store-elasticsearch-password instead. Password to use for Elasticsearch logging" )] pub elasticsearch_password: Option, #[clap( From 34396fb1c58f9e0bc50c057d4b460d34e02ab599 Mon Sep 17 00:00:00 2001 From: Ford Date: Thu, 15 Jan 2026 13:56:51 -0800 Subject: [PATCH 05/28] graph,node: Refactor LoggerFactory for multi-backend support Refactors LoggerFactory to use LogStoreConfig instead of elastic-only: - Replaced elastic_config with log_store_config parameter - Build ElasticLoggingConfig on-demand from LogStoreConfig::Elasticsearch - Support all log drain types (File, Loki, Elasticsearch) - Maintain backward compatibility with existing elastic configuration This enables the factory to create drains for any configured backend while preserving the existing component logger patterns. --- graph/src/log/factory.rs | 163 ++++++++++++++++++++++++++++----------- node/src/bin/manager.rs | 8 +- 2 files changed, 126 insertions(+), 45 deletions(-) diff --git a/graph/src/log/factory.rs b/graph/src/log/factory.rs index 1e8aef33b2e..58306a448b3 100644 --- a/graph/src/log/factory.rs +++ b/graph/src/log/factory.rs @@ -1,11 +1,15 @@ use std::sync::Arc; +use std::time::Duration; use prometheus::Counter; use slog::*; +use crate::components::log_store::LogStoreConfig; use crate::components::metrics::MetricsRegistry; use crate::components::store::DeploymentLocator; use crate::log::elastic::*; +use crate::log::file::{file_logger, FileDrainConfig}; +use crate::log::loki::{loki_logger, LokiDrainConfig}; use crate::log::split::*; use crate::prelude::ENV_VARS; @@ -23,20 +27,20 @@ pub struct ComponentLoggerConfig { #[derive(Clone)] pub struct LoggerFactory { parent: Logger, - elastic_config: Option, + log_store_config: Option, metrics_registry: Arc, } impl LoggerFactory { - /// Creates a new factory using a parent logger and optional Elasticsearch configuration. + /// Creates a new factory using a parent logger and optional log store configuration. pub fn new( logger: Logger, - elastic_config: Option, + log_store_config: Option, metrics_registry: Arc, ) -> Self { Self { parent: logger, - elastic_config, + log_store_config, metrics_registry, } } @@ -45,7 +49,7 @@ impl LoggerFactory { pub fn with_parent(&self, parent: Logger) -> Self { Self { parent, - elastic_config: self.elastic_config.clone(), + log_store_config: self.log_store_config.clone(), metrics_registry: self.metrics_registry.clone(), } } @@ -62,56 +66,127 @@ impl LoggerFactory { None => term_logger, Some(config) => match config.elastic { None => term_logger, - Some(config) => self - .elastic_config - .clone() - .map(|elastic_config| { - split_logger( - term_logger.clone(), - elastic_logger( - ElasticDrainConfig { - general: elastic_config, - index: config.index, - custom_id_key: String::from("componentId"), - custom_id_value: component.to_string(), - flush_interval: ENV_VARS.elastic_search_flush_interval, - max_retries: ENV_VARS.elastic_search_max_retries, - }, + Some(elastic_component_config) => { + // Check if we have Elasticsearch configured in log_store_config + match &self.log_store_config { + Some(LogStoreConfig::Elasticsearch { + endpoint, + username, + password, + .. + }) => { + // Build ElasticLoggingConfig on-demand + let elastic_config = ElasticLoggingConfig { + endpoint: endpoint.clone(), + username: username.clone(), + password: password.clone(), + client: reqwest::Client::new(), + }; + + split_logger( term_logger.clone(), - self.logs_sent_counter(None), - ), - ) - }) - .unwrap_or(term_logger), + elastic_logger( + ElasticDrainConfig { + general: elastic_config, + index: elastic_component_config.index, + custom_id_key: String::from("componentId"), + custom_id_value: component.to_string(), + flush_interval: ENV_VARS.elastic_search_flush_interval, + max_retries: ENV_VARS.elastic_search_max_retries, + }, + term_logger.clone(), + self.logs_sent_counter(None), + ), + ) + } + _ => { + // No Elasticsearch configured, just use terminal logger + term_logger + } + } + } }, } } - /// Creates a subgraph logger with Elasticsearch support. + /// Creates a subgraph logger with multi-backend support. pub fn subgraph_logger(&self, loc: &DeploymentLocator) -> Logger { let term_logger = self .parent .new(o!("subgraph_id" => loc.hash.to_string(), "sgd" => loc.id.to_string())); - self.elastic_config - .clone() - .map(|elastic_config| { - split_logger( + // Determine which drain to use based on log_store_config + let drain = match &self.log_store_config { + Some(LogStoreConfig::Elasticsearch { + endpoint, + username, + password, + index, + .. + }) => { + // Build ElasticLoggingConfig on-demand + let elastic_config = ElasticLoggingConfig { + endpoint: endpoint.clone(), + username: username.clone(), + password: password.clone(), + client: reqwest::Client::new(), + }; + + Some(elastic_logger( + ElasticDrainConfig { + general: elastic_config, + index: index.clone(), + custom_id_key: String::from("subgraphId"), + custom_id_value: loc.hash.to_string(), + flush_interval: ENV_VARS.elastic_search_flush_interval, + max_retries: ENV_VARS.elastic_search_max_retries, + }, + term_logger.clone(), + self.logs_sent_counter(Some(loc.hash.as_str())), + )) + } + + None => None, + + Some(LogStoreConfig::Loki { + endpoint, + tenant_id, + }) => { + // Use Loki + Some(loki_logger( + LokiDrainConfig { + endpoint: endpoint.clone(), + tenant_id: tenant_id.clone(), + flush_interval: Duration::from_secs(5), + subgraph_id: loc.hash.to_string(), + }, term_logger.clone(), - elastic_logger( - ElasticDrainConfig { - general: elastic_config, - index: ENV_VARS.elastic_search_index.clone(), - custom_id_key: String::from("subgraphId"), - custom_id_value: loc.hash.to_string(), - flush_interval: ENV_VARS.elastic_search_flush_interval, - max_retries: ENV_VARS.elastic_search_max_retries, - }, - term_logger.clone(), - self.logs_sent_counter(Some(loc.hash.as_str())), - ), - ) - }) + )) + } + + Some(LogStoreConfig::File { + directory, + max_file_size, + retention_days, + }) => { + // Use File + Some(file_logger( + FileDrainConfig { + directory: directory.clone(), + subgraph_id: loc.hash.to_string(), + max_file_size: *max_file_size, + retention_days: *retention_days, + }, + term_logger.clone(), + )) + } + + Some(LogStoreConfig::Disabled) => None, + }; + + // Combine terminal and storage drain + drain + .map(|storage_drain| split_logger(term_logger.clone(), storage_drain)) .unwrap_or(term_logger) } diff --git a/node/src/bin/manager.rs b/node/src/bin/manager.rs index 2eb5f41fa27..e8cb4338700 100644 --- a/node/src/bin/manager.rs +++ b/node/src/bin/manager.rs @@ -1078,7 +1078,13 @@ impl Context { let load_manager = Arc::new(LoadManager::new(&logger, vec![], vec![], registry.clone())); - Arc::new(GraphQlRunner::new(&logger, store, load_manager, registry)) + Arc::new(GraphQlRunner::new( + &logger, + store, + load_manager, + registry, + Arc::new(graph::components::log_store::NoOpLogStore), + )) } async fn networks(&self) -> anyhow::Result { From 2bfcccf83871c8a1bc73dc1742c9d5a3b0a9560e Mon Sep 17 00:00:00 2001 From: Ford Date: Thu, 15 Jan 2026 13:57:03 -0800 Subject: [PATCH 06/28] graphql,graph: Add GraphQL schema and resolver for _logs query Adds GraphQL API for querying subgraph logs: Schema types: - LogLevel enum (CRITICAL, ERROR, WARNING, INFO, DEBUG) - _Log_ type with id, timestamp, level, text, arguments, meta - _LogArgument_ type for structured key-value pairs - _LogMeta_ type for source location (module, line, column) Query field (_logs) with filters: - level: Filter by log level - from/to: Timestamp range (ISO 8601) - search: Text search in log messages - first/skip: Pagination (max 1000, skip max 10000) --- graph/src/schema/api.rs | 113 +++++++++++++++++++++- graph/src/schema/logs.graphql | 61 ++++++++++++ graph/src/schema/mod.rs | 3 + graphql/src/store/logs.rs | 174 ++++++++++++++++++++++++++++++++++ graphql/src/store/mod.rs | 1 + 5 files changed, 347 insertions(+), 5 deletions(-) create mode 100644 graph/src/schema/logs.graphql create mode 100644 graphql/src/store/logs.rs diff --git a/graph/src/schema/api.rs b/graph/src/schema/api.rs index 4ebbbdc23c4..4223b556c17 100644 --- a/graph/src/schema/api.rs +++ b/graph/src/schema/api.rs @@ -10,7 +10,7 @@ use crate::cheap_clone::CheapClone; use crate::data::graphql::{ObjectOrInterface, ObjectTypeExt, TypeExt}; use crate::data::store::IdType; use crate::env::ENV_VARS; -use crate::schema::{ast, META_FIELD_NAME, META_FIELD_TYPE, SCHEMA_TYPE_NAME}; +use crate::schema::{ast, LOGS_FIELD_NAME, META_FIELD_NAME, META_FIELD_TYPE, SCHEMA_TYPE_NAME}; use crate::data::graphql::ext::{ camel_cased_names, DefinitionExt, DirectiveExt, DocumentExt, ValueExt, @@ -349,7 +349,7 @@ pub(in crate::schema) fn api_schema( ) -> Result { // Refactor: Don't clone the schema. let mut api = init_api_schema(input_schema)?; - add_meta_field_type(&mut api.document); + add_builtin_field_types(&mut api.document); add_types_for_object_types(&mut api, input_schema)?; add_types_for_interface_types(&mut api, input_schema)?; add_types_for_aggregation_types(&mut api, input_schema)?; @@ -444,18 +444,24 @@ fn init_api_schema(input_schema: &InputSchema) -> Result .map_err(|e| APISchemaError::SchemaCreationFailed(e.to_string())) } -/// Adds a global `_Meta_` type to the schema. The `_meta` field -/// accepts values of this type -fn add_meta_field_type(api: &mut s::Document) { +/// Adds built-in field types to the schema. Currently adds `_Meta_` and `_Log_` types +/// which are used by the `_meta` and `_logs` fields respectively. +fn add_builtin_field_types(api: &mut s::Document) { lazy_static! { static ref META_FIELD_SCHEMA: s::Document = { let schema = include_str!("meta.graphql"); s::parse_schema(schema).expect("the schema `meta.graphql` is invalid") }; + static ref LOGS_FIELD_SCHEMA: s::Document = { + let schema = include_str!("logs.graphql"); + s::parse_schema(schema).expect("the schema `logs.graphql` is invalid") + }; } api.definitions .extend(META_FIELD_SCHEMA.definitions.iter().cloned()); + api.definitions + .extend(LOGS_FIELD_SCHEMA.definitions.iter().cloned()); } fn add_types_for_object_types( @@ -1098,6 +1104,7 @@ fn add_query_type(api: &mut s::Document, input_schema: &InputSchema) -> Result<( fields.append(&mut agg_fields); fields.append(&mut fulltext_fields); fields.push(meta_field()); + fields.push(logs_field()); let typedef = s::TypeDefinition::Object(s::ObjectType { position: q::Pos::default(), @@ -1303,6 +1310,102 @@ fn meta_field() -> s::Field { META_FIELD.clone() } +fn logs_field() -> s::Field { + lazy_static! { + static ref LOGS_FIELD: s::Field = s::Field { + position: Pos::default(), + description: Some( + "Query execution logs emitted by the subgraph during indexing. \ + Results are sorted by timestamp in descending order (newest first)." + .to_string() + ), + name: LOGS_FIELD_NAME.to_string(), + arguments: vec![ + // level: LogLevel + s::InputValue { + position: Pos::default(), + description: Some( + "Filter logs by severity level. Only logs at this level will be returned." + .to_string() + ), + name: String::from("level"), + value_type: s::Type::NamedType(String::from("LogLevel")), + default_value: None, + directives: vec![], + }, + // from: String (RFC3339 timestamp) + s::InputValue { + position: Pos::default(), + description: Some( + "Filter logs from this timestamp onwards (inclusive). \ + Must be in RFC3339 format (e.g., '2024-01-15T10:30:00Z')." + .to_string() + ), + name: String::from("from"), + value_type: s::Type::NamedType(String::from("String")), + default_value: None, + directives: vec![], + }, + // to: String (RFC3339 timestamp) + s::InputValue { + position: Pos::default(), + description: Some( + "Filter logs until this timestamp (inclusive). \ + Must be in RFC3339 format (e.g., '2024-01-15T23:59:59Z')." + .to_string() + ), + name: String::from("to"), + value_type: s::Type::NamedType(String::from("String")), + default_value: None, + directives: vec![], + }, + // search: String (full-text search) + s::InputValue { + position: Pos::default(), + description: Some( + "Search for logs containing this text in the message. \ + Case-insensitive substring match. Maximum length: 1000 characters." + .to_string() + ), + name: String::from("search"), + value_type: s::Type::NamedType(String::from("String")), + default_value: None, + directives: vec![], + }, + // first: Int (default 100, max 1000) + s::InputValue { + position: Pos::default(), + description: Some( + "Maximum number of logs to return. Default: 100, Maximum: 1000." + .to_string() + ), + name: String::from("first"), + value_type: s::Type::NamedType(String::from("Int")), + default_value: Some(s::Value::Int(100.into())), + directives: vec![], + }, + // skip: Int (default 0, max 10000) + s::InputValue { + position: Pos::default(), + description: Some( + "Number of logs to skip (for pagination). Default: 0, Maximum: 10000." + .to_string() + ), + name: String::from("skip"), + value_type: s::Type::NamedType(String::from("Int")), + default_value: Some(s::Value::Int(0.into())), + directives: vec![], + }, + ], + field_type: s::Type::NonNullType(Box::new(s::Type::ListType(Box::new( + s::Type::NonNullType(Box::new(s::Type::NamedType(String::from("_Log_")))), + )))), + directives: vec![], + }; + } + LOGS_FIELD.clone() +} + #[cfg(test)] mod tests { use crate::{ diff --git a/graph/src/schema/logs.graphql b/graph/src/schema/logs.graphql new file mode 100644 index 00000000000..be80e029152 --- /dev/null +++ b/graph/src/schema/logs.graphql @@ -0,0 +1,61 @@ +""" +The severity level of a log entry. +Log levels are ordered from most to least severe: CRITICAL > ERROR > WARNING > INFO > DEBUG +""" +enum LogLevel { + "Critical errors that require immediate attention" + CRITICAL + "Error conditions that indicate a failure" + ERROR + "Warning conditions that may require attention" + WARNING + "Informational messages about normal operations" + INFO + "Detailed diagnostic information for debugging" + DEBUG +} + +""" +A log entry emitted by a subgraph during indexing. +Logs can be generated by the subgraph's AssemblyScript code using the `log.*` functions. +""" +type _Log_ { + "Unique identifier for this log entry" + id: String! + "The deployment hash of the subgraph that emitted this log" + subgraphId: String! + "The timestamp when the log was emitted, in RFC3339 format (e.g., '2024-01-15T10:30:00Z')" + timestamp: String! + "The severity level of the log entry" + level: LogLevel! + "The log message text" + text: String! + "Additional structured data passed to the log function as key-value pairs" + arguments: [_LogArgument_!]! + "Metadata about the source location in the subgraph code where the log was emitted" + meta: _LogMeta_! +} + +""" +A key-value pair of additional data associated with a log entry. +These correspond to arguments passed to the log function in the subgraph code. +""" +type _LogArgument_ { + "The parameter name" + key: String! + "The parameter value, serialized as a string" + value: String! +} + +""" +Source code location metadata for a log entry. +Indicates where in the subgraph's AssemblyScript code the log statement was executed. +""" +type _LogMeta_ { + "The module or file path where the log was emitted" + module: String! + "The line number in the source file" + line: Int! + "The column number in the source file" + column: Int! +} diff --git a/graph/src/schema/mod.rs b/graph/src/schema/mod.rs index 1e40299df63..095c1a454df 100644 --- a/graph/src/schema/mod.rs +++ b/graph/src/schema/mod.rs @@ -41,6 +41,9 @@ pub const INTROSPECTION_SCHEMA_FIELD_NAME: &str = "__schema"; pub const META_FIELD_TYPE: &str = "_Meta_"; pub const META_FIELD_NAME: &str = "_meta"; +pub const LOGS_FIELD_TYPE: &str = "_Log_"; +pub const LOGS_FIELD_NAME: &str = "_logs"; + pub const INTROSPECTION_TYPE_FIELD_NAME: &str = "__type"; pub const BLOCK_FIELD_TYPE: &str = "_Block_"; diff --git a/graphql/src/store/logs.rs b/graphql/src/store/logs.rs new file mode 100644 index 00000000000..44b60041ffa --- /dev/null +++ b/graphql/src/store/logs.rs @@ -0,0 +1,174 @@ +use graph::components::log_store::LogQuery; +use graph::prelude::{q, r, DeploymentHash, QueryExecutionError}; + +use crate::execution::ast as a; + +const MAX_FIRST: u32 = 1000; +const MAX_SKIP: u32 = 10000; +const MAX_TEXT_LENGTH: usize = 1000; + +/// Validate and sanitize text search input to prevent injection attacks +fn validate_text_input(text: &str) -> Result<(), &'static str> { + if text.is_empty() { + return Err("search text cannot be empty"); + } + + if text.len() > MAX_TEXT_LENGTH { + return Err("search text exceeds maximum length of 1000 characters"); + } + + // Reject strings that look like Elasticsearch query DSL to prevent injection + if text + .chars() + .any(|c| matches!(c, '{' | '}' | '[' | ']' | ':' | '"')) + { + return Err("search text contains invalid characters ({}[]:\")"); + } + + Ok(()) +} + +/// Validate RFC3339 timestamp format +fn validate_timestamp(timestamp: &str) -> Result<(), &'static str> { + if !timestamp.contains('T') { + return Err("must be in ISO 8601 format (e.g., 2024-01-15T10:30:00Z)"); + } + + if !timestamp.ends_with('Z') && !timestamp.contains('+') && !timestamp.contains('-') { + return Err("must include timezone (Z or offset like +00:00)"); + } + + if timestamp.len() > 50 { + return Err("timestamp exceeds maximum length"); + } + + // Check for suspicious characters that could be injection attempts + if timestamp + .chars() + .any(|c| matches!(c, '{' | '}' | ';' | '\'' | '"')) + { + return Err("timestamp contains invalid characters"); + } + + Ok(()) +} + +pub fn build_log_query( + field: &a::Field, + subgraph_id: &DeploymentHash, +) -> Result { + let mut level = None; + let mut from = None; + let mut to = None; + let mut search = None; + let mut first = 100; + let mut skip = 0; + + // Parse arguments + for (name, value) in &field.arguments { + match name.as_str() { + "level" => { + if let r::Value::Enum(level_str) = value { + level = Some(level_str.parse().map_err(|e: String| { + QueryExecutionError::InvalidArgumentError( + field.position, + "level".to_string(), + q::Value::String(e), + ) + })?); + } + } + "from" => { + if let r::Value::String(from_str) = value { + validate_timestamp(from_str).map_err(|e| { + QueryExecutionError::InvalidArgumentError( + field.position, + "from".to_string(), + q::Value::String(format!("Invalid timestamp: {}", e)), + ) + })?; + from = Some(from_str.clone()); + } + } + "to" => { + if let r::Value::String(to_str) = value { + validate_timestamp(to_str).map_err(|e| { + QueryExecutionError::InvalidArgumentError( + field.position, + "to".to_string(), + q::Value::String(format!("Invalid timestamp: {}", e)), + ) + })?; + to = Some(to_str.clone()); + } + } + "search" => { + if let r::Value::String(search_str) = value { + validate_text_input(search_str).map_err(|e| { + QueryExecutionError::InvalidArgumentError( + field.position, + "search".to_string(), + q::Value::String(format!("Invalid search text: {}", e)), + ) + })?; + search = Some(search_str.clone()); + } + } + "first" => { + if let r::Value::Int(first_val) = value { + let first_i64 = *first_val; + if first_i64 < 0 { + return Err(QueryExecutionError::InvalidArgumentError( + field.position, + "first".to_string(), + q::Value::String("first must be non-negative".to_string()), + )); + } + let first_u32 = first_i64 as u32; + if first_u32 > MAX_FIRST { + return Err(QueryExecutionError::InvalidArgumentError( + field.position, + "first".to_string(), + q::Value::String(format!("first must not exceed {}", MAX_FIRST)), + )); + } + first = first_u32; + } + } + "skip" => { + if let r::Value::Int(skip_val) = value { + let skip_i64 = *skip_val; + if skip_i64 < 0 { + return Err(QueryExecutionError::InvalidArgumentError( + field.position, + "skip".to_string(), + q::Value::String("skip must be non-negative".to_string()), + )); + } + let skip_u32 = skip_i64 as u32; + if skip_u32 > MAX_SKIP { + return Err(QueryExecutionError::InvalidArgumentError( + field.position, + "skip".to_string(), + q::Value::String(format!("skip must not exceed {}", MAX_SKIP)), + )); + } + skip = skip_u32; + } + } + _ => { + // Unknown argument, ignore + } + } + } + + Ok(LogQuery { + subgraph_id: subgraph_id.clone(), + level, + from, + to, + search, + first, + skip, + }) +} diff --git a/graphql/src/store/mod.rs b/graphql/src/store/mod.rs index 6a4850b6a86..8f77a832b90 100644 --- a/graphql/src/store/mod.rs +++ b/graphql/src/store/mod.rs @@ -1,3 +1,4 @@ +pub mod logs; mod prefetch; mod query; mod resolver; From f47e987b32a078dcd2bcd72e04cd20b95417b624 Mon Sep 17 00:00:00 2001 From: Ford Date: Thu, 15 Jan 2026 13:57:13 -0800 Subject: [PATCH 07/28] graphql,node,server,store: Wire up _logs query in execution Integrates _logs query into the GraphQL execution pipeline: Execution layer: - Execute _logs queries via log_store.query_logs() - Convert LogEntry results to GraphQL response objects - Handle log store errors gracefully Query parsing: - Recognize _logs as special query field - Build LogQuery from GraphQL arguments - Pass log_store to execution context Service wiring: - Create log store from configuration in launcher - Provide log store to GraphQL runner - Use NoOpLogStore in test environments This completes the read path from GraphQL query to log storage backend. --- graphql/src/execution/execution.rs | 71 +++++++++++++++- graphql/src/query/mod.rs | 4 + graphql/src/runner.rs | 4 + node/src/launcher.rs | 127 ++++++++++++++++++++++++++--- server/index-node/src/service.rs | 1 + store/test-store/src/store.rs | 3 +- 6 files changed, 195 insertions(+), 15 deletions(-) diff --git a/graphql/src/execution/execution.rs b/graphql/src/execution/execution.rs index 48477d3eb5f..ba6f81f4ac5 100644 --- a/graphql/src/execution/execution.rs +++ b/graphql/src/execution/execution.rs @@ -8,7 +8,7 @@ use graph::{ }, futures03::future::TryFutureExt, prelude::{s, CheapClone}, - schema::{is_introspection_field, INTROSPECTION_QUERY_TYPE, META_FIELD_NAME}, + schema::{is_introspection_field, INTROSPECTION_QUERY_TYPE, LOGS_FIELD_NAME, META_FIELD_NAME}, util::{herd_cache::HerdCache, lfu_cache::EvictStats, timed_rw_lock::TimedMutex}, }; use lazy_static::lazy_static; @@ -231,6 +231,9 @@ where /// Whether to include an execution trace in the result pub trace: bool, + + /// The log store to use for querying logs. + pub log_store: Arc, } pub(crate) fn get_field<'a>( @@ -264,6 +267,7 @@ where // `cache_status` is a dead value for the introspection context. cache_status: AtomicCell::new(CacheStatus::Miss), trace: ENV_VARS.log_sql_timing(), + log_store: self.log_store.cheap_clone(), } } } @@ -273,11 +277,12 @@ pub(crate) async fn execute_root_selection_set_uncached( selection_set: &a::SelectionSet, root_type: &sast::ObjectType, ) -> Result<(Object, Trace), Vec> { - // Split the top-level fields into introspection fields and - // regular data fields + // Split the top-level fields into introspection fields, + // logs fields, meta fields, and regular data fields let mut data_set = a::SelectionSet::empty_from(selection_set); let mut intro_set = a::SelectionSet::empty_from(selection_set); let mut meta_items = Vec::new(); + let mut logs_fields = Vec::new(); for field in selection_set.fields_for(root_type)? { // See if this is an introspection or data field. We don't worry about @@ -285,6 +290,8 @@ pub(crate) async fn execute_root_selection_set_uncached( // the data_set SelectionSet if is_introspection_field(&field.name) { intro_set.push(field)? + } else if field.name == LOGS_FIELD_NAME { + logs_fields.push(field) } else if field.name == META_FIELD_NAME || field.name == "__typename" { meta_items.push(field) } else { @@ -314,6 +321,64 @@ pub(crate) async fn execute_root_selection_set_uncached( ); } + // Resolve logs fields, if there are any + for field in logs_fields { + use graph::data::graphql::object; + + // Build log query from field arguments + let log_query = crate::store::logs::build_log_query(field, ctx.query.schema.id()) + .map_err(|e| vec![e])?; + + // Query the log store + let log_entries = ctx.log_store.query_logs(log_query).await.map_err(|e| { + vec![QueryExecutionError::StoreError( + anyhow::Error::from(e).into(), + )] + })?; + + // Convert log entries to GraphQL values + let log_values: Vec = log_entries + .into_iter() + .map(|entry| { + // Convert arguments Vec<(String, String)> to GraphQL objects + let arguments: Vec = entry + .arguments + .into_iter() + .map(|(key, value)| { + object! { + key: key, + value: value, + __typename: "_LogArgument_" + } + }) + .collect(); + + // Convert log level to string + let level_str = entry.level.as_str().to_uppercase(); + + object! { + id: entry.id, + subgraphId: entry.subgraph_id.to_string(), + timestamp: entry.timestamp, + level: level_str, + text: entry.text, + arguments: arguments, + meta: object! { + module: entry.meta.module, + line: r::Value::Int(entry.meta.line), + column: r::Value::Int(entry.meta.column), + __typename: "_LogMeta_" + }, + __typename: "_Log_" + } + }) + .collect(); + + let response_key = Word::from(field.response_key()); + let logs_object = Object::from_iter(vec![(response_key, r::Value::List(log_values))]); + values.append(logs_object); + } + Ok((values, trace)) } diff --git a/graphql/src/query/mod.rs b/graphql/src/query/mod.rs index 641eb4581bb..86244c4cc71 100644 --- a/graphql/src/query/mod.rs +++ b/graphql/src/query/mod.rs @@ -29,6 +29,9 @@ pub struct QueryExecutionOptions { /// Whether to include an execution trace in the result pub trace: bool, + + /// The log store to use for querying logs. + pub log_store: Arc, } /// Executes a query and returns a result. @@ -52,6 +55,7 @@ where max_skip: options.max_skip, cache_status: Default::default(), trace: options.trace, + log_store: options.log_store, }); let selection_set = selection_set diff --git a/graphql/src/runner.rs b/graphql/src/runner.rs index 293dcaa111b..3af945e030f 100644 --- a/graphql/src/runner.rs +++ b/graphql/src/runner.rs @@ -26,6 +26,7 @@ pub struct GraphQlRunner { store: Arc, load_manager: Arc, graphql_metrics: Arc, + log_store: Arc, } #[cfg(debug_assertions)] @@ -44,6 +45,7 @@ where store: Arc, load_manager: Arc, registry: Arc, + log_store: Arc, ) -> Self { let logger = logger.new(o!("component" => "GraphQlRunner")); let graphql_metrics = Arc::new(GraphQLMetrics::new(registry)); @@ -52,6 +54,7 @@ where store, load_manager, graphql_metrics, + log_store, } } @@ -186,6 +189,7 @@ where max_first: max_first.unwrap_or(ENV_VARS.graphql.max_first), max_skip: max_skip.unwrap_or(ENV_VARS.graphql.max_skip), trace: do_trace, + log_store: self.log_store.cheap_clone(), }, )); } diff --git a/node/src/launcher.rs b/node/src/launcher.rs index c07b0e06426..19e61d5cf10 100644 --- a/node/src/launcher.rs +++ b/node/src/launcher.rs @@ -37,6 +37,7 @@ use tokio_util::sync::CancellationToken; use crate::config::Config; use crate::helpers::watch_subgraph_updates; +use crate::log_config_provider::{LogStoreConfigProvider, LogStoreConfigSources}; use crate::network_setup::Networks; use crate::opt::Opt; use crate::store_builder::StoreBuilder; @@ -367,6 +368,7 @@ fn build_graphql_server( metrics_registry: Arc, network_store: &Arc, logger_factory: &LoggerFactory, + log_store: Arc, ) -> GraphQLQueryServer> { let shards: Vec<_> = config.stores.keys().cloned().collect(); let load_manager = Arc::new(LoadManager::new( @@ -380,6 +382,7 @@ fn build_graphql_server( network_store.clone(), load_manager, metrics_registry, + log_store, )); GraphQLQueryServer::new(logger_factory, graphql_runner.clone()) @@ -443,20 +446,121 @@ pub async fn run( info!(logger, "Starting up"; "node_id" => &node_id); - // Optionally, identify the Elasticsearch logging configuration - let elastic_config = opt - .elasticsearch_url - .clone() - .map(|endpoint| ElasticLoggingConfig { - endpoint, - username: opt.elasticsearch_user.clone(), - password: opt.elasticsearch_password.clone(), - client: reqwest::Client::new(), - }); + // Create log store configuration provider + // Build LogStoreConfig from CLI args with backward compatibility + let cli_config = if let Some(backend) = opt.log_store_backend.as_ref() { + // New generic CLI args used + match backend.to_lowercase().as_str() { + "elasticsearch" | "elastic" | "es" => { + let url = opt + .log_store_elasticsearch_url + .clone() + .or_else(|| { + if opt.elasticsearch_url.is_some() { + warn!( + logger, + "Using deprecated --elasticsearch-url, use --log-store-elasticsearch-url instead" + ); + } + opt.elasticsearch_url.clone() + }); + + url.map(|endpoint| { + let index = opt + .log_store_elasticsearch_index + .clone() + .or_else(|| std::env::var("GRAPH_LOG_STORE_ELASTICSEARCH_INDEX").ok()) + .or_else(|| std::env::var("GRAPH_ELASTIC_SEARCH_INDEX").ok()) + .unwrap_or_else(|| "subgraph".to_string()); + + let timeout_secs = std::env::var("GRAPH_LOG_STORE_ELASTICSEARCH_TIMEOUT") + .or_else(|_| std::env::var("GRAPH_ELASTICSEARCH_TIMEOUT")) + .ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or(10); + + graph::components::log_store::LogStoreConfig::Elasticsearch { + endpoint, + username: opt + .log_store_elasticsearch_user + .clone() + .or_else(|| opt.elasticsearch_user.clone()), + password: opt + .log_store_elasticsearch_password + .clone() + .or_else(|| opt.elasticsearch_password.clone()), + index, + timeout_secs, + } + }) + } + + "loki" => opt.log_store_loki_url.clone().map(|endpoint| { + graph::components::log_store::LogStoreConfig::Loki { + endpoint, + tenant_id: opt.log_store_loki_tenant_id.clone(), + } + }), + + "file" | "files" => opt.log_store_file_dir.clone().map(|directory| { + graph::components::log_store::LogStoreConfig::File { + directory: std::path::PathBuf::from(directory), + max_file_size: opt.log_store_file_max_size.unwrap_or(100 * 1024 * 1024), + retention_days: opt.log_store_file_retention_days.unwrap_or(30), + } + }), + + "disabled" | "none" => None, + + other => { + warn!(logger, "Invalid log store backend: {}", other); + None + } + } + } else if opt.elasticsearch_url.is_some() { + // Old Elasticsearch-specific CLI args used (backward compatibility) + warn!( + logger, + "Using deprecated --elasticsearch-url CLI argument, \ + please use --log-store-backend elasticsearch --log-store-elasticsearch-url instead" + ); + + let index = opt + .log_store_elasticsearch_index + .clone() + .or_else(|| std::env::var("GRAPH_LOG_STORE_ELASTICSEARCH_INDEX").ok()) + .or_else(|| std::env::var("GRAPH_ELASTIC_SEARCH_INDEX").ok()) + .unwrap_or_else(|| "subgraph".to_string()); + + let timeout_secs = std::env::var("GRAPH_LOG_STORE_ELASTICSEARCH_TIMEOUT") + .or_else(|_| std::env::var("GRAPH_ELASTICSEARCH_TIMEOUT")) + .ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or(10); + + Some( + graph::components::log_store::LogStoreConfig::Elasticsearch { + endpoint: opt.elasticsearch_url.clone().unwrap(), + username: opt.elasticsearch_user.clone(), + password: opt.elasticsearch_password.clone(), + index, + timeout_secs, + }, + ) + } else { + // No CLI config provided + None + }; + + let log_config_provider = LogStoreConfigProvider::new(LogStoreConfigSources { cli_config }); + + // Resolve log store (for querying) and config (for drains) + // Priority: GRAPH_LOG_STORE env var → CLI config → NoOp/None + let (log_store, log_store_config) = log_config_provider.resolve(&logger); // Create a component and subgraph logger factory let logger_factory = - LoggerFactory::new(logger.clone(), elastic_config, metrics_registry.clone()); + LoggerFactory::new(logger.clone(), log_store_config, metrics_registry.clone()); let arweave_resolver = Arc::new(ArweaveClient::new( logger.cheap_clone(), @@ -573,6 +677,7 @@ pub async fn run( metrics_registry.clone(), &network_store, &logger_factory, + log_store.clone(), ); let index_node_server = IndexNodeServer::new( diff --git a/server/index-node/src/service.rs b/server/index-node/src/service.rs index 09ddfd29038..d54e658c430 100644 --- a/server/index-node/src/service.rs +++ b/server/index-node/src/service.rs @@ -153,6 +153,7 @@ where max_first: u32::MAX, max_skip: u32::MAX, trace: false, + log_store: Arc::new(graph::components::log_store::NoOpLogStore), }; let (result, _) = execute_query(query_clone.cheap_clone(), None, None, options).await; query_clone.log_execution(0); diff --git a/store/test-store/src/store.rs b/store/test-store/src/store.rs index b90d244bbcc..ee991b7d471 100644 --- a/store/test-store/src/store.rs +++ b/store/test-store/src/store.rs @@ -570,7 +570,7 @@ async fn execute_subgraph_query_internal( error_policy, query.schema.id().clone(), graphql_metrics(), - LOAD_MANAGER.clone() + LOAD_MANAGER.clone(), ) .await ); @@ -584,6 +584,7 @@ async fn execute_subgraph_query_internal( max_first: u32::MAX, max_skip: u32::MAX, trace, + log_store: std::sync::Arc::new(graph::components::log_store::NoOpLogStore), }, ) .await; From b807aab366c37fbc1fa8ef88711ca88485205bea Mon Sep 17 00:00:00 2001 From: Ford Date: Thu, 15 Jan 2026 13:57:24 -0800 Subject: [PATCH 08/28] tests: Add integration tests for log querying Adds comprehensive integration test for _logs query: Test implementation: - Deploys logs-query subgraph and waits for sync - Triggers contract events to generate logs - Queries _logs field with various filters - Verifies log entries are returned correctly - Tests filtering by level and text search --- CLAUDE.md | 3 + pnpm-lock.yaml | 1829 ++++++++++------- .../test-store/tests/graphql/introspection.rs | 1 + .../tests/graphql/mock_introspection.json | 387 +++- store/test-store/tests/graphql/query.rs | 2 + .../logs-query/abis/Contract.abi | 33 + .../integration-tests/logs-query/package.json | 13 + .../logs-query/schema.graphql | 4 + .../logs-query/src/mapping.ts | 39 + .../logs-query/subgraph.yaml | 26 + tests/src/config.rs | 39 +- tests/src/fixture/mod.rs | 1 + tests/tests/integration_tests.rs | 149 +- 13 files changed, 1797 insertions(+), 729 deletions(-) create mode 100644 tests/integration-tests/logs-query/abis/Contract.abi create mode 100644 tests/integration-tests/logs-query/package.json create mode 100644 tests/integration-tests/logs-query/schema.graphql create mode 100644 tests/integration-tests/logs-query/src/mapping.ts create mode 100644 tests/integration-tests/logs-query/subgraph.yaml diff --git a/CLAUDE.md b/CLAUDE.md index 4432890bfd8..edb8dd1ee3c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -98,6 +98,9 @@ TEST_CASE=grafted just test-integration # (Optional) Use graph-cli instead of gnd for compatibility testing GRAPH_CLI=node_modules/.bin/graph just test-integration + +# Override ports if using different service ports (e.g., for local development) +POSTGRES_TEST_PORT=5432 ETHEREUM_TEST_PORT=8545 IPFS_TEST_PORT=5001 just test-integration ``` **⚠️ Test Verification Requirements:** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d361bbe9c56..4470fab39b0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,147 +6,206 @@ settings: importers: - .: - devDependencies: - assemblyscript: - specifier: 0.19.23 - version: 0.19.23 + .: {} tests/integration-tests/base: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/block-handlers: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/declared-calls-basic: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.97.1 + version: 0.97.1(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@graphprotocol/graph-ts': specifier: 0.33.0 version: 0.33.0 tests/integration-tests/declared-calls-struct-fields: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.97.1 + version: 0.97.1(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@graphprotocol/graph-ts': specifier: 0.33.0 version: 0.33.0 tests/integration-tests/ethereum-api-tests: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.36.0-alpha-20240422133139-8761ea3 version: 0.36.0-alpha-20240422133139-8761ea3 tests/integration-tests/grafted: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/host-exports: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/int8: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) + '@graphprotocol/graph-ts': + specifier: 0.34.0 + version: 0.34.0 + + tests/integration-tests/logs-query: + devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/multiple-subgraph-datasources: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.93.4-alpha-20250105163501-f401d0c57c4ba1f1af95a928d447efd63a56ecdc + version: 0.93.4-alpha-20250105163501-f401d0c57c4ba1f1af95a928d447efd63a56ecdc(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@graphprotocol/graph-ts': specifier: 0.36.0-alpha-20241129215038-b75cda9 version: 0.36.0-alpha-20241129215038-b75cda9 tests/integration-tests/non-fatal-errors: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/overloaded-functions: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/poi-for-failed-subgraph: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/remove-then-update: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/reverted-calls: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/source-subgraph: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.91.0-alpha-20241129215038-b75cda9 + version: 0.91.0-alpha-20241129215038-b75cda9(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.36.0-alpha-20241129215038-b75cda9 version: 0.36.0-alpha-20241129215038-b75cda9 tests/integration-tests/source-subgraph-a: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/source-subgraph-b: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/subgraph-data-sources: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.93.4-alpha-20250105163501-f401d0c57c4ba1f1af95a928d447efd63a56ecdc + version: 0.93.4-alpha-20250105163501-f401d0c57c4ba1f1af95a928d447efd63a56ecdc(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@graphprotocol/graph-ts': specifier: 0.36.0-alpha-20241129215038-b75cda9 version: 0.36.0-alpha-20241129215038-b75cda9 tests/integration-tests/timestamp: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/topic-filter: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.71.0-alpha-20240419180731-51ea29d + version: 0.71.0-alpha-20240419180731-51ea29d(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.35.0 version: 0.35.0 tests/integration-tests/value-roundtrip: devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 - tests/runner-tests/aggregation-current-bucket: - devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.98.1 - version: 0.98.1(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@graphprotocol/graph-ts': - specifier: 0.35.0 - version: 0.35.0 - tests/runner-tests/api-version: devDependencies: '@graphprotocol/graph-cli': @@ -258,6 +317,21 @@ importers: specifier: 0.31.0 version: 0.31.0 + tests/runner-tests/logs-query: + devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.69.0 + version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) + '@graphprotocol/graph-ts': + specifier: 0.34.0 + version: 0.34.0 + + tests/runner-tests/substreams: + devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.61.0 + version: 0.61.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) + tests/runner-tests/typename: devDependencies: '@graphprotocol/graph-cli': @@ -287,10 +361,6 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@dnsquery/dns-packet@6.1.1': - resolution: {integrity: sha512-WXTuFvL3G+74SchFAtz3FgIYVOe196ycvGsMgvSH/8Goptb1qpIQtIuM4SOK9G9lhMWYpHxnXyy544ZhluFOew==} - engines: {node: '>=6'} - '@ethersproject/abi@5.0.7': resolution: {integrity: sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw==} @@ -367,8 +437,33 @@ packages: engines: {node: '>=14'} hasBin: true - '@graphprotocol/graph-cli@0.98.1': - resolution: {integrity: sha512-GrWFcRCBlLcRT+gIGundQl7yyrX3YWUPj66bxThKf5CJvvWXdZoNxrj27dMMqulsSwYmpCkb3YmpCiVJFGdpHw==} + '@graphprotocol/graph-cli@0.61.0': + resolution: {integrity: sha512-gc3+DioZ/K40sQCt6DsNvbqfPTc9ZysuSz3I9MJ++bD6SftaSSweWwfpPysDMzDuxvUAhLAsJ6QjBACPngT2Kw==} + engines: {node: '>=14'} + hasBin: true + + '@graphprotocol/graph-cli@0.69.0': + resolution: {integrity: sha512-DoneR0TRkZYumsygdi/RST+OB55TgwmhziredI21lYzfj0QNXGEHZOagTOKeFKDFEpP3KR6BAq6rQIrkprJ1IQ==} + engines: {node: '>=18'} + hasBin: true + + '@graphprotocol/graph-cli@0.71.0-alpha-20240419180731-51ea29d': + resolution: {integrity: sha512-S8TRg4aHzsRQ0I7aJl91d4R2qoPzK0svrRpFcqzZ4AoYr52yBdmPo4yTsSDlB8sQl2zz2e5avJ5r1avU1J7m+g==} + engines: {node: '>=18'} + hasBin: true + + '@graphprotocol/graph-cli@0.91.0-alpha-20241129215038-b75cda9': + resolution: {integrity: sha512-LpfQPjOkCOquTeWqeeC9MJr4eTyKspl2g8u/K8S8qe3SKzMmuHcwQfq/dgBxCbs3m+4vrDYJgDUcQNJ6W5afyw==} + engines: {node: '>=18'} + hasBin: true + + '@graphprotocol/graph-cli@0.93.4-alpha-20250105163501-f401d0c57c4ba1f1af95a928d447efd63a56ecdc': + resolution: {integrity: sha512-+pleAuy1422Q26KCNjMd+DJvjazEb3rSRTM+Y0cRwdMJtl2qcDAXUcg9E/9z+tpCFxx61ujf7T3z04x8Tlq+Lg==} + engines: {node: '>=20.18.1'} + hasBin: true + + '@graphprotocol/graph-cli@0.97.1': + resolution: {integrity: sha512-j5dc2Tl694jMZmVQu8SSl5Yt3VURiBPgglQEpx30aW6UJ89eLR/x46Nn7S6eflV69fmB5IHAuAACnuTzo8MD0Q==} engines: {node: '>=20.18.1'} hasBin: true @@ -393,12 +488,8 @@ packages: '@graphprotocol/graph-ts@0.36.0-alpha-20241129215038-b75cda9': resolution: {integrity: sha512-DPLx/owGh38n6HCQaxO6rk40zfYw3EYqSvyHp+s3ClMCxQET9x4/hberkOXrPaxxiPxgUTVa6ie4mwc7GTroEw==} - '@inquirer/ansi@1.0.2': - resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} - engines: {node: '>=18'} - - '@inquirer/checkbox@4.3.2': - resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} + '@inquirer/checkbox@4.2.1': + resolution: {integrity: sha512-bevKGO6kX1eM/N+pdh9leS5L7TBF4ICrzi9a+cbWkrxeAeIcwlo/7OfWGCDERdRCI2/Q6tjltX4bt07ALHDwFw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -406,8 +497,8 @@ packages: '@types/node': optional: true - '@inquirer/confirm@5.1.21': - resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} + '@inquirer/confirm@5.1.15': + resolution: {integrity: sha512-SwHMGa8Z47LawQN0rog0sT+6JpiL0B7eW9p1Bb7iCeKDGTI5Ez25TSc2l8kw52VV7hA4sX/C78CGkMrKXfuspA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -415,8 +506,8 @@ packages: '@types/node': optional: true - '@inquirer/core@10.3.2': - resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} + '@inquirer/core@10.1.15': + resolution: {integrity: sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -424,8 +515,8 @@ packages: '@types/node': optional: true - '@inquirer/editor@4.2.23': - resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} + '@inquirer/editor@4.2.17': + resolution: {integrity: sha512-r6bQLsyPSzbWrZZ9ufoWL+CztkSatnJ6uSxqd6N+o41EZC51sQeWOzI6s5jLb+xxTWxl7PlUppqm8/sow241gg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -433,8 +524,8 @@ packages: '@types/node': optional: true - '@inquirer/expand@4.0.23': - resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} + '@inquirer/expand@4.0.17': + resolution: {integrity: sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -442,8 +533,8 @@ packages: '@types/node': optional: true - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + '@inquirer/external-editor@1.0.1': + resolution: {integrity: sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -451,12 +542,12 @@ packages: '@types/node': optional: true - '@inquirer/figures@1.0.15': - resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} + '@inquirer/figures@1.0.13': + resolution: {integrity: sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==} engines: {node: '>=18'} - '@inquirer/input@4.3.1': - resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} + '@inquirer/input@4.2.1': + resolution: {integrity: sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -464,8 +555,8 @@ packages: '@types/node': optional: true - '@inquirer/number@3.0.23': - resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} + '@inquirer/number@3.0.17': + resolution: {integrity: sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -473,8 +564,8 @@ packages: '@types/node': optional: true - '@inquirer/password@4.0.23': - resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} + '@inquirer/password@4.0.17': + resolution: {integrity: sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -482,8 +573,8 @@ packages: '@types/node': optional: true - '@inquirer/prompts@7.10.1': - resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==} + '@inquirer/prompts@7.8.3': + resolution: {integrity: sha512-iHYp+JCaCRktM/ESZdpHI51yqsDgXu+dMs4semzETftOaF8u5hwlqnbIsuIR/LrWZl8Pm1/gzteK9I7MAq5HTA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -491,8 +582,8 @@ packages: '@types/node': optional: true - '@inquirer/rawlist@4.1.11': - resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} + '@inquirer/rawlist@4.1.5': + resolution: {integrity: sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -500,8 +591,8 @@ packages: '@types/node': optional: true - '@inquirer/search@3.2.2': - resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} + '@inquirer/search@3.1.0': + resolution: {integrity: sha512-PMk1+O/WBcYJDq2H7foV0aAZSmDdkzZB9Mw2v/DmONRJopwA/128cS9M/TXWLKKdEQKZnKwBzqu2G4x/2Nqx8Q==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -509,8 +600,8 @@ packages: '@types/node': optional: true - '@inquirer/select@4.4.2': - resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} + '@inquirer/select@4.3.1': + resolution: {integrity: sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -518,8 +609,8 @@ packages: '@types/node': optional: true - '@inquirer/type@3.0.10': - resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} + '@inquirer/type@3.0.8': + resolution: {integrity: sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -530,12 +621,12 @@ packages: '@ipld/dag-cbor@7.0.3': resolution: {integrity: sha512-1VVh2huHsuohdXC1bGJNE8WR72slZ9XE2T3wbBBq31dm7ZBatmKLLxrB+XAqafxfRFjv08RZmj/W/ZqaM13AuA==} - '@ipld/dag-cbor@9.2.5': - resolution: {integrity: sha512-84wSr4jv30biui7endhobYhXBQzQE4c/wdoWlFrKcfiwH+ofaPg8fwsM8okX9cOzkkrsAsNdDyH3ou+kiLquwQ==} + '@ipld/dag-cbor@9.2.4': + resolution: {integrity: sha512-GbDWYl2fdJgkYtIJN0HY9oO0o50d1nB4EQb7uYWKUd2ztxCjxiEW3PjwGG0nqUpN1G4Cug6LX8NzbA7fKT+zfA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} - '@ipld/dag-json@10.2.6': - resolution: {integrity: sha512-51yc5azhmkvc9mp2HV/vtJ8SlgFXADp55wAPuuAjQZ+yPurAYuTVddS3ke5vT4sjcd4DbE+DWjsMZGXjFB2cuA==} + '@ipld/dag-json@10.2.5': + resolution: {integrity: sha512-Q4Fr3IBDEN8gkpgNefynJ4U/ZO5Kwr7WSUMBDbZx0c37t0+IwQCTM9yJh8l5L4SRFjm31MuHwniZ/kM+P7GQ3Q==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} '@ipld/dag-json@8.0.11': @@ -548,9 +639,17 @@ packages: resolution: {integrity: sha512-w4PZ2yPqvNmlAir7/2hsCRMqny1EY5jj26iZcSgxREJexmbAc2FI21jp26MqiNdfgAxvkCnf2N/TJI18GaDNwA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} - '@isaacs/cliui@9.0.0': - resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} - engines: {node: '>=18'} + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} @@ -565,23 +664,20 @@ packages: '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} - '@libp2p/crypto@5.1.13': - resolution: {integrity: sha512-8NN9cQP3jDn+p9+QE9ByiEoZ2lemDFf/unTgiKmS3JF93ph240EUVdbCyyEgOMfykzb0okTM4gzvwfx9osJebQ==} + '@libp2p/crypto@5.1.7': + resolution: {integrity: sha512-7DO0piidLEKfCuNfS420BlHG0e2tH7W/zugdsPSiC/1Apa/s1B1dBkaIEgfDkGjrRP4S/8Or86Rtq7zXeEu67g==} - '@libp2p/interface@2.11.0': - resolution: {integrity: sha512-0MUFKoXWHTQW3oWIgSHApmYMUKWO/Y02+7Hpyp+n3z+geD4Xo2Rku2gYWmxcq+Pyjkz6Q9YjDWz3Yb2SoV2E8Q==} + '@libp2p/interface@2.10.5': + resolution: {integrity: sha512-Z52n04Mph/myGdwyExbFi5S/HqrmZ9JOmfLc2v4r2Cik3GRdw98vrGH19PFvvwjLwAjaqsweCtlGaBzAz09YDw==} - '@libp2p/interface@3.1.0': - resolution: {integrity: sha512-RE7/XyvC47fQBe1cHxhMvepYKa5bFCUyFrrpj8PuM0E7JtzxU7F+Du5j4VXbg2yLDcToe0+j8mB7jvwE2AThYw==} + '@libp2p/logger@5.1.21': + resolution: {integrity: sha512-V1TWlZM5BuKkiGQ7En4qOnseVP82JwDIpIfNjceUZz1ArL32A5HXJjLQnJchkZ3VW8PVciJzUos/vP6slhPY6Q==} - '@libp2p/logger@5.2.0': - resolution: {integrity: sha512-OEFS529CnIKfbWEHmuCNESw9q0D0hL8cQ8klQfjIVPur15RcgAEgc1buQ7Y6l0B6tCYg120bp55+e9tGvn8c0g==} + '@libp2p/peer-id@5.1.8': + resolution: {integrity: sha512-pGaM4BwjnXdGtAtd84L4/wuABpsnFYE+AQ+h3GxNFme0IsTaTVKWd1jBBE5YFeKHBHGUOhF3TlHsdjFfjQA7TA==} - '@libp2p/peer-id@5.1.9': - resolution: {integrity: sha512-cVDp7lX187Epmi/zr0Qq2RsEMmueswP9eIxYSFoMcHL/qcvRFhsxOfUGB8361E26s2WJvC9sXZ0oJS9XVueJhQ==} - - '@multiformats/dns@1.0.13': - resolution: {integrity: sha512-yr4bxtA3MbvJ+2461kYIYMsiiZj/FIqKI64hE4SdvWJUdWF9EtZLar38juf20Sf5tguXKFUruluswAO6JsjS2w==} + '@multiformats/dns@1.0.6': + resolution: {integrity: sha512-nt/5UqjMPtyvkG9BQYdJ4GfLK3nMqGpFZOzf4hAmIa0sJh2LlS9YKXZ4FgwBDsaHvzZqR/rUFIywIc7pkHNNuw==} '@multiformats/multiaddr-to-uri@11.0.2': resolution: {integrity: sha512-SiLFD54zeOJ0qMgo9xv1Tl9O5YktDKAVDP4q4hL16mSq4O4sfFNagNADz8eAofxd6TfQUzGQ3TkRRG9IY2uHRg==} @@ -589,15 +685,12 @@ packages: '@multiformats/multiaddr@12.5.1': resolution: {integrity: sha512-+DDlr9LIRUS8KncI1TX/FfUn8F2dl6BIxJgshS/yFQCNB5IAF0OGzcwB39g5NLE22s4qqDePv0Qof6HdpJ/4aQ==} - '@multiformats/multiaddr@13.0.1': - resolution: {integrity: sha512-XToN915cnfr6Lr9EdGWakGJbPT0ghpg/850HvdC+zFX8XvpLZElwa8synCiwa8TuvKNnny6m8j8NVBNCxhIO3g==} - '@noble/curves@1.4.2': resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} - '@noble/curves@2.0.1': - resolution: {integrity: sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==} - engines: {node: '>= 20.19.0'} + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} '@noble/hashes@1.4.0': resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} @@ -607,10 +700,6 @@ packages: resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} - '@noble/hashes@2.0.1': - resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} - engines: {node: '>= 20.19.0'} - '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -635,32 +724,36 @@ packages: resolution: {integrity: sha512-1QlPaHMhOORySCXkQyzjsIsy2GYTilOw3LkjeHkCgsPJQjAT4IclVytJusWktPbYNys9O+O4V23J44yomQvnBQ==} engines: {node: '>=14.0.0'} - '@oclif/core@4.5.5': - resolution: {integrity: sha512-iQzlaJQgPeUXrtrX71OzDwxPikQ7c2FhNd8U8rBB7BCtj2XYfmzBT/Hmbc+g9OKDIG/JkbJT0fXaWMMBrhi+1A==} + '@oclif/core@4.0.34': + resolution: {integrity: sha512-jHww7lIqyifamynDSjDNNjNOwFTQdKYeOSYaxUaoWhqXnRwacZ+pfUN4Y0L9lqSN4MQtlWM9mwnBD7FvlT9kPw==} engines: {node: '>=18.0.0'} - '@oclif/core@4.8.0': - resolution: {integrity: sha512-jteNUQKgJHLHFbbz806aGZqf+RJJ7t4gwF4MYa8fCwCxQ8/klJNWc0MvaJiBebk7Mc+J39mdlsB4XraaCKznFw==} + '@oclif/core@4.3.0': + resolution: {integrity: sha512-lIzHY+JMP6evrS5E/sGijNnwrCoNtGy8703jWXcMuPOYKiFhWoAqnIm1BGgoRgmxczkbSfRsHUL/lwsSgh74Lw==} + engines: {node: '>=18.0.0'} + + '@oclif/core@4.5.2': + resolution: {integrity: sha512-eQcKyrEcDYeZJKu4vUWiu0ii/1Gfev6GF4FsLSgNez5/+aQyAUCjg3ZWlurf491WiYZTXCWyKAxyPWk8DKv2MA==} engines: {node: '>=18.0.0'} '@oclif/plugin-autocomplete@2.3.10': resolution: {integrity: sha512-Ow1AR8WtjzlyCtiWWPgzMyT8SbcDJFr47009riLioHa+MHX2BCDtVn2DVnN/E6b9JlPV5ptQpjefoRSNWBesmg==} engines: {node: '>=12.0.0'} - '@oclif/plugin-autocomplete@3.2.40': - resolution: {integrity: sha512-HCfDuUV3l5F5Wz7SKkaoFb+OMQ5vKul8zvsPNgI0QbZcQuGHmn3svk+392wSfXboyA1gq8kzEmKPAoQK6r6UNw==} + '@oclif/plugin-autocomplete@3.2.34': + resolution: {integrity: sha512-KhbPcNjitAU7jUojMXJ3l7duWVub0L0pEr3r3bLrpJBNuIJhoIJ7p56Ropcb7OMH2xcaz5B8HGq56cTOe1FHEg==} engines: {node: '>=18.0.0'} '@oclif/plugin-not-found@2.4.3': resolution: {integrity: sha512-nIyaR4y692frwh7wIHZ3fb+2L6XEecQwRDIb4zbEam0TvaVmBQWZoColQyWA84ljFBPZ8XWiQyTz+ixSwdRkqg==} engines: {node: '>=12.0.0'} - '@oclif/plugin-not-found@3.2.74': - resolution: {integrity: sha512-6RD/EuIUGxAYR45nMQg+nw+PqwCXUxkR6Eyn+1fvbVjtb9d+60OPwB77LCRUI4zKNI+n0LOFaMniEdSpb+A7kQ==} + '@oclif/plugin-not-found@3.2.65': + resolution: {integrity: sha512-WgP78eBiRsQYxRIkEui/eyR0l3a2w6LdGMoZTg3DvFwKqZ2X542oUfUmTSqvb19LxdS4uaQ+Mwp4DTVHw5lk/A==} engines: {node: '>=18.0.0'} - '@oclif/plugin-warn-if-update-available@3.1.55': - resolution: {integrity: sha512-VIEBoaoMOCjl3y+w/kdfZMODi0mVMnDuM0vkBf3nqeidhRXVXq87hBqYDdRwN1XoD+eDfE8tBbOP7qtSOONztQ==} + '@oclif/plugin-warn-if-update-available@3.1.46': + resolution: {integrity: sha512-YDlr//SHmC80eZrt+0wNFWSo1cOSU60RoWdhSkAoPB3pUGPSNHZDquXDpo7KniinzYPsj1rfetCYk7UVXwYu7A==} engines: {node: '>=18.0.0'} '@peculiar/asn1-schema@2.4.0': @@ -674,8 +767,8 @@ packages: resolution: {integrity: sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg==} engines: {node: '>=10.12.0'} - '@pinax/graph-networks-registry@0.7.1': - resolution: {integrity: sha512-Gn2kXRiEd5COAaMY/aDCRO0V+zfb1uQKCu5HFPoWka+EsZW27AlTINA7JctYYYEMuCbjMia5FBOzskjgEvj6LA==} + '@pinax/graph-networks-registry@0.6.7': + resolution: {integrity: sha512-xogeCEZ50XRMxpBwE3TZjJ8RCO8Guv39gDRrrKtlpDEDEMLm0MzD3A0SQObgj7aF7qTZNRTWzsuvQdxgzw25wQ==} '@pnpm/config.env-replace@1.1.0': resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} @@ -685,8 +778,8 @@ packages: resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} engines: {node: '>=12.22.0'} - '@pnpm/npm-conf@3.0.2': - resolution: {integrity: sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==} + '@pnpm/npm-conf@2.3.1': + resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} engines: {node: '>=12'} '@protobufjs/aspromise@1.1.2': @@ -755,6 +848,9 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/dns-packet@5.6.5': + resolution: {integrity: sha512-qXOC7XLOEe43ehtWJCMnQXvgcIpv6rPmQ1jXT98Ad8A3TB1Ue50jsCbSSSyuazScEuZ/Q026vHbrOTVkmwA+7Q==} + '@types/form-data@0.0.33': resolution: {integrity: sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==} @@ -798,8 +894,8 @@ packages: '@whatwg-node/events@0.0.3': resolution: {integrity: sha512-IqnKIDWfXBJkvy/k6tzskWTc2NK3LcqHlb+KHGCrjOCH4jfQckRX0NAiIcC/vIqQkzLYw2r2CTSwAxcrtcD6lA==} - '@whatwg-node/fetch@0.10.13': - resolution: {integrity: sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q==} + '@whatwg-node/fetch@0.10.10': + resolution: {integrity: sha512-watz4i/Vv4HpoJ+GranJ7HH75Pf+OkPQ63NoVmru6Srgc8VezTArB00i/oQlnn0KWh14gM42F22Qcc9SU9mo/w==} engines: {node: '>=18.0.0'} '@whatwg-node/fetch@0.8.8': @@ -808,8 +904,8 @@ packages: '@whatwg-node/node-fetch@0.3.6': resolution: {integrity: sha512-w9wKgDO4C95qnXZRwZTfCmLWqyRnooGjcIwG0wADWjw9/HN0p7dtvtgSvItZtUyNteEvgTrd8QojNEqV6DAGTA==} - '@whatwg-node/node-fetch@0.8.5': - resolution: {integrity: sha512-4xzCl/zphPqlp9tASLVeUhB5+WJHbuWGYpfoC2q1qh5dw0AqZBW7L27V5roxYWijPxj4sspRAAoOH3d2ztaHUQ==} + '@whatwg-node/node-fetch@0.7.25': + resolution: {integrity: sha512-szCTESNJV+Xd56zU6ShOi/JWROxE9IwCic8o5D9z5QECZloas6Ez5tUuKqXTAdu6fHFx1t6C+5gwj8smzOLjtg==} engines: {node: '>=18.0.0'} '@whatwg-node/promise-helpers@1.3.2': @@ -868,6 +964,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.2.0: + resolution: {integrity: sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==} + engines: {node: '>=12'} + ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -876,6 +976,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + ansicolors@0.3.2: resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==} @@ -889,8 +993,8 @@ packages: any-signal@3.0.1: resolution: {integrity: sha512-xgZgJtKEa9YmDqXodIgl7Fl1C8yNXr8w6gXjqK3LW4GcEiYT+6AQfJSE/8SPsEpLLmcvbv8YU+qet94UewHxqg==} - any-signal@4.2.0: - resolution: {integrity: sha512-LndMvYuAPf4rC195lk7oSFuHOYFpOszIYrNYv0gHAvz+aEhE9qPZLhmrIz5pXP2BSsPOXvsuHDXEGaiQhIh9wA==} + any-signal@4.1.1: + resolution: {integrity: sha512-iADenERppdC+A2YKbOXXB2WUeABLaM6qnpZ70kZbPZ1cZMMJ7eF+3CaYm+/PhBizgkzlvssC7QuHS30oOiQYWA==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} anymatch@3.1.3: @@ -965,13 +1069,12 @@ packages: axios@0.21.4: resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} + axios@0.26.1: + resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - balanced-match@4.0.2: - resolution: {integrity: sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==} - engines: {node: 20 || >=22} - base-x@3.0.11: resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} @@ -989,6 +1092,11 @@ packages: resolution: {integrity: sha512-v7ms6N/H7iciuk6QInon3/n2mu7oRX+6knJ9xFPsJ3rQePgAqcR3CRTwUheFd8SLbiq4LL7Z4G/44L9zscdt9A==} engines: {node: '>=10'} + 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. + binaryen@101.0.0-nightly.20210723: resolution: {integrity: sha512-eioJNqhHlkguVSbblHOtLqlhtC882SOEPKmNFZaDuz1hzQjolxZ+eu3/kaS10n3sGPONsIZsO7R9fR00UyhEUA==} hasBin: true @@ -1024,10 +1132,6 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - brace-expansion@5.0.2: - resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==} - engines: {node: 20 || >=22} - braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -1056,9 +1160,6 @@ packages: buffer-alloc@1.2.0: resolution: {integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==} - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - buffer-fill@1.0.0: resolution: {integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==} @@ -1068,9 +1169,6 @@ packages: buffer-xor@1.0.3: resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -1113,8 +1211,8 @@ packages: resolution: {integrity: sha512-b3tFPA9pUr2zCUiCfRd2+wok2/LBSNUMKOuRRok+WlvvAgEt/PlbgPTsZUcwCOs53IJvLgTp0eotwtosE6njug==} hasBin: true - cborg@4.5.8: - resolution: {integrity: sha512-6/viltD51JklRhq4L7jC3zgy6gryuG5xfZ3kzpE+PravtyeQLeQmCYLREhQH7pWENg5pY4Yu/XCd6a7dKScVlw==} + cborg@4.2.13: + resolution: {integrity: sha512-HAiZCITe/5Av0ukt6rOYE+VjnuFGfujN3NUKgEbIlONpRpsYMZAa+Bjk16mj6dQMuB0n81AuNrcB9YVMshcrfA==} hasBin: true chalk@2.4.2: @@ -1129,13 +1227,17 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chardet@2.1.1: - resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + chardet@2.1.0: + resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} + chokidar@4.0.1: + resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==} + engines: {node: '>= 14.16.0'} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -1271,8 +1373,8 @@ packages: supports-color: optional: true - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1280,8 +1382,8 @@ packages: supports-color: optional: true - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1289,32 +1391,12 @@ packages: supports-color: optional: true - decompress-tar@4.1.1: - resolution: {integrity: sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==} - engines: {node: '>=4'} - - decompress-tarbz2@4.1.1: - resolution: {integrity: sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==} - engines: {node: '>=4'} - - decompress-targz@4.1.1: - resolution: {integrity: sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==} - engines: {node: '>=4'} - - decompress-unzip@4.0.1: - resolution: {integrity: sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==} - engines: {node: '>=4'} - - decompress@4.2.1: - resolution: {integrity: sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==} - engines: {node: '>=4'} - - default-browser-id@5.0.1: - resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + default-browser-id@5.0.0: + resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} engines: {node: '>=18'} - default-browser@5.5.0: - resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} + default-browser@5.2.1: + resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} engines: {node: '>=18'} defaults@1.0.4: @@ -1324,6 +1406,10 @@ packages: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} @@ -1347,12 +1433,20 @@ packages: dns-over-http-resolver@1.2.3: resolution: {integrity: sha512-miDiVSI6KSNbi4SVifzO/reD8rMnxgrlnkrlkugOLQpWQTe2qMdHsZp5DmfKjxNE+/T3VAAYLQUZMv9SMr6+AA==} + dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} + engines: {node: '>=6'} + docker-compose@0.23.19: resolution: {integrity: sha512-v5vNLIdUqwj4my80wxFDkNH+4S85zsRuH29SO7dCWVWPCMt/ohZBsGN6g6KXWifT0pzQ7uOxqEKCYCDPJ8Vz4g==} engines: {node: '>= 6.0.0'} - docker-compose@1.3.0: - resolution: {integrity: sha512-7Gevk/5eGD50+eMD+XDnFnOrruFkL0kSd7jEG4cjmqweDSUhB7i0g8is/nBdVpl+Bx338SqIB2GLKm32M+Vs6g==} + docker-compose@1.1.0: + resolution: {integrity: sha512-VrkQJNafPQ5d6bGULW0P6KqcxSkv3ZU5Wn2wQA19oB71o7+55vQ9ogFe2MMeNbK+jc9rrKVy280DnHO5JLMWOQ==} + engines: {node: '>= 6.0.0'} + + docker-compose@1.2.0: + resolution: {integrity: sha512-wIU1eHk3Op7dFgELRdmOYlPYS4gP8HhH1ZmZa13QZF59y0fblzFDFmKPhyc05phCy2hze9OEvNZAsoljrs+72w==} engines: {node: '>= 6.0.0'} docker-modem@1.0.9: @@ -1367,6 +1461,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ecc-jsbn@0.1.2: resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} @@ -1395,6 +1492,9 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} @@ -1467,8 +1567,8 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} - eventemitter3@5.0.4: - resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} evp_bytestokey@1.0.3: resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} @@ -1520,9 +1620,6 @@ packages: fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1532,18 +1629,6 @@ packages: picomatch: optional: true - file-type@3.9.0: - resolution: {integrity: sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==} - engines: {node: '>=0.10.0'} - - file-type@5.2.0: - resolution: {integrity: sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==} - engines: {node: '>=4'} - - file-type@6.2.0: - resolution: {integrity: sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==} - engines: {node: '>=4'} - filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} @@ -1582,8 +1667,12 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs-extra@11.3.2: - resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + + fs-extra@11.3.0: + resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} fs-extra@9.1.0: @@ -1608,10 +1697,6 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - generator-function@2.0.1: - resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} - engines: {node: '>= 0.4'} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -1631,10 +1716,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@2.3.1: - resolution: {integrity: sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==} - engines: {node: '>=0.10.0'} - get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -1646,20 +1727,23 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - glob@11.0.3: - resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} + glob@11.0.0: + resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} + engines: {node: 20 || >=22} + hasBin: true + + glob@11.0.2: + resolution: {integrity: sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==} 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@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - 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 + deprecated: Glob versions prior to v9 are no longer supported glob@9.3.5: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} 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 globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} @@ -1669,6 +1753,10 @@ packages: resolution: {integrity: sha512-Cwx/8S8Z4YQg07a6AFsaGnnnmd8mN17414NcPS3OoDtZRwxgsvwRNJNg69niD6fDa8oNwslCG0xH7rEpRNNE/g==} hasBin: true + gluegun@5.1.6: + resolution: {integrity: sha512-9zbi4EQWIVvSOftJWquWzr9gLX2kaDgPkNR5dYWbM53eVvCI3iKuxLlnKoHC0v4uPoq+Kr/+F569tjoFbA4DSA==} + hasBin: true + gluegun@5.2.0: resolution: {integrity: sha512-jSUM5xUy2ztYFQANne17OUm/oAd7qSX7EBksS9bQDt9UvLPqcEkeWUebmaposb8Tx7eTTD8uJVWGRe6PYSsYkg==} hasBin: true @@ -1696,6 +1784,10 @@ packages: resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + graphql@16.9.0: + resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + har-schema@2.0.0: resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} engines: {node: '>=4'} @@ -1771,10 +1863,6 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.2: - resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} - engines: {node: '>=0.10.0'} - ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -1785,8 +1873,11 @@ packages: immutable@4.2.1: resolution: {integrity: sha512-7WYV7Q5BTs0nlQm7tl92rDYYoyELLKHoDMBKhrxEoiV4mrfVdRz8hzPiYOzH7yWjzoVEamxRuAqhxL2PLRwZYQ==} - immutable@5.1.4: - resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==} + immutable@5.0.3: + resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==} + + immutable@5.1.2: + resolution: {integrity: sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -1882,8 +1973,8 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-function@1.1.2: - resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + is-generator-function@1.1.0: + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} engines: {node: '>= 0.4'} is-glob@4.0.3: @@ -1907,9 +1998,6 @@ packages: resolution: {integrity: sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==} engines: {node: '>=8'} - is-natural-number@4.0.1: - resolution: {integrity: sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -1926,10 +2014,6 @@ packages: resolution: {integrity: sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==} engines: {node: '>=0.10.0'} - is-stream@1.1.0: - resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} - engines: {node: '>=0.10.0'} - is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -1945,8 +2029,8 @@ packages: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} - is-wsl@3.1.1: - resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} engines: {node: '>=16'} isarray@0.0.1: @@ -2018,8 +2102,8 @@ packages: it-to-stream@1.0.0: resolution: {integrity: sha512-pLULMZMAB/+vbdvbZtebC0nWBTbG581lk6w8P7DfIIIKUfa8FbY7Oi0FxZcFPbxvISs7A9E+cMpLDBc1XhpAOA==} - jackspeak@4.2.3: - resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} engines: {node: 20 || >=22} jake@10.9.4: @@ -2032,6 +2116,11 @@ packages: engines: {node: '>=8'} hasBin: true + jayson@4.1.3: + resolution: {integrity: sha512-LtXh5aYZodBZ9Fc3j6f2w+MTNcnxteMOrb+QgIouguGOulWi0lieEkOUg+HkjjFs0DGoWDds6bi4E9hpNFLulQ==} + engines: {node: '>=8'} + hasBin: true + jayson@4.2.0: resolution: {integrity: sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg==} engines: {node: '>=8'} @@ -2084,8 +2173,8 @@ packages: resolution: {integrity: sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==} engines: {node: '>=10.0.0'} - kubo-rpc-client@5.4.1: - resolution: {integrity: sha512-v86bQWtyA//pXTrt9y4iEwjW6pt1gA18Z1famWXIR/HN5TFdYwQ3yHOlRE6JSWBDQ0rR6FOMyrrGy8To78mXow==} + kubo-rpc-client@5.2.0: + resolution: {integrity: sha512-J3ppL1xf7f27NDI9jUPGkr1QiExXLyxUTUwHUMMB1a4AZR4s6113SVXPHRYwe1pFIO3hRb5G+0SuHaxYSfhzBA==} lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} @@ -2139,8 +2228,8 @@ packages: lodash.upperfirst@4.3.1: resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} log-symbols@3.0.0: resolution: {integrity: sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==} @@ -2155,8 +2244,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.2.6: - resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} + lru-cache@11.1.0: + resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} engines: {node: 20 || >=22} lru-cache@6.0.0: @@ -2166,10 +2255,6 @@ packages: main-event@1.0.1: resolution: {integrity: sha512-NWtdGrAca/69fm6DIVd8T9rtfDII4Q8NQbIbsKQq2VzS9eqOGYs8uaNQjcuaCq/d9H/o625aOTJX2Qoxzqw0Pw==} - make-dir@1.3.0: - resolution: {integrity: sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==} - engines: {node: '>=4'} - make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -2213,8 +2298,8 @@ packages: minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - minimatch@10.2.1: - resolution: {integrity: sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==} + minimatch@10.0.3: + resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} engines: {node: 20 || >=22} minimatch@3.1.2: @@ -2270,9 +2355,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - ms@3.0.0-canary.202508261828: - resolution: {integrity: sha512-NotsCoUCIUkojWCzQff4ttdCfIPoA1UGZsyQbi7KmqkNRfKCrvga8JJi2PknHymHOuor0cJSn/ylj52Cbt2IrQ==} - engines: {node: '>=18'} + ms@3.0.0-canary.1: + resolution: {integrity: sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==} + engines: {node: '>=12.13'} multiaddr-to-uri@8.0.0: resolution: {integrity: sha512-dq4p/vsOOUdVEd1J1gl+R2GFrXJQH8yjLtz4hodqdVbieg39LvBOdMQRdQnfbg5LSM/q1BYNVf5CBbwZFFqBgA==} @@ -2285,8 +2370,8 @@ packages: multiformats@13.1.3: resolution: {integrity: sha512-CZPi9lFZCM/+7oRolWYsvalsyWQGFo+GpdaTmjxXXomC+nP/W1Rnxb9sUgjvmNmRZ5bOPqRAl4nuK+Ydw/4tGw==} - multiformats@13.4.2: - resolution: {integrity: sha512-eh6eHCrRi1+POZ3dA+Dq1C6jhP1GNtr9CRINMb67OKzqW9I5DUuZM/3jLPlzhgpGeiNUlEGEbkCYChXMCc/8DQ==} + multiformats@13.4.0: + resolution: {integrity: sha512-Mkb/QcclrJxKC+vrcIFl297h52QcKh2Az/9A5vbWytbQt4225UWWWmIuSsKksdww9NkIeYcA7DkfftyLuC/JSg==} multiformats@9.9.0: resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} @@ -2304,8 +2389,8 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@5.1.6: - resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} + nanoid@5.1.5: + resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} engines: {node: ^18 || >=20} hasBin: true @@ -2380,10 +2465,18 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - open@10.2.0: - resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + open@10.1.0: + resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} engines: {node: '>=18'} + open@10.1.2: + resolution: {integrity: sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==} + engines: {node: '>=18'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + ora@4.0.2: resolution: {integrity: sha512-YUOZbamht5mfLxPmk4M35CD/5DuOkAacxlEUbStVXpBAt4fyhBf+vZHI/HRkI++QUp3sNoeA2Gw4C+hi4eGSig==} engines: {node: '>=8'} @@ -2399,13 +2492,13 @@ packages: p-fifo@1.0.0: resolution: {integrity: sha512-IjoCxXW48tqdtDFz6fqo5q1UfFVjjVZe8TC1QRflvNUJtNfCUhxOUw6MOVZhDPjqhSzc26xKdugsO17gmzd5+A==} - p-queue@9.1.0: - resolution: {integrity: sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==} - engines: {node: '>=20'} + p-queue@8.1.0: + resolution: {integrity: sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==} + engines: {node: '>=18'} - p-timeout@7.0.1: - resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} - engines: {node: '>=20'} + p-timeout@6.1.4: + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -2420,8 +2513,8 @@ packages: parse-duration@1.1.2: resolution: {integrity: sha512-p8EIONG8L0u7f8GFgfVlL4n8rnChTt8O5FSxgxMz2tjc9FMP199wxVKVB6IbKx11uTbKHACSvaLVIKNnoeNR/A==} - parse-duration@2.1.5: - resolution: {integrity: sha512-/IX1KRw6zHDOOJrgIz++gvFASbFl7nc8GEXaLdD7d1t1x/GnrK6hh5Fgk8G3RLpkIEi4tsGj9pupGLWNg0EiJA==} + parse-duration@2.1.4: + resolution: {integrity: sha512-b98m6MsCh+akxfyoz9w9dt0AlH2dfYLOBss5SdDsr9pkhKNvkWBXU/r8A4ahmIGByBOLV2+4YwfCuFxbDDaGyg==} parse-json@4.0.0: resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} @@ -2446,8 +2539,8 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-scurry@2.0.1: - resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} engines: {node: 20 || >=22} path-type@4.0.0: @@ -2458,9 +2551,6 @@ packages: resolution: {integrity: sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==} engines: {node: '>=0.12'} - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} @@ -2475,22 +2565,6 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} - pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} - - pify@3.0.0: - resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} - engines: {node: '>=4'} - - pinkie-promise@2.0.1: - resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} - engines: {node: '>=0.10.0'} - - pinkie@2.0.4: - resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} - engines: {node: '>=0.10.0'} - pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -2504,8 +2578,18 @@ packages: engines: {node: '>=4'} hasBin: true - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.0.3: + resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} + engines: {node: '>=14'} + hasBin: true + + prettier@3.4.2: + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} + engines: {node: '>=14'} + hasBin: true + + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} engines: {node: '>=14'} hasBin: true @@ -2515,10 +2599,6 @@ packages: progress-events@1.0.1: resolution: {integrity: sha512-MOzLIwhpt64KIVN64h1MwdKWiyKFNc/S6BoYKPIVUHFg0/eIEyBulhWCgn678v/4c0ri3FdGuzXymNCv02MUIw==} - progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - promise@8.3.0: resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==} @@ -2593,8 +2673,8 @@ packages: redeyed@2.1.1: resolution: {integrity: sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==} - registry-auth-token@5.1.1: - resolution: {integrity: sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==} + registry-auth-token@5.1.0: + resolution: {integrity: sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==} engines: {node: '>=14'} request@2.88.2: @@ -2637,8 +2717,8 @@ packages: resolution: {integrity: sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==} hasBin: true - run-applescript@7.1.0: - resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + run-applescript@7.0.0: + resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} engines: {node: '>=18'} run-parallel@1.2.0: @@ -2664,10 +2744,6 @@ packages: resolution: {integrity: sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==} engines: {node: '>=18.0.0'} - seek-bzip@1.0.6: - resolution: {integrity: sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==} - hasBin: true - semver@7.3.5: resolution: {integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==} engines: {node: '>=10'} @@ -2683,13 +2759,8 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} - engines: {node: '>=10'} - hasBin: true - - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} hasBin: true @@ -2782,6 +2853,10 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + string_decoder@0.10.31: resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} @@ -2799,8 +2874,9 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-dirs@2.1.0: - resolution: {integrity: sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==} + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} @@ -2810,10 +2886,6 @@ packages: resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==} engines: {node: '>=6.5.0', npm: '>=3'} - supports-color@10.2.2: - resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} - engines: {node: '>=18'} - supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -2826,6 +2898,10 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} + supports-color@9.4.0: + resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==} + engines: {node: '>=12'} + supports-hyperlinks@2.3.0: resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} engines: {node: '>=8'} @@ -2858,8 +2934,8 @@ packages: timeout-abort-controller@2.0.0: resolution: {integrity: sha512-2FAPXfzTPYEgw27bQGTHc0SzrbmnU2eso4qo172zMLZzaGqeu09PFa5B2FCUHM1tflgRqPgn5KQgp6+Vex4uNA==} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} tmp-promise@3.0.3: @@ -2935,14 +3011,15 @@ packages: uint8arrays@5.1.0: resolution: {integrity: sha512-vA6nFepEmlSKkMBnLBaUMVvAC4G3CTmO58C12y4sq6WPDOR7mOFYOi7GlrQ4djeSbP6JG9Pv9tJDM97PedRSww==} - unbzip2-stream@1.4.3: - resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} - undici-types@7.10.0: resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} - undici@7.16.0: - resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} + undici@7.1.1: + resolution: {integrity: sha512-WZkQ6eH9f5ZT93gaIffsbUaDpBwjbpvmMbfaEhOnbdUneurTESeRxwPGwjI28mRFESH3W3e8Togijh37ptOQqA==} + engines: {node: '>=20.18.1'} + + undici@7.9.0: + resolution: {integrity: sha512-e696y354tf5cFZPXsF26Yg+5M63+5H3oE6Vtkh2oqbvsE2Oe7s2nIbcQh5lmG7Lp/eS29vJtTpw9+p6PX0qNSg==} engines: {node: '>=20.18.1'} universalify@2.0.1: @@ -2962,9 +3039,6 @@ packages: resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} engines: {node: '>=6.14.2'} - utf8-codec@1.0.0: - resolution: {integrity: sha512-S/QSLezp3qvG4ld5PUfXiH7mCFxLKjSVZRFkB3DOjgwHuJPFDkInAXc/anf7BAbHt/D38ozDzL+QMZ6/7gsI6w==} - utf8@3.0.0: resolution: {integrity: sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==} @@ -2996,8 +3070,8 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - weald@1.1.1: - resolution: {integrity: sha512-PaEQShzMCz8J/AD2N3dJMc1hTZWkJeLKS2NMeiVkV5KDHwgZe7qXLEzyodsT/SODxWDdXJJqocuwf3kHzcXhSQ==} + weald@1.0.4: + resolution: {integrity: sha512-+kYTuHonJBwmFhP1Z4YQK/dGi3jAnJGCYhyODFpHK73rbxnp9lnZQj7a2m+WVgn8fXr5bJaxUpF6l8qZpPeNWQ==} web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} @@ -3068,6 +3142,10 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -3083,10 +3161,6 @@ packages: utf-8-validate: optional: true - wsl-utils@0.1.0: - resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} - engines: {node: '>=18'} - xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -3098,8 +3172,13 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} - yaml@2.8.1: - resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + yaml@2.6.1: + resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} + engines: {node: '>= 14'} + hasBin: true + + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} engines: {node: '>= 14.6'} hasBin: true @@ -3107,15 +3186,12 @@ packages: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} - yoctocolors-cjs@2.1.3: - resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} engines: {node: '>=18'} zod@3.25.76: @@ -3141,11 +3217,6 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@dnsquery/dns-packet@6.1.1': - dependencies: - '@leichtgewicht/ip-codec': 2.0.5 - utf8-codec: 1.0.0 - '@ethersproject/abi@5.0.7': dependencies: '@ethersproject/address': 5.8.0 @@ -3287,7 +3358,7 @@ snapshots: binary-install-raw: 0.0.13(debug@4.3.4) chalk: 3.0.0 chokidar: 3.5.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 docker-compose: 0.23.19 dockerode: 2.5.8 fs-extra: 9.1.0 @@ -3326,7 +3397,7 @@ snapshots: binary-install-raw: 0.0.13(debug@4.3.4) chalk: 3.0.0 chokidar: 3.5.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 docker-compose: 0.23.19 dockerode: 2.5.8 fs-extra: 9.1.0 @@ -3367,7 +3438,7 @@ snapshots: binary-install-raw: 0.0.13(debug@4.3.4) chalk: 3.0.0 chokidar: 3.5.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 docker-compose: 0.23.19 dockerode: 2.5.8 fs-extra: 9.1.0 @@ -3397,194 +3468,390 @@ snapshots: - typescript - utf-8-validate - '@graphprotocol/graph-cli@0.98.1(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@graphprotocol/graph-cli@0.61.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10)': dependencies: '@float-capital/float-subgraph-uncrashable': 0.0.0-internal-testing.5 - '@oclif/core': 4.5.5 - '@oclif/plugin-autocomplete': 3.2.40 - '@oclif/plugin-not-found': 3.2.74(@types/node@24.3.0) - '@oclif/plugin-warn-if-update-available': 3.1.55 - '@pinax/graph-networks-registry': 0.7.1 - '@whatwg-node/fetch': 0.10.13 + '@oclif/core': 2.8.6(@types/node@24.3.0)(typescript@5.9.2) + '@oclif/plugin-autocomplete': 2.3.10(@types/node@24.3.0)(typescript@5.9.2) + '@oclif/plugin-not-found': 2.4.3(@types/node@24.3.0)(typescript@5.9.2) + '@whatwg-node/fetch': 0.8.8 assemblyscript: 0.19.23 - chokidar: 4.0.3 - debug: 4.4.3(supports-color@8.1.1) - decompress: 4.2.1 - docker-compose: 1.3.0 - fs-extra: 11.3.2 - glob: 11.0.3 - gluegun: 5.2.0(debug@4.4.3) - graphql: 16.11.0 - immutable: 5.1.4 - jayson: 4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - js-yaml: 4.1.0 - kubo-rpc-client: 5.4.1(undici@7.16.0) - open: 10.2.0 - prettier: 3.6.2 - progress: 2.0.3 - semver: 7.7.3 + binary-install-raw: 0.0.13(debug@4.3.4) + chalk: 3.0.0 + chokidar: 3.5.3 + debug: 4.3.4 + docker-compose: 0.23.19 + dockerode: 2.5.8 + fs-extra: 9.1.0 + glob: 9.3.5 + gluegun: 5.1.2(debug@4.3.4) + graphql: 15.5.0 + immutable: 4.2.1 + ipfs-http-client: 55.0.0(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13)) + jayson: 4.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + js-yaml: 3.14.1 + prettier: 1.19.1 + request: 2.88.2 + semver: 7.4.0 + sync-request: 6.1.0 tmp-promise: 3.0.3 - undici: 7.16.0 - web3-eth-abi: 4.4.1(typescript@5.9.2)(zod@3.25.76) - yaml: 2.8.1 + web3-eth-abi: 1.7.0 + which: 2.0.2 + yaml: 1.10.2 transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' - '@types/node' - bufferutil + - encoding + - node-fetch - supports-color - typescript - utf-8-validate - - zod - - '@graphprotocol/graph-ts@0.30.0': - dependencies: - assemblyscript: 0.19.10 - - '@graphprotocol/graph-ts@0.31.0': - dependencies: - assemblyscript: 0.19.10 - - '@graphprotocol/graph-ts@0.33.0': - dependencies: - assemblyscript: 0.19.10 - - '@graphprotocol/graph-ts@0.34.0': - dependencies: - assemblyscript: 0.19.10 - - '@graphprotocol/graph-ts@0.35.0': - dependencies: - assemblyscript: 0.19.10 - - '@graphprotocol/graph-ts@0.36.0-alpha-20240422133139-8761ea3': - dependencies: - assemblyscript: 0.19.10 - - '@graphprotocol/graph-ts@0.36.0-alpha-20241129215038-b75cda9': - dependencies: - assemblyscript: 0.19.10 - - '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@24.3.0)': + '@graphprotocol/graph-cli@0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.3.0) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.3.0) - yoctocolors-cjs: 2.1.3 + '@float-capital/float-subgraph-uncrashable': 0.0.0-internal-testing.5 + '@oclif/core': 2.8.6(@types/node@24.3.0)(typescript@5.9.2) + '@oclif/plugin-autocomplete': 2.3.10(@types/node@24.3.0)(typescript@5.9.2) + '@oclif/plugin-not-found': 2.4.3(@types/node@24.3.0)(typescript@5.9.2) + '@whatwg-node/fetch': 0.8.8 + assemblyscript: 0.19.23 + binary-install-raw: 0.0.13(debug@4.3.4) + chalk: 3.0.0 + chokidar: 3.5.3 + debug: 4.3.4 + docker-compose: 0.23.19 + dockerode: 2.5.8 + fs-extra: 9.1.0 + glob: 9.3.5 + gluegun: 5.1.6(debug@4.3.4) + graphql: 15.5.0 + immutable: 4.2.1 + ipfs-http-client: 55.0.0(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13)) + jayson: 4.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + js-yaml: 3.14.1 + prettier: 3.0.3 + semver: 7.4.0 + sync-request: 6.1.0 + tmp-promise: 3.0.3 + web3-eth-abi: 1.7.0 + which: 2.0.2 + yaml: 1.10.2 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - bufferutil + - encoding + - node-fetch + - supports-color + - typescript + - utf-8-validate + + '@graphprotocol/graph-cli@0.71.0-alpha-20240419180731-51ea29d(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10)': + dependencies: + '@float-capital/float-subgraph-uncrashable': 0.0.0-internal-testing.5 + '@oclif/core': 2.8.6(@types/node@24.3.0)(typescript@5.9.2) + '@oclif/plugin-autocomplete': 2.3.10(@types/node@24.3.0)(typescript@5.9.2) + '@oclif/plugin-not-found': 2.4.3(@types/node@24.3.0)(typescript@5.9.2) + '@whatwg-node/fetch': 0.8.8 + assemblyscript: 0.19.23 + binary-install-raw: 0.0.13(debug@4.3.4) + chalk: 3.0.0 + chokidar: 3.5.3 + debug: 4.3.4 + docker-compose: 0.23.19 + dockerode: 2.5.8 + fs-extra: 9.1.0 + glob: 9.3.5 + gluegun: 5.1.6(debug@4.3.4) + graphql: 15.5.0 + immutable: 4.2.1 + ipfs-http-client: 55.0.0(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13)) + jayson: 4.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + js-yaml: 3.14.1 + prettier: 3.0.3 + semver: 7.4.0 + sync-request: 6.1.0 + tmp-promise: 3.0.3 + web3-eth-abi: 1.7.0 + which: 2.0.2 + yaml: 1.10.2 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - bufferutil + - encoding + - node-fetch + - supports-color + - typescript + - utf-8-validate + + '@graphprotocol/graph-cli@0.91.0-alpha-20241129215038-b75cda9(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10)': + dependencies: + '@float-capital/float-subgraph-uncrashable': 0.0.0-internal-testing.5 + '@oclif/core': 2.8.6(@types/node@24.3.0)(typescript@5.9.2) + '@oclif/plugin-autocomplete': 2.3.10(@types/node@24.3.0)(typescript@5.9.2) + '@oclif/plugin-not-found': 2.4.3(@types/node@24.3.0)(typescript@5.9.2) + '@oclif/plugin-warn-if-update-available': 3.1.46 + '@whatwg-node/fetch': 0.8.8 + assemblyscript: 0.19.23 + binary-install-raw: 0.0.13(debug@4.3.4) + chalk: 3.0.0 + chokidar: 3.5.3 + debug: 4.3.4 + docker-compose: 0.23.19 + dockerode: 2.5.8 + fs-extra: 9.1.0 + glob: 9.3.5 + gluegun: 5.1.6(debug@4.3.4) + graphql: 15.5.0 + immutable: 4.2.1 + ipfs-http-client: 55.0.0(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13)) + jayson: 4.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + js-yaml: 3.14.1 + open: 8.4.2 + prettier: 3.0.3 + semver: 7.4.0 + sync-request: 6.1.0 + tmp-promise: 3.0.3 + web3-eth-abi: 1.7.0 + which: 2.0.2 + yaml: 1.10.2 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - bufferutil + - encoding + - node-fetch + - supports-color + - typescript + - utf-8-validate + + '@graphprotocol/graph-cli@0.93.4-alpha-20250105163501-f401d0c57c4ba1f1af95a928d447efd63a56ecdc(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@float-capital/float-subgraph-uncrashable': 0.0.0-internal-testing.5 + '@oclif/core': 4.0.34 + '@oclif/plugin-autocomplete': 3.2.34 + '@oclif/plugin-not-found': 3.2.65(@types/node@24.3.0) + '@oclif/plugin-warn-if-update-available': 3.1.46 + '@pinax/graph-networks-registry': 0.6.7 + '@whatwg-node/fetch': 0.10.10 + assemblyscript: 0.19.23 + binary-install: 1.1.2(debug@4.3.7) + chokidar: 4.0.1 + debug: 4.3.7(supports-color@8.1.1) + docker-compose: 1.1.0 + fs-extra: 11.2.0 + glob: 11.0.0 + gluegun: 5.2.0(debug@4.3.7) + graphql: 16.9.0 + immutable: 5.0.3 + jayson: 4.1.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + js-yaml: 4.1.0 + kubo-rpc-client: 5.2.0(undici@7.1.1) + open: 10.1.0 + prettier: 3.4.2 + semver: 7.6.3 + tmp-promise: 3.0.3 + undici: 7.1.1 + web3-eth-abi: 4.4.1(typescript@5.9.2)(zod@3.25.76) + yaml: 2.6.1 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - supports-color + - typescript + - utf-8-validate + - zod + + '@graphprotocol/graph-cli@0.97.1(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@float-capital/float-subgraph-uncrashable': 0.0.0-internal-testing.5 + '@oclif/core': 4.3.0 + '@oclif/plugin-autocomplete': 3.2.34 + '@oclif/plugin-not-found': 3.2.65(@types/node@24.3.0) + '@oclif/plugin-warn-if-update-available': 3.1.46 + '@pinax/graph-networks-registry': 0.6.7 + '@whatwg-node/fetch': 0.10.10 + assemblyscript: 0.19.23 + chokidar: 4.0.3 + debug: 4.4.1(supports-color@8.1.1) + docker-compose: 1.2.0 + fs-extra: 11.3.0 + glob: 11.0.2 + gluegun: 5.2.0(debug@4.4.1) + graphql: 16.11.0 + immutable: 5.1.2 + jayson: 4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + js-yaml: 4.1.0 + kubo-rpc-client: 5.2.0(undici@7.9.0) + open: 10.1.2 + prettier: 3.5.3 + semver: 7.7.2 + tmp-promise: 3.0.3 + undici: 7.9.0 + web3-eth-abi: 4.4.1(typescript@5.9.2)(zod@3.25.76) + yaml: 2.8.0 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - supports-color + - typescript + - utf-8-validate + - zod + + '@graphprotocol/graph-ts@0.30.0': + dependencies: + assemblyscript: 0.19.10 + + '@graphprotocol/graph-ts@0.31.0': + dependencies: + assemblyscript: 0.19.10 + + '@graphprotocol/graph-ts@0.33.0': + dependencies: + assemblyscript: 0.19.10 + + '@graphprotocol/graph-ts@0.34.0': + dependencies: + assemblyscript: 0.19.10 + + '@graphprotocol/graph-ts@0.35.0': + dependencies: + assemblyscript: 0.19.10 + + '@graphprotocol/graph-ts@0.36.0-alpha-20240422133139-8761ea3': + dependencies: + assemblyscript: 0.19.10 + + '@graphprotocol/graph-ts@0.36.0-alpha-20241129215038-b75cda9': + dependencies: + assemblyscript: 0.19.10 + + '@inquirer/checkbox@4.2.1(@types/node@24.3.0)': + dependencies: + '@inquirer/core': 10.1.15(@types/node@24.3.0) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.3.0) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 optionalDependencies: '@types/node': 24.3.0 - '@inquirer/confirm@5.1.21(@types/node@24.3.0)': + '@inquirer/confirm@5.1.15(@types/node@24.3.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.3.0) - '@inquirer/type': 3.0.10(@types/node@24.3.0) + '@inquirer/core': 10.1.15(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) optionalDependencies: '@types/node': 24.3.0 - '@inquirer/core@10.3.2(@types/node@24.3.0)': + '@inquirer/core@10.1.15(@types/node@24.3.0)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.3.0) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.3.0) + ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.3 + yoctocolors-cjs: 2.1.2 optionalDependencies: '@types/node': 24.3.0 - '@inquirer/editor@4.2.23(@types/node@24.3.0)': + '@inquirer/editor@4.2.17(@types/node@24.3.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.3.0) - '@inquirer/external-editor': 1.0.3(@types/node@24.3.0) - '@inquirer/type': 3.0.10(@types/node@24.3.0) + '@inquirer/core': 10.1.15(@types/node@24.3.0) + '@inquirer/external-editor': 1.0.1(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) optionalDependencies: '@types/node': 24.3.0 - '@inquirer/expand@4.0.23(@types/node@24.3.0)': + '@inquirer/expand@4.0.17(@types/node@24.3.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.3.0) - '@inquirer/type': 3.0.10(@types/node@24.3.0) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 10.1.15(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) + yoctocolors-cjs: 2.1.2 optionalDependencies: '@types/node': 24.3.0 - '@inquirer/external-editor@1.0.3(@types/node@24.3.0)': + '@inquirer/external-editor@1.0.1(@types/node@24.3.0)': dependencies: - chardet: 2.1.1 - iconv-lite: 0.7.2 + chardet: 2.1.0 + iconv-lite: 0.6.3 optionalDependencies: '@types/node': 24.3.0 - '@inquirer/figures@1.0.15': {} + '@inquirer/figures@1.0.13': {} - '@inquirer/input@4.3.1(@types/node@24.3.0)': + '@inquirer/input@4.2.1(@types/node@24.3.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.3.0) - '@inquirer/type': 3.0.10(@types/node@24.3.0) + '@inquirer/core': 10.1.15(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) optionalDependencies: '@types/node': 24.3.0 - '@inquirer/number@3.0.23(@types/node@24.3.0)': + '@inquirer/number@3.0.17(@types/node@24.3.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.3.0) - '@inquirer/type': 3.0.10(@types/node@24.3.0) + '@inquirer/core': 10.1.15(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) optionalDependencies: '@types/node': 24.3.0 - '@inquirer/password@4.0.23(@types/node@24.3.0)': + '@inquirer/password@4.0.17(@types/node@24.3.0)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.3.0) - '@inquirer/type': 3.0.10(@types/node@24.3.0) + '@inquirer/core': 10.1.15(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) + ansi-escapes: 4.3.2 optionalDependencies: '@types/node': 24.3.0 - '@inquirer/prompts@7.10.1(@types/node@24.3.0)': - dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@24.3.0) - '@inquirer/confirm': 5.1.21(@types/node@24.3.0) - '@inquirer/editor': 4.2.23(@types/node@24.3.0) - '@inquirer/expand': 4.0.23(@types/node@24.3.0) - '@inquirer/input': 4.3.1(@types/node@24.3.0) - '@inquirer/number': 3.0.23(@types/node@24.3.0) - '@inquirer/password': 4.0.23(@types/node@24.3.0) - '@inquirer/rawlist': 4.1.11(@types/node@24.3.0) - '@inquirer/search': 3.2.2(@types/node@24.3.0) - '@inquirer/select': 4.4.2(@types/node@24.3.0) + '@inquirer/prompts@7.8.3(@types/node@24.3.0)': + dependencies: + '@inquirer/checkbox': 4.2.1(@types/node@24.3.0) + '@inquirer/confirm': 5.1.15(@types/node@24.3.0) + '@inquirer/editor': 4.2.17(@types/node@24.3.0) + '@inquirer/expand': 4.0.17(@types/node@24.3.0) + '@inquirer/input': 4.2.1(@types/node@24.3.0) + '@inquirer/number': 3.0.17(@types/node@24.3.0) + '@inquirer/password': 4.0.17(@types/node@24.3.0) + '@inquirer/rawlist': 4.1.5(@types/node@24.3.0) + '@inquirer/search': 3.1.0(@types/node@24.3.0) + '@inquirer/select': 4.3.1(@types/node@24.3.0) optionalDependencies: '@types/node': 24.3.0 - '@inquirer/rawlist@4.1.11(@types/node@24.3.0)': + '@inquirer/rawlist@4.1.5(@types/node@24.3.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.3.0) - '@inquirer/type': 3.0.10(@types/node@24.3.0) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 10.1.15(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) + yoctocolors-cjs: 2.1.2 optionalDependencies: '@types/node': 24.3.0 - '@inquirer/search@3.2.2(@types/node@24.3.0)': + '@inquirer/search@3.1.0(@types/node@24.3.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@24.3.0) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.3.0) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 10.1.15(@types/node@24.3.0) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.3.0) + yoctocolors-cjs: 2.1.2 optionalDependencies: '@types/node': 24.3.0 - '@inquirer/select@4.4.2(@types/node@24.3.0)': + '@inquirer/select@4.3.1(@types/node@24.3.0)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@24.3.0) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@24.3.0) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 10.1.15(@types/node@24.3.0) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.3.0) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 optionalDependencies: '@types/node': 24.3.0 - '@inquirer/type@3.0.10(@types/node@24.3.0)': + '@inquirer/type@3.0.8(@types/node@24.3.0)': optionalDependencies: '@types/node': 24.3.0 @@ -3593,15 +3860,15 @@ snapshots: cborg: 1.10.2 multiformats: 9.9.0 - '@ipld/dag-cbor@9.2.5': + '@ipld/dag-cbor@9.2.4': dependencies: - cborg: 4.5.8 - multiformats: 13.4.2 + cborg: 4.2.13 + multiformats: 13.4.0 - '@ipld/dag-json@10.2.6': + '@ipld/dag-json@10.2.5': dependencies: - cborg: 4.5.8 - multiformats: 13.4.2 + cborg: 4.2.13 + multiformats: 13.4.0 '@ipld/dag-json@8.0.11': dependencies: @@ -3614,9 +3881,22 @@ snapshots: '@ipld/dag-pb@4.1.5': dependencies: - multiformats: 13.4.2 + multiformats: 13.4.0 + + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 - '@isaacs/cliui@9.0.0': {} + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 '@jridgewell/resolve-uri@3.1.2': {} @@ -3629,57 +3909,49 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} - '@libp2p/crypto@5.1.13': + '@libp2p/crypto@5.1.7': dependencies: - '@libp2p/interface': 3.1.0 - '@noble/curves': 2.0.1 - '@noble/hashes': 2.0.1 - multiformats: 13.4.2 + '@libp2p/interface': 2.10.5 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + multiformats: 13.4.0 protons-runtime: 5.6.0 uint8arraylist: 2.4.8 uint8arrays: 5.1.0 - '@libp2p/interface@2.11.0': + '@libp2p/interface@2.10.5': dependencies: - '@multiformats/dns': 1.0.13 + '@multiformats/dns': 1.0.6 '@multiformats/multiaddr': 12.5.1 it-pushable: 3.2.3 it-stream-types: 2.0.2 main-event: 1.0.1 - multiformats: 13.4.2 + multiformats: 13.4.0 progress-events: 1.0.1 uint8arraylist: 2.4.8 - '@libp2p/interface@3.1.0': + '@libp2p/logger@5.1.21': dependencies: - '@multiformats/dns': 1.0.13 - '@multiformats/multiaddr': 13.0.1 - main-event: 1.0.1 - multiformats: 13.4.2 - progress-events: 1.0.1 - uint8arraylist: 2.4.8 - - '@libp2p/logger@5.2.0': - dependencies: - '@libp2p/interface': 2.11.0 + '@libp2p/interface': 2.10.5 '@multiformats/multiaddr': 12.5.1 interface-datastore: 8.3.2 - multiformats: 13.4.2 - weald: 1.1.1 + multiformats: 13.4.0 + weald: 1.0.4 - '@libp2p/peer-id@5.1.9': + '@libp2p/peer-id@5.1.8': dependencies: - '@libp2p/crypto': 5.1.13 - '@libp2p/interface': 2.11.0 - multiformats: 13.4.2 + '@libp2p/crypto': 5.1.7 + '@libp2p/interface': 2.10.5 + multiformats: 13.4.0 uint8arrays: 5.1.0 - '@multiformats/dns@1.0.13': + '@multiformats/dns@1.0.6': dependencies: - '@dnsquery/dns-packet': 6.1.1 - '@libp2p/interface': 3.1.0 + '@types/dns-packet': 5.6.5 + buffer: 6.0.3 + dns-packet: 5.6.1 hashlru: 2.3.0 - p-queue: 9.1.0 + p-queue: 8.1.0 progress-events: 1.0.1 uint8arrays: 5.1.0 @@ -3691,16 +3963,9 @@ snapshots: dependencies: '@chainsafe/is-ip': 2.1.0 '@chainsafe/netmask': 2.0.0 - '@multiformats/dns': 1.0.13 + '@multiformats/dns': 1.0.6 abort-error: 1.0.1 - multiformats: 13.4.2 - uint8-varint: 2.0.4 - uint8arrays: 5.1.0 - - '@multiformats/multiaddr@13.0.1': - dependencies: - '@chainsafe/is-ip': 2.1.0 - multiformats: 13.4.2 + multiformats: 13.4.0 uint8-varint: 2.0.4 uint8arrays: 5.1.0 @@ -3708,16 +3973,14 @@ snapshots: dependencies: '@noble/hashes': 1.4.0 - '@noble/curves@2.0.1': + '@noble/curves@1.9.7': dependencies: - '@noble/hashes': 2.0.1 + '@noble/hashes': 1.8.0 '@noble/hashes@1.4.0': {} '@noble/hashes@1.8.0': {} - '@noble/hashes@2.0.1': {} - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -3775,7 +4038,7 @@ snapshots: chalk: 4.1.2 clean-stack: 3.0.1 cli-progress: 3.12.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) ejs: 3.1.10 fs-extra: 9.1.0 get-package-type: 0.1.0 @@ -3787,7 +4050,7 @@ snapshots: natural-orderby: 2.0.3 object-treeify: 1.1.33 password-prompt: 1.1.3 - semver: 7.4.0 + semver: 7.7.2 string-width: 4.2.3 strip-ansi: 6.0.1 supports-color: 8.1.1 @@ -3824,7 +4087,7 @@ snapshots: natural-orderby: 2.0.3 object-treeify: 1.1.33 password-prompt: 1.1.3 - semver: 7.6.3 + semver: 7.7.2 string-width: 4.2.3 strip-ansi: 6.0.1 supports-color: 8.1.1 @@ -3840,44 +4103,65 @@ snapshots: - '@types/node' - typescript - '@oclif/core@4.5.5': + '@oclif/core@4.0.34': dependencies: ansi-escapes: 4.3.2 ansis: 3.17.0 clean-stack: 3.0.1 cli-spinners: 2.9.2 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) ejs: 3.1.10 get-package-type: 0.1.0 + globby: 11.1.0 indent-string: 4.0.0 is-wsl: 2.2.0 lilconfig: 3.1.3 minimatch: 9.0.5 - semver: 7.7.4 + semver: 7.6.3 string-width: 4.2.3 supports-color: 8.1.1 - tinyglobby: 0.2.15 widest-line: 3.1.0 wordwrap: 1.0.0 wrap-ansi: 7.0.0 - '@oclif/core@4.8.0': + '@oclif/core@4.3.0': dependencies: ansi-escapes: 4.3.2 ansis: 3.17.0 clean-stack: 3.0.1 cli-spinners: 2.9.2 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) ejs: 3.1.10 get-package-type: 0.1.0 + globby: 11.1.0 indent-string: 4.0.0 is-wsl: 2.2.0 lilconfig: 3.1.3 minimatch: 9.0.5 - semver: 7.7.4 + semver: 7.7.2 string-width: 4.2.3 supports-color: 8.1.1 - tinyglobby: 0.2.15 + widest-line: 3.1.0 + wordwrap: 1.0.0 + wrap-ansi: 7.0.0 + + '@oclif/core@4.5.2': + dependencies: + ansi-escapes: 4.3.2 + ansis: 3.17.0 + clean-stack: 3.0.1 + cli-spinners: 2.9.2 + debug: 4.4.1(supports-color@8.1.1) + ejs: 3.1.10 + get-package-type: 0.1.0 + indent-string: 4.0.0 + is-wsl: 2.2.0 + lilconfig: 3.1.3 + minimatch: 9.0.5 + semver: 7.7.2 + string-width: 4.2.3 + supports-color: 8.1.1 + tinyglobby: 0.2.14 widest-line: 3.1.0 wordwrap: 1.0.0 wrap-ansi: 7.0.0 @@ -3894,11 +4178,11 @@ snapshots: - supports-color - typescript - '@oclif/plugin-autocomplete@3.2.40': + '@oclif/plugin-autocomplete@3.2.34': dependencies: - '@oclif/core': 4.8.0 + '@oclif/core': 4.0.34 ansis: 3.17.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) ejs: 3.1.10 transitivePeerDependencies: - supports-color @@ -3914,23 +4198,23 @@ snapshots: - '@types/node' - typescript - '@oclif/plugin-not-found@3.2.74(@types/node@24.3.0)': + '@oclif/plugin-not-found@3.2.65(@types/node@24.3.0)': dependencies: - '@inquirer/prompts': 7.10.1(@types/node@24.3.0) - '@oclif/core': 4.8.0 + '@inquirer/prompts': 7.8.3(@types/node@24.3.0) + '@oclif/core': 4.5.2 ansis: 3.17.0 fast-levenshtein: 3.0.0 transitivePeerDependencies: - '@types/node' - '@oclif/plugin-warn-if-update-available@3.1.55': + '@oclif/plugin-warn-if-update-available@3.1.46': dependencies: - '@oclif/core': 4.8.0 + '@oclif/core': 4.0.34 ansis: 3.17.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) http-call: 5.3.0 - lodash: 4.17.23 - registry-auth-token: 5.1.1 + lodash: 4.17.21 + registry-auth-token: 5.1.0 transitivePeerDependencies: - supports-color @@ -3952,7 +4236,7 @@ snapshots: tslib: 2.8.1 webcrypto-core: 1.8.1 - '@pinax/graph-networks-registry@0.7.1': {} + '@pinax/graph-networks-registry@0.6.7': {} '@pnpm/config.env-replace@1.1.0': {} @@ -3960,7 +4244,7 @@ snapshots: dependencies: graceful-fs: 4.2.10 - '@pnpm/npm-conf@3.0.2': + '@pnpm/npm-conf@2.3.1': dependencies: '@pnpm/config.env-replace': 1.1.0 '@pnpm/network.ca-file': 1.0.2 @@ -4025,6 +4309,10 @@ snapshots: '@types/node': 24.3.0 '@types/connect@3.4.38': + dependencies: + '@types/node': 12.20.55 + + '@types/dns-packet@5.6.5': dependencies: '@types/node': 24.3.0 @@ -4060,7 +4348,7 @@ snapshots: '@types/ws@7.4.7': dependencies: - '@types/node': 24.3.0 + '@types/node': 12.20.55 '@whatwg-node/disposablestack@0.0.6': dependencies: @@ -4069,9 +4357,9 @@ snapshots: '@whatwg-node/events@0.0.3': {} - '@whatwg-node/fetch@0.10.13': + '@whatwg-node/fetch@0.10.10': dependencies: - '@whatwg-node/node-fetch': 0.8.5 + '@whatwg-node/node-fetch': 0.7.25 urlpattern-polyfill: 10.1.0 '@whatwg-node/fetch@0.8.8': @@ -4090,7 +4378,7 @@ snapshots: fast-url-parser: 1.1.3 tslib: 2.8.1 - '@whatwg-node/node-fetch@0.8.5': + '@whatwg-node/node-fetch@0.7.25': dependencies: '@fastify/busboy': 3.2.0 '@whatwg-node/disposablestack': 0.0.6 @@ -4146,6 +4434,8 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.2.0: {} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 @@ -4154,6 +4444,8 @@ snapshots: dependencies: color-convert: 2.0.1 + ansi-styles@6.2.1: {} + ansicolors@0.3.2: {} ansis@3.17.0: {} @@ -4165,7 +4457,7 @@ snapshots: any-signal@3.0.1: {} - any-signal@4.2.0: {} + any-signal@4.1.1: {} anymatch@3.1.3: dependencies: @@ -4178,9 +4470,15 @@ snapshots: transitivePeerDependencies: - debug - apisauce@2.1.6(debug@4.4.3): + apisauce@2.1.6(debug@4.3.7): + dependencies: + axios: 0.21.4(debug@4.3.7) + transitivePeerDependencies: + - debug + + apisauce@2.1.6(debug@4.4.1): dependencies: - axios: 0.21.4(debug@4.4.3) + axios: 0.21.4(debug@4.4.1) transitivePeerDependencies: - debug @@ -4243,17 +4541,25 @@ snapshots: transitivePeerDependencies: - debug - axios@0.21.4(debug@4.4.3): + axios@0.21.4(debug@4.3.7): dependencies: - follow-redirects: 1.15.11(debug@4.4.3) + follow-redirects: 1.15.11(debug@4.3.7) transitivePeerDependencies: - debug - balanced-match@1.0.2: {} + axios@0.21.4(debug@4.4.1): + dependencies: + follow-redirects: 1.15.11(debug@4.4.1) + transitivePeerDependencies: + - debug - balanced-match@4.0.2: + axios@0.26.1(debug@4.3.7): dependencies: - jackspeak: 4.2.3 + follow-redirects: 1.15.11(debug@4.3.7) + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} base-x@3.0.11: dependencies: @@ -4275,6 +4581,14 @@ snapshots: transitivePeerDependencies: - debug + binary-install@1.1.2(debug@4.3.7): + dependencies: + axios: 0.26.1(debug@4.3.7) + rimraf: 3.0.2 + tar: 6.2.1 + transitivePeerDependencies: + - debug + binaryen@101.0.0-nightly.20210723: {} binaryen@102.0.0-nightly.20211028: {} @@ -4309,10 +4623,6 @@ snapshots: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.2: - dependencies: - balanced-match: 4.0.2 - braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -4349,19 +4659,12 @@ snapshots: buffer-alloc-unsafe: 1.1.0 buffer-fill: 1.0.0 - buffer-crc32@0.2.13: {} - buffer-fill@1.0.0: {} buffer-from@1.1.2: {} buffer-xor@1.0.3: {} - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - buffer@6.0.3: dependencies: base64-js: 1.5.1 @@ -4374,7 +4677,7 @@ snapshots: bundle-name@4.1.0: dependencies: - run-applescript: 7.1.0 + run-applescript: 7.0.0 busboy@1.6.0: dependencies: @@ -4408,7 +4711,7 @@ snapshots: cborg@1.10.2: {} - cborg@4.5.8: {} + cborg@4.2.13: {} chalk@2.4.2: dependencies: @@ -4426,7 +4729,7 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - chardet@2.1.1: {} + chardet@2.1.0: {} chokidar@3.5.3: dependencies: @@ -4440,6 +4743,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + chokidar@4.0.1: + dependencies: + readdirp: 4.1.2 + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -4566,7 +4873,7 @@ snapshots: dag-jose@5.1.1: dependencies: - '@ipld/dag-cbor': 9.2.5 + '@ipld/dag-cbor': 9.2.4 multiformats: 13.1.3 dashdash@1.14.1: @@ -4577,68 +4884,28 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.3.4(supports-color@8.1.1): + debug@4.3.4: dependencies: ms: 2.1.2 - optionalDependencies: - supports-color: 8.1.1 - debug@4.4.1(supports-color@8.1.1): + debug@4.3.7(supports-color@8.1.1): dependencies: ms: 2.1.3 optionalDependencies: supports-color: 8.1.1 - debug@4.4.3(supports-color@8.1.1): + debug@4.4.1(supports-color@8.1.1): dependencies: ms: 2.1.3 optionalDependencies: supports-color: 8.1.1 - decompress-tar@4.1.1: - dependencies: - file-type: 5.2.0 - is-stream: 1.1.0 - tar-stream: 1.6.2 - - decompress-tarbz2@4.1.1: - dependencies: - decompress-tar: 4.1.1 - file-type: 6.2.0 - is-stream: 1.1.0 - seek-bzip: 1.0.6 - unbzip2-stream: 1.4.3 - - decompress-targz@4.1.1: - dependencies: - decompress-tar: 4.1.1 - file-type: 5.2.0 - is-stream: 1.1.0 - - decompress-unzip@4.0.1: - dependencies: - file-type: 3.9.0 - get-stream: 2.3.1 - pify: 2.3.0 - yauzl: 2.10.0 + default-browser-id@5.0.0: {} - decompress@4.2.1: - dependencies: - decompress-tar: 4.1.1 - decompress-tarbz2: 4.1.1 - decompress-targz: 4.1.1 - decompress-unzip: 4.0.1 - graceful-fs: 4.2.11 - make-dir: 1.3.0 - pify: 2.3.0 - strip-dirs: 2.1.0 - - default-browser-id@5.0.1: {} - - default-browser@5.5.0: + default-browser@5.2.1: dependencies: bundle-name: 4.1.0 - default-browser-id: 5.0.1 + default-browser-id: 5.0.0 defaults@1.0.4: dependencies: @@ -4650,6 +4917,8 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + define-lazy-prop@2.0.0: {} + define-lazy-prop@3.0.0: {} delay@5.0.0: {} @@ -4671,13 +4940,21 @@ snapshots: - node-fetch - supports-color + dns-packet@5.6.1: + dependencies: + '@leichtgewicht/ip-codec': 2.0.5 + docker-compose@0.23.19: dependencies: yaml: 1.10.2 - docker-compose@1.3.0: + docker-compose@1.1.0: + dependencies: + yaml: 2.6.1 + + docker-compose@1.2.0: dependencies: - yaml: 2.8.1 + yaml: 2.8.0 docker-modem@1.0.9: dependencies: @@ -4702,6 +4979,8 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + ecc-jsbn@0.1.2: dependencies: jsbn: 0.1.1 @@ -4735,6 +5014,8 @@ snapshots: emoji-regex@8.0.0: {} + emoji-regex@9.2.2: {} + encoding@0.1.13: dependencies: iconv-lite: 0.6.3 @@ -4824,7 +5105,7 @@ snapshots: event-target-shim@5.0.1: {} - eventemitter3@5.0.4: {} + eventemitter3@5.0.1: {} evp_bytestokey@1.0.3: dependencies: @@ -4883,20 +5164,10 @@ snapshots: dependencies: reusify: 1.1.0 - fd-slicer@1.1.0: - dependencies: - pend: 1.2.0 - fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 - file-type@3.9.0: {} - - file-type@5.2.0: {} - - file-type@6.2.0: {} - filelist@1.0.4: dependencies: minimatch: 5.1.6 @@ -4907,11 +5178,15 @@ snapshots: follow-redirects@1.15.11(debug@4.3.4): optionalDependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 - follow-redirects@1.15.11(debug@4.4.3): + follow-redirects@1.15.11(debug@4.3.7): optionalDependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) + + follow-redirects@1.15.11(debug@4.4.1): + optionalDependencies: + debug: 4.4.1(supports-color@8.1.1) for-each@0.3.5: dependencies: @@ -4941,7 +5216,13 @@ snapshots: fs-constants@1.0.0: {} - fs-extra@11.3.2: + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.2.0 @@ -4970,8 +5251,6 @@ snapshots: function-bind@1.1.2: {} - generator-function@2.0.1: {} - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4996,11 +5275,6 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stream@2.3.1: - dependencies: - object-assign: 4.1.1 - pinkie-promise: 2.0.1 - get-stream@6.0.1: {} getpass@0.1.7: @@ -5011,14 +5285,23 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@11.0.3: + glob@11.0.0: dependencies: foreground-child: 3.3.1 - jackspeak: 4.2.3 - minimatch: 10.2.1 + jackspeak: 4.1.1 + minimatch: 10.0.3 minipass: 7.1.2 package-json-from-dist: 1.0.1 - path-scurry: 2.0.1 + path-scurry: 2.0.0 + + glob@11.0.2: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.1 + minimatch: 10.0.3 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 glob@7.2.3: dependencies: @@ -5080,9 +5363,79 @@ snapshots: transitivePeerDependencies: - debug - gluegun@5.2.0(debug@4.4.3): + gluegun@5.1.6(debug@4.3.4): + dependencies: + apisauce: 2.1.6(debug@4.3.4) + app-module-path: 2.2.0 + cli-table3: 0.6.0 + colors: 1.4.0 + cosmiconfig: 7.0.1 + cross-spawn: 7.0.3 + ejs: 3.1.8 + enquirer: 2.3.6 + execa: 5.1.1 + fs-jetpack: 4.3.1 + lodash.camelcase: 4.3.0 + lodash.kebabcase: 4.1.1 + lodash.lowercase: 4.3.0 + lodash.lowerfirst: 4.3.1 + lodash.pad: 4.5.1 + lodash.padend: 4.6.1 + lodash.padstart: 4.6.1 + lodash.repeat: 4.1.0 + lodash.snakecase: 4.1.1 + lodash.startcase: 4.4.0 + lodash.trim: 4.5.1 + lodash.trimend: 4.5.1 + lodash.trimstart: 4.5.1 + lodash.uppercase: 4.3.0 + lodash.upperfirst: 4.3.1 + ora: 4.0.2 + pluralize: 8.0.0 + semver: 7.3.5 + which: 2.0.2 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - debug + + gluegun@5.2.0(debug@4.3.7): + dependencies: + apisauce: 2.1.6(debug@4.3.7) + app-module-path: 2.2.0 + cli-table3: 0.6.0 + colors: 1.4.0 + cosmiconfig: 7.0.1 + cross-spawn: 7.0.3 + ejs: 3.1.8 + enquirer: 2.3.6 + execa: 5.1.1 + fs-jetpack: 4.3.1 + lodash.camelcase: 4.3.0 + lodash.kebabcase: 4.1.1 + lodash.lowercase: 4.3.0 + lodash.lowerfirst: 4.3.1 + lodash.pad: 4.5.1 + lodash.padend: 4.6.1 + lodash.padstart: 4.6.1 + lodash.repeat: 4.1.0 + lodash.snakecase: 4.1.1 + lodash.startcase: 4.4.0 + lodash.trim: 4.5.1 + lodash.trimend: 4.5.1 + lodash.trimstart: 4.5.1 + lodash.uppercase: 4.3.0 + lodash.upperfirst: 4.3.1 + ora: 4.0.2 + pluralize: 8.0.0 + semver: 7.3.5 + which: 2.0.2 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - debug + + gluegun@5.2.0(debug@4.4.1): dependencies: - apisauce: 2.1.6(debug@4.4.3) + apisauce: 2.1.6(debug@4.4.1) app-module-path: 2.2.0 cli-table3: 0.6.0 colors: 1.4.0 @@ -5129,6 +5482,8 @@ snapshots: graphql@16.11.0: {} + graphql@16.9.0: {} + har-schema@2.0.0: {} har-validator@5.1.5: @@ -5187,7 +5542,7 @@ snapshots: http-call@5.3.0: dependencies: content-type: 1.0.5 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) is-retry-allowed: 1.2.0 is-stream: 2.0.1 parse-json: 4.0.0 @@ -5213,17 +5568,15 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.2: - dependencies: - safer-buffer: 2.1.2 - ieee754@1.2.1: {} ignore@5.3.2: {} immutable@4.2.1: {} - immutable@5.1.4: {} + immutable@5.0.3: {} + + immutable@5.1.2: {} import-fresh@3.3.1: dependencies: @@ -5374,10 +5727,9 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.1.2: + is-generator-function@1.1.0: dependencies: call-bound: 1.0.4 - generator-function: 2.0.1 get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 @@ -5398,8 +5750,6 @@ snapshots: dependencies: ip-regex: 4.3.0 - is-natural-number@4.0.1: {} - is-number@7.0.0: {} is-plain-obj@2.1.0: {} @@ -5413,8 +5763,6 @@ snapshots: is-retry-allowed@1.2.0: {} - is-stream@1.1.0: {} - is-stream@2.0.1: {} is-typed-array@1.1.15: @@ -5427,7 +5775,7 @@ snapshots: dependencies: is-docker: 2.2.1 - is-wsl@3.1.1: + is-wsl@3.1.0: dependencies: is-inside-container: 1.0.0 @@ -5493,9 +5841,9 @@ snapshots: p-fifo: 1.0.0 readable-stream: 3.6.2 - jackspeak@4.2.3: + jackspeak@4.1.1: dependencies: - '@isaacs/cliui': 9.0.0 + '@isaacs/cliui': 8.0.2 jake@10.9.4: dependencies: @@ -5521,6 +5869,24 @@ snapshots: - bufferutil - utf-8-validate + jayson@4.1.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + '@types/connect': 3.4.38 + '@types/node': 12.20.55 + '@types/ws': 7.4.7 + JSONStream: 1.3.5 + commander: 2.20.3 + delay: 5.0.0 + es6-promisify: 5.0.0 + eyes: 0.1.8 + isomorphic-ws: 4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + json-stringify-safe: 5.0.1 + uuid: 8.3.2 + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + jayson@4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@types/connect': 3.4.38 @@ -5585,18 +5951,18 @@ snapshots: node-gyp-build: 4.8.4 readable-stream: 3.6.2 - kubo-rpc-client@5.4.1(undici@7.16.0): + kubo-rpc-client@5.2.0(undici@7.1.1): dependencies: - '@ipld/dag-cbor': 9.2.5 - '@ipld/dag-json': 10.2.6 + '@ipld/dag-cbor': 9.2.4 + '@ipld/dag-json': 10.2.5 '@ipld/dag-pb': 4.1.5 - '@libp2p/crypto': 5.1.13 - '@libp2p/interface': 2.11.0 - '@libp2p/logger': 5.2.0 - '@libp2p/peer-id': 5.1.9 + '@libp2p/crypto': 5.1.7 + '@libp2p/interface': 2.10.5 + '@libp2p/logger': 5.1.21 + '@libp2p/peer-id': 5.1.8 '@multiformats/multiaddr': 12.5.1 '@multiformats/multiaddr-to-uri': 11.0.2 - any-signal: 4.2.0 + any-signal: 4.1.1 blob-to-it: 2.0.10 browser-readablestream-to-it: 2.0.10 dag-jose: 5.1.1 @@ -5612,10 +5978,48 @@ snapshots: it-peekable: 3.0.8 it-to-stream: 1.0.0 merge-options: 3.0.4 - multiformats: 13.4.2 - nanoid: 5.1.6 - native-fetch: 4.0.2(undici@7.16.0) - parse-duration: 2.1.5 + multiformats: 13.4.0 + nanoid: 5.1.5 + native-fetch: 4.0.2(undici@7.1.1) + parse-duration: 2.1.4 + react-native-fetch-api: 3.0.0 + stream-to-it: 1.0.1 + uint8arrays: 5.1.0 + wherearewe: 2.0.1 + transitivePeerDependencies: + - undici + + kubo-rpc-client@5.2.0(undici@7.9.0): + dependencies: + '@ipld/dag-cbor': 9.2.4 + '@ipld/dag-json': 10.2.5 + '@ipld/dag-pb': 4.1.5 + '@libp2p/crypto': 5.1.7 + '@libp2p/interface': 2.10.5 + '@libp2p/logger': 5.1.21 + '@libp2p/peer-id': 5.1.8 + '@multiformats/multiaddr': 12.5.1 + '@multiformats/multiaddr-to-uri': 11.0.2 + any-signal: 4.1.1 + blob-to-it: 2.0.10 + browser-readablestream-to-it: 2.0.10 + dag-jose: 5.1.1 + electron-fetch: 1.9.1 + err-code: 3.0.1 + ipfs-unixfs: 11.2.5 + iso-url: 1.2.1 + it-all: 3.0.9 + it-first: 3.0.9 + it-glob: 3.0.4 + it-last: 3.0.9 + it-map: 3.1.4 + it-peekable: 3.0.8 + it-to-stream: 1.0.0 + merge-options: 3.0.4 + multiformats: 13.4.0 + nanoid: 5.1.5 + native-fetch: 4.0.2(undici@7.9.0) + parse-duration: 2.1.4 react-native-fetch-api: 3.0.0 stream-to-it: 1.0.1 uint8arrays: 5.1.0 @@ -5657,7 +6061,7 @@ snapshots: lodash.upperfirst@4.3.1: {} - lodash@4.17.23: {} + lodash@4.17.21: {} log-symbols@3.0.0: dependencies: @@ -5669,7 +6073,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.2.6: {} + lru-cache@11.1.0: {} lru-cache@6.0.0: dependencies: @@ -5677,10 +6081,6 @@ snapshots: main-event@1.0.1: {} - make-dir@1.3.0: - dependencies: - pify: 3.0.0 - make-error@1.3.6: {} math-intrinsics@1.1.0: {} @@ -5716,9 +6116,9 @@ snapshots: minimalistic-crypto-utils@1.0.1: {} - minimatch@10.2.1: + minimatch@10.0.3: dependencies: - brace-expansion: 5.0.2 + '@isaacs/brace-expansion': 5.0.0 minimatch@3.1.2: dependencies: @@ -5763,7 +6163,7 @@ snapshots: ms@2.1.3: {} - ms@3.0.0-canary.202508261828: {} + ms@3.0.0-canary.1: {} multiaddr-to-uri@8.0.0(node-fetch@2.7.0(encoding@0.1.13)): dependencies: @@ -5786,7 +6186,7 @@ snapshots: multiformats@13.1.3: {} - multiformats@13.4.2: {} + multiformats@13.4.0: {} multiformats@9.9.0: {} @@ -5796,7 +6196,7 @@ snapshots: nanoid@3.3.11: {} - nanoid@5.1.6: {} + nanoid@5.1.5: {} native-abort-controller@1.0.4(abort-controller@3.0.0): dependencies: @@ -5806,9 +6206,13 @@ snapshots: dependencies: node-fetch: 2.7.0(encoding@0.1.13) - native-fetch@4.0.2(undici@7.16.0): + native-fetch@4.0.2(undici@7.1.1): dependencies: - undici: 7.16.0 + undici: 7.1.1 + + native-fetch@4.0.2(undici@7.9.0): + dependencies: + undici: 7.9.0 natural-orderby@2.0.3: {} @@ -5851,12 +6255,25 @@ snapshots: dependencies: mimic-fn: 2.1.0 - open@10.2.0: + open@10.1.0: + dependencies: + default-browser: 5.2.1 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + is-wsl: 3.1.0 + + open@10.1.2: dependencies: - default-browser: 5.5.0 + default-browser: 5.2.1 define-lazy-prop: 3.0.0 is-inside-container: 1.0.0 - wsl-utils: 0.1.0 + is-wsl: 3.1.0 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 ora@4.0.2: dependencies: @@ -5877,12 +6294,12 @@ snapshots: fast-fifo: 1.3.2 p-defer: 3.0.0 - p-queue@9.1.0: + p-queue@8.1.0: dependencies: - eventemitter3: 5.0.4 - p-timeout: 7.0.1 + eventemitter3: 5.0.1 + p-timeout: 6.1.4 - p-timeout@7.0.1: {} + p-timeout@6.1.4: {} package-json-from-dist@1.0.1: {} @@ -5894,7 +6311,7 @@ snapshots: parse-duration@1.1.2: {} - parse-duration@2.1.5: {} + parse-duration@2.1.4: {} parse-json@4.0.0: dependencies: @@ -5922,9 +6339,9 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - path-scurry@2.0.1: + path-scurry@2.0.0: dependencies: - lru-cache: 11.2.6 + lru-cache: 11.1.0 minipass: 7.1.2 path-type@4.0.0: {} @@ -5938,8 +6355,6 @@ snapshots: sha.js: 2.4.12 to-buffer: 1.2.1 - pend@1.2.0: {} - performance-now@2.1.0: {} picocolors@1.1.1: {} @@ -5948,30 +6363,22 @@ snapshots: picomatch@4.0.3: {} - pify@2.3.0: {} - - pify@3.0.0: {} - - pinkie-promise@2.0.1: - dependencies: - pinkie: 2.0.4 - - pinkie@2.0.4: {} - pluralize@8.0.0: {} possible-typed-array-names@1.1.0: {} prettier@1.19.1: {} - prettier@3.6.2: {} + prettier@3.0.3: {} + + prettier@3.4.2: {} + + prettier@3.5.3: {} process-nextick-args@2.0.1: {} progress-events@1.0.1: {} - progress@2.0.3: {} - promise@8.3.0: dependencies: asap: 2.0.6 @@ -6072,9 +6479,9 @@ snapshots: dependencies: esprima: 4.0.1 - registry-auth-token@5.1.1: + registry-auth-token@5.1.0: dependencies: - '@pnpm/npm-conf': 3.0.2 + '@pnpm/npm-conf': 2.3.1 request@2.88.2: dependencies: @@ -6132,7 +6539,7 @@ snapshots: dependencies: bn.js: 5.2.2 - run-applescript@7.1.0: {} + run-applescript@7.0.0: {} run-parallel@1.2.0: dependencies: @@ -6158,10 +6565,6 @@ snapshots: node-addon-api: 5.1.0 node-gyp-build: 4.8.4 - seek-bzip@1.0.6: - dependencies: - commander: 2.20.3 - semver@7.3.5: dependencies: lru-cache: 6.0.0 @@ -6172,9 +6575,7 @@ snapshots: semver@7.6.3: {} - semver@7.7.3: {} - - semver@7.7.4: {} + semver@7.7.2: {} set-function-length@1.2.2: dependencies: @@ -6284,6 +6685,12 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + string_decoder@0.10.31: {} string_decoder@1.1.1: @@ -6302,9 +6709,9 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-dirs@2.1.0: + strip-ansi@7.1.0: dependencies: - is-natural-number: 4.0.1 + ansi-regex: 6.2.0 strip-final-newline@2.0.0: {} @@ -6312,8 +6719,6 @@ snapshots: dependencies: is-hex-prefixed: 1.0.0 - supports-color@10.2.2: {} - supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -6326,6 +6731,8 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-color@9.4.0: {} + supports-hyperlinks@2.3.0: dependencies: has-flag: 4.0.0 @@ -6389,7 +6796,7 @@ snapshots: native-abort-controller: 1.0.4(abort-controller@3.0.0) retimer: 3.0.0 - tinyglobby@0.2.15: + tinyglobby@0.2.14: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 @@ -6470,16 +6877,13 @@ snapshots: uint8arrays@5.1.0: dependencies: - multiformats: 13.4.2 - - unbzip2-stream@1.4.3: - dependencies: - buffer: 5.7.1 - through: 2.3.8 + multiformats: 13.4.0 undici-types@7.10.0: {} - undici@7.16.0: {} + undici@7.1.1: {} + + undici@7.9.0: {} universalify@2.0.1: {} @@ -6496,8 +6900,6 @@ snapshots: node-gyp-build: 4.8.4 optional: true - utf8-codec@1.0.0: {} - utf8@3.0.0: {} util-deprecate@1.0.2: {} @@ -6506,7 +6908,7 @@ snapshots: dependencies: inherits: 2.0.4 is-arguments: 1.2.0 - is-generator-function: 1.1.2 + is-generator-function: 1.1.0 is-typed-array: 1.1.15 which-typed-array: 1.1.19 @@ -6528,10 +6930,10 @@ snapshots: dependencies: defaults: 1.0.4 - weald@1.1.1: + weald@1.0.4: dependencies: - ms: 3.0.0-canary.202508261828 - supports-color: 10.2.2 + ms: 3.0.0-canary.1 + supports-color: 9.4.0 web-streams-polyfill@3.3.3: {} @@ -6570,7 +6972,7 @@ snapshots: web3-utils@4.3.3: dependencies: ethereum-cryptography: 2.2.1 - eventemitter3: 5.0.4 + eventemitter3: 5.0.1 web3-errors: 1.3.1 web3-types: 1.10.0 web3-validator: 2.0.6 @@ -6634,6 +7036,12 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + wrappy@1.0.2: {} ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): @@ -6641,27 +7049,20 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - wsl-utils@0.1.0: - dependencies: - is-wsl: 3.1.1 - xtend@4.0.2: {} yallist@4.0.0: {} yaml@1.10.2: {} - yaml@2.8.1: {} + yaml@2.6.1: {} - yargs-parser@21.1.1: {} + yaml@2.8.0: {} - yauzl@2.10.0: - dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 + yargs-parser@21.1.1: {} yn@3.1.1: {} - yoctocolors-cjs@2.1.3: {} + yoctocolors-cjs@2.1.2: {} zod@3.25.76: {} diff --git a/store/test-store/tests/graphql/introspection.rs b/store/test-store/tests/graphql/introspection.rs index 6a978bccfc5..750c6f34f62 100644 --- a/store/test-store/tests/graphql/introspection.rs +++ b/store/test-store/tests/graphql/introspection.rs @@ -125,6 +125,7 @@ async fn introspection_query(schema: Arc, query: &str) -> QueryResult max_first: u32::MAX, max_skip: u32::MAX, trace: false, + log_store: Arc::new(graph::components::log_store::NoOpLogStore), }; let result = diff --git a/store/test-store/tests/graphql/mock_introspection.json b/store/test-store/tests/graphql/mock_introspection.json index 75e54f5787d..6a01143f911 100644 --- a/store/test-store/tests/graphql/mock_introspection.json +++ b/store/test-store/tests/graphql/mock_introspection.json @@ -198,6 +198,47 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "ENUM", + "name": "LogLevel", + "description": "The severity level of a log entry.\nLog levels are ordered from most to least severe: CRITICAL > ERROR > WARNING > INFO > DEBUG", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "CRITICAL", + "description": "Critical errors that require immediate attention", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ERROR", + "description": "Error conditions that indicate a failure", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "WARNING", + "description": "Warning conditions that may require attention", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "INFO", + "description": "Informational messages about normal operations", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "DEBUG", + "description": "Detailed diagnostic information for debugging", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, { "kind": "INTERFACE", "name": "Node", @@ -743,6 +784,91 @@ }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "_logs", + "description": "Query execution logs emitted by the subgraph during indexing. Results are sorted by timestamp in descending order (newest first).", + "args": [ + { + "name": "level", + "description": "Filter logs by severity level. Only logs at this level will be returned.", + "type": { + "kind": "ENUM", + "name": "LogLevel", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "from", + "description": "Filter logs from this timestamp onwards (inclusive). Must be in RFC3339 format (e.g., '2024-01-15T10:30:00Z').", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "to", + "description": "Filter logs until this timestamp (inclusive). Must be in RFC3339 format (e.g., '2024-01-15T23:59:59Z').", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "search", + "description": "Search for logs containing this text in the message. Case-insensitive substring match. Maximum length: 1000 characters.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "first", + "description": "Maximum number of logs to return. Default: 100, Maximum: 1000.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": "100" + }, + { + "name": "skip", + "description": "Number of logs to skip (for pagination). Default: 0, Maximum: 10000.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": "0" + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "_Log_", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, @@ -1367,6 +1493,239 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "_LogArgument_", + "description": "A key-value pair of additional data associated with a log entry.\nThese correspond to arguments passed to the log function in the subgraph code.", + "fields": [ + { + "name": "key", + "description": "The parameter name", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "value", + "description": "The parameter value, serialized as a string", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "_LogMeta_", + "description": "Source code location metadata for a log entry.\nIndicates where in the subgraph's AssemblyScript code the log statement was executed.", + "fields": [ + { + "name": "module", + "description": "The module or file path where the log was emitted", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "line", + "description": "The line number in the source file", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "column", + "description": "The column number in the source file", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "_Log_", + "description": "A log entry emitted by a subgraph during indexing.\nLogs can be generated by the subgraph's AssemblyScript code using the `log.*` functions.", + "fields": [ + { + "name": "id", + "description": "Unique identifier for this log entry", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "subgraphId", + "description": "The deployment hash of the subgraph that emitted this log", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "timestamp", + "description": "The timestamp when the log was emitted, in RFC3339 format (e.g., '2024-01-15T10:30:00Z')", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "level", + "description": "The severity level of the log entry", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "LogLevel", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "text", + "description": "The log message text", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "arguments", + "description": "Additional structured data passed to the log function as key-value pairs", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "_LogArgument_", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "meta", + "description": "Metadata about the source location in the subgraph code where the log was emitted", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "_LogMeta_", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", "name": "_Meta_", @@ -1454,7 +1813,9 @@ { "name": "language", "description": null, - "locations": ["FIELD_DEFINITION"], + "locations": [ + "FIELD_DEFINITION" + ], "args": [ { "name": "language", @@ -1471,7 +1832,11 @@ { "name": "skip", "description": null, - "locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"], + "locations": [ + "FIELD", + "FRAGMENT_SPREAD", + "INLINE_FRAGMENT" + ], "args": [ { "name": "if", @@ -1492,7 +1857,11 @@ { "name": "include", "description": null, - "locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"], + "locations": [ + "FIELD", + "FRAGMENT_SPREAD", + "INLINE_FRAGMENT" + ], "args": [ { "name": "if", @@ -1513,13 +1882,17 @@ { "name": "entity", "description": "Marks the GraphQL type as indexable entity. Each type that should be an entity is required to be annotated with this directive.", - "locations": ["OBJECT"], + "locations": [ + "OBJECT" + ], "args": [] }, { "name": "subgraphId", "description": "Defined a Subgraph ID for an object type", - "locations": ["OBJECT"], + "locations": [ + "OBJECT" + ], "args": [ { "name": "id", @@ -1540,7 +1913,9 @@ { "name": "derivedFrom", "description": "creates a virtual field on the entity that may be queried but cannot be set manually through the mappings API.", - "locations": ["FIELD_DEFINITION"], + "locations": [ + "FIELD_DEFINITION" + ], "args": [ { "name": "field", diff --git a/store/test-store/tests/graphql/query.rs b/store/test-store/tests/graphql/query.rs index f206fe2644f..9908866610b 100644 --- a/store/test-store/tests/graphql/query.rs +++ b/store/test-store/tests/graphql/query.rs @@ -616,6 +616,7 @@ async fn execute_query_document_with_variables( STORE.clone(), LOAD_MANAGER.clone(), METRICS_REGISTRY.clone(), + Arc::new(graph::components::log_store::NoOpLogStore), )); let target = QueryTarget::Deployment(id.clone(), Default::default()); let query = Query::new(query, variables, false); @@ -726,6 +727,7 @@ where STORE.clone(), LOAD_MANAGER.clone(), METRICS_REGISTRY.clone(), + Arc::new(graph::components::log_store::NoOpLogStore), )); let target = QueryTarget::Deployment(id.clone(), Default::default()); let query = Query::new(query, variables, false); diff --git a/tests/integration-tests/logs-query/abis/Contract.abi b/tests/integration-tests/logs-query/abis/Contract.abi new file mode 100644 index 00000000000..02da1a9e7f3 --- /dev/null +++ b/tests/integration-tests/logs-query/abis/Contract.abi @@ -0,0 +1,33 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint16", + "name": "x", + "type": "uint16" + } + ], + "name": "Trigger", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "x", + "type": "uint16" + } + ], + "name": "emitTrigger", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/tests/integration-tests/logs-query/package.json b/tests/integration-tests/logs-query/package.json new file mode 100644 index 00000000000..c1a68515054 --- /dev/null +++ b/tests/integration-tests/logs-query/package.json @@ -0,0 +1,13 @@ +{ + "name": "logs-query-subgraph", + "version": "0.0.0", + "private": true, + "scripts": { + "codegen": "graph codegen --skip-migrations", + "deploy:test": "graph deploy test/logs-query --version-label v0.0.1 --ipfs $IPFS_URI --node $GRAPH_NODE_ADMIN_URI" + }, + "devDependencies": { + "@graphprotocol/graph-cli": "0.69.0", + "@graphprotocol/graph-ts": "0.34.0" + } +} diff --git a/tests/integration-tests/logs-query/schema.graphql b/tests/integration-tests/logs-query/schema.graphql new file mode 100644 index 00000000000..1459e34353b --- /dev/null +++ b/tests/integration-tests/logs-query/schema.graphql @@ -0,0 +1,4 @@ +type Trigger @entity { + id: ID! + x: Int! +} diff --git a/tests/integration-tests/logs-query/src/mapping.ts b/tests/integration-tests/logs-query/src/mapping.ts new file mode 100644 index 00000000000..8f4d55e6a9f --- /dev/null +++ b/tests/integration-tests/logs-query/src/mapping.ts @@ -0,0 +1,39 @@ +import { Trigger as TriggerEvent } from "../generated/Contract/Contract"; +import { Trigger } from "../generated/schema"; +import { log } from "@graphprotocol/graph-ts"; + +export function handleTrigger(event: TriggerEvent): void { + let entity = new Trigger(event.transaction.hash.toHex()); + entity.x = event.params.x; + entity.save(); + + // Generate various log levels and types for testing + let x = event.params.x as i32; + + if (x == 0) { + log.info("Processing trigger with value zero", []); + } + + if (x == 1) { + log.error("Error processing trigger", []); + } + + if (x == 2) { + log.warning("Warning: unusual trigger value", ["hash", event.transaction.hash.toHexString()]); + } + + if (x == 3) { + log.debug("Debug: trigger details", ["blockNumber", event.block.number.toString()]); + } + + if (x == 4) { + log.info("Handler execution successful", ["entity_id", entity.id]); + } + + if (x == 5) { + log.error("Critical timeout error", []); + } + + // Log for every event to test general log capture + log.info("Trigger event processed", []); +} diff --git a/tests/integration-tests/logs-query/subgraph.yaml b/tests/integration-tests/logs-query/subgraph.yaml new file mode 100644 index 00000000000..be3061d5533 --- /dev/null +++ b/tests/integration-tests/logs-query/subgraph.yaml @@ -0,0 +1,26 @@ +specVersion: 0.0.5 +description: Logs Query Test Subgraph +repository: https://github.com/graphprotocol/graph-node +schema: + file: ./schema.graphql +dataSources: + - kind: ethereum/contract + name: Contract + network: test + source: + address: "0x5FbDB2315678afecb367f032d93F642f64180aa3" + abi: Contract + startBlock: 0 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Trigger + abis: + - name: Contract + file: ./abis/Contract.abi + eventHandlers: + - event: Trigger(uint16) + handler: handleTrigger + file: ./src/mapping.ts diff --git a/tests/src/config.rs b/tests/src/config.rs index fc84c243912..e6d05ef54cf 100644 --- a/tests/src/config.rs +++ b/tests/src/config.rs @@ -140,11 +140,17 @@ impl GraphNodeConfig { let bin = fs::canonicalize("../target/debug/gnd") .expect("failed to infer `graph-node` program location. (Was it built already?)"); + // Allow overriding IPFS port via environment variable + let ipfs_port = std::env::var("IPFS_TEST_PORT") + .ok() + .and_then(|p| p.parse().ok()) + .unwrap_or(3001); + let ipfs_uri = format!("http://localhost:{}", ipfs_port); + Self { bin, ports: GraphNodePorts::default(), - ipfs_uri: std::env::var("GRAPH_NODE_TEST_IPFS_URL") - .unwrap_or_else(|_| "http://127.0.0.1:3001".to_string()), + ipfs_uri, log_file: TestFile::new("integration-tests/graph-node.log"), } } @@ -155,11 +161,17 @@ impl Default for GraphNodeConfig { let bin = fs::canonicalize("../target/debug/graph-node") .expect("failed to infer `graph-node` program location. (Was it built already?)"); + // Allow overriding IPFS port via environment variable + let ipfs_port = std::env::var("IPFS_TEST_PORT") + .ok() + .and_then(|p| p.parse().ok()) + .unwrap_or(3001); + let ipfs_uri = format!("http://localhost:{}", ipfs_port); + Self { bin, ports: GraphNodePorts::default(), - ipfs_uri: std::env::var("GRAPH_NODE_TEST_IPFS_URL") - .unwrap_or_else(|_| "http://127.0.0.1:3001".to_string()), + ipfs_uri, log_file: TestFile::new("integration-tests/graph-node.log"), } } @@ -221,7 +233,9 @@ impl Config { .stderr(stderr) .args(args.clone()) .env("GRAPH_STORE_WRITE_BATCH_DURATION", "5") - .env("ETHEREUM_REORG_THRESHOLD", "0"); + .env("ETHEREUM_REORG_THRESHOLD", "0") + .env("GRAPH_LOG_STORE_BACKEND", "file") + .env("GRAPH_LOG_STORE_FILE_DIR", "/tmp/integration-test-logs"); status!( "graph-node", @@ -290,17 +304,28 @@ impl Default for Config { let num_parallel_tests = std::env::var("N_CONCURRENT_TESTS") .map(|x| x.parse().expect("N_CONCURRENT_TESTS must be a number")) .unwrap_or(1000); + + // Allow overriding ports via environment variables + let postgres_port = std::env::var("POSTGRES_TEST_PORT") + .ok() + .and_then(|p| p.parse().ok()) + .unwrap_or(3011); + let eth_port = std::env::var("ETHEREUM_TEST_PORT") + .ok() + .and_then(|p| p.parse().ok()) + .unwrap_or(3021); + Config { db: DbConfig { host: "localhost".to_string(), - port: 3011, + port: postgres_port, user: "graph-node".to_string(), password: "let-me-in".to_string(), name: "graph-node".to_string(), }, eth: EthConfig { network: "test".to_string(), - port: 3021, + port: eth_port, host: "localhost".to_string(), }, graph_node: GraphNodeConfig::from_env(), diff --git a/tests/src/fixture/mod.rs b/tests/src/fixture/mod.rs index 19b459f2a6c..9fdfc281b3e 100644 --- a/tests/src/fixture/mod.rs +++ b/tests/src/fixture/mod.rs @@ -614,6 +614,7 @@ pub async fn setup_inner( stores.network_store.clone(), Arc::new(load_manager), mock_registry.clone(), + Arc::new(graph::components::log_store::NoOpLogStore), )); let indexing_status_service = Arc::new(IndexNodeService::new( diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index ee1f0f201dc..ea3be84b654 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -1390,6 +1390,150 @@ async fn test_declared_calls_struct_fields(ctx: TestContext) -> anyhow::Result<( Ok(()) } +async fn test_logs_query(ctx: TestContext) -> anyhow::Result<()> { + let subgraph = ctx.subgraph; + assert!(subgraph.healthy); + + // Wait a moment for logs to be written + sleep(Duration::from_secs(2)).await; + + // Test 1: Query all logs + let query = r#"{ + _logs(first: 100) { + id + timestamp + level + text + } + }"# + .to_string(); + let resp = subgraph.query(&query).await?; + + let logs = resp["data"]["_logs"] + .as_array() + .context("Expected _logs to be an array")?; + + // We should have logs from the subgraph (user logs + system logs) + assert!( + !logs.is_empty(), + "Expected to have logs, got none. Response: {:?}", + resp + ); + + // Test 2: Filter by ERROR level + let query = r#"{ + _logs(level: ERROR, first: 100) { + level + text + } + }"# + .to_string(); + let resp = subgraph.query(&query).await?; + let error_logs = resp["data"]["_logs"] + .as_array() + .context("Expected _logs to be an array")?; + + // Check that we have error logs and they're all ERROR level + for log in error_logs { + assert_eq!( + log["level"].as_str(), + Some("ERROR"), + "Expected ERROR level, got: {:?}", + log + ); + } + + // Test 3: Search for specific text + let query = r#"{ + _logs(search: "timeout", first: 100) { + id + text + } + }"# + .to_string(); + let resp = subgraph.query(&query).await?; + let timeout_logs = resp["data"]["_logs"] + .as_array() + .context("Expected _logs to be an array")?; + + // If we have timeout logs, verify they contain the word "timeout" + for log in timeout_logs { + let text = log["text"] + .as_str() + .context("Expected text field to be a string")?; + assert!( + text.to_lowercase().contains("timeout"), + "Expected log to contain 'timeout', got: {}", + text + ); + } + + // Test 4: Pagination + let query = r#"{ + _logs(first: 2, skip: 0) { + id + } + }"# + .to_string(); + let resp = subgraph.query(&query).await?; + let first_page = resp["data"]["_logs"] + .as_array() + .context("Expected _logs to be an array")?; + + let query = r#"{ + _logs(first: 2, skip: 2) { + id + } + }"# + .to_string(); + let resp = subgraph.query(&query).await?; + let second_page = resp["data"]["_logs"] + .as_array() + .context("Expected _logs to be an array")?; + + // If we have enough logs, verify pages are different + if first_page.len() == 2 && !second_page.is_empty() { + let first_ids: Vec<_> = first_page.iter().map(|l| &l["id"]).collect(); + let second_ids: Vec<_> = second_page.iter().map(|l| &l["id"]).collect(); + + // Verify no overlap between pages + for id in &second_ids { + assert!( + !first_ids.contains(id), + "Log ID {:?} appears in both pages", + id + ); + } + } + + // Test 5: Query with arguments field to verify structured logging + let query = r#"{ + _logs(first: 10) { + text + arguments { + key + value + } + } + }"# + .to_string(); + let resp = subgraph.query(&query).await?; + let logs_with_args = resp["data"]["_logs"] + .as_array() + .context("Expected _logs to be an array")?; + + // Verify arguments field is present (even if empty for some logs) + for log in logs_with_args { + assert!( + log.get("arguments").is_some(), + "Expected arguments field to exist in log: {:?}", + log + ); + } + + Ok(()) +} + /// The main test entrypoint. #[graph::test] async fn integration_tests() -> anyhow::Result<()> { @@ -1424,13 +1568,14 @@ async fn integration_tests() -> anyhow::Result<()> { "declared-calls-struct-fields", test_declared_calls_struct_fields, ), + TestCase::new("logs-query", test_logs_query), ]; // Filter the test cases if a specific test name is provided - let cases_to_run: Vec<_> = if let Some(test_name) = test_name_to_run { + let cases_to_run: Vec<_> = if let Some(ref test_name) = test_name_to_run { cases .into_iter() - .filter(|case| case.name == test_name) + .filter(|case| case.name == *test_name) .collect() } else { cases From 8ebc977263617f79c89f9640a8889ae6b661ba36 Mon Sep 17 00:00:00 2001 From: Ford Date: Thu, 15 Jan 2026 13:57:34 -0800 Subject: [PATCH 09/28] docs: Add log store documentation --- README.md | 38 ++ docs/environment-variables.md | 52 +++ docs/log-store.md | 853 ++++++++++++++++++++++++++++++++++ 3 files changed, 943 insertions(+) create mode 100644 docs/log-store.md diff --git a/README.md b/README.md index f5c3416ce29..667c606a045 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,44 @@ Very large `graph-node` instances can also be configured using a the `graph-node` needs to connect to multiple chains or if the work of indexing and querying needs to be split across [multiple databases](./docs/config.md). +#### Log Storage + +`graph-node` supports storing and querying subgraph logs through multiple backends: + +- **File**: Local JSON Lines files (recommended for local development) +- **Elasticsearch**: Enterprise-grade search and analytics (for production) +- **Loki**: Grafana's log aggregation system (for production) +- **Disabled**: No log storage (default) + +**Quick example (file-based logs for local development):** +```bash +mkdir -p ./graph-logs + +cargo run -p graph-node --release -- \ + --postgres-url $POSTGRES_URL \ + --ethereum-rpc mainnet:archive:https://... \ + --ipfs 127.0.0.1:5001 \ + --log-store-backend file \ + --log-store-file-dir ./graph-logs +``` + +Logs are queried via GraphQL at `http://localhost:8000/graphql`: +```graphql +query { + _logs(subgraphId: "QmYourSubgraphHash", level: ERROR, first: 10) { + timestamp + level + text + } +} +``` + +**For complete documentation**, see the **[Log Store Guide](./docs/log-store.md)**, which covers: +- How to configure each backend (Elasticsearch, Loki, File) +- Complete GraphQL query examples +- Choosing the right backend for your use case +- Performance considerations and best practices + ## Contributing Please check [CONTRIBUTING.md](CONTRIBUTING.md) for development flow and conventions we use. diff --git a/docs/environment-variables.md b/docs/environment-variables.md index bdb3c3d0ecc..7a601e32e64 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -315,3 +315,55 @@ those. Disabling the store call cache may significantly impact performance; the actual impact depends on the average execution time of an `eth_call` compared to the cost of a database lookup for a cached result. (default: false) + +## Log Store Configuration + +`graph-node` supports storing and querying subgraph logs through multiple backends: Elasticsearch, Loki, local files, or disabled. + +**For complete log store documentation**, including detailed configuration, querying examples, and choosing the right backend, see the **[Log Store Guide](log-store.md)**. + +### Quick Reference + +**Backend selection:** +- `GRAPH_LOG_STORE_BACKEND`: `disabled` (default), `elasticsearch`, `loki`, or `file` + +**Elasticsearch:** +- `GRAPH_LOG_STORE_ELASTICSEARCH_URL`: Elasticsearch endpoint URL (required) +- `GRAPH_LOG_STORE_ELASTICSEARCH_USER`: Username (optional) +- `GRAPH_LOG_STORE_ELASTICSEARCH_PASSWORD`: Password (optional) +- `GRAPH_LOG_STORE_ELASTICSEARCH_INDEX`: Index name (default: `subgraph`) + +**Loki:** +- `GRAPH_LOG_STORE_LOKI_URL`: Loki endpoint URL (required) +- `GRAPH_LOG_STORE_LOKI_TENANT_ID`: Tenant ID (optional) + +**File-based:** +- `GRAPH_LOG_STORE_FILE_DIR`: Log directory (required) +- `GRAPH_LOG_STORE_FILE_MAX_SIZE`: Max file size in bytes (default: 104857600 = 100MB) +- `GRAPH_LOG_STORE_FILE_RETENTION_DAYS`: Retention period (default: 30) + +**Deprecated variables** (will be removed in future versions): +- `GRAPH_ELASTICSEARCH_URL` → use `GRAPH_LOG_STORE_ELASTICSEARCH_URL` +- `GRAPH_ELASTICSEARCH_USER` → use `GRAPH_LOG_STORE_ELASTICSEARCH_USER` +- `GRAPH_ELASTICSEARCH_PASSWORD` → use `GRAPH_LOG_STORE_ELASTICSEARCH_PASSWORD` +- `GRAPH_ELASTIC_SEARCH_INDEX` → use `GRAPH_LOG_STORE_ELASTICSEARCH_INDEX` + +### Example: File-based Logs for Local Development + +```bash +mkdir -p ./graph-logs +export GRAPH_LOG_STORE_BACKEND=file +export GRAPH_LOG_STORE_FILE_DIR=./graph-logs + +graph-node \ + --postgres-url postgresql://graph:pass@localhost/graph-node \ + --ethereum-rpc mainnet:https://... \ + --ipfs 127.0.0.1:5001 +``` + +See the **[Log Store Guide](log-store.md)** for: +- Detailed configuration for all backends +- How log stores work internally +- GraphQL query examples +- Choosing the right backend for your use case +- Best practices and troubleshooting diff --git a/docs/log-store.md b/docs/log-store.md new file mode 100644 index 00000000000..8be1cddecd8 --- /dev/null +++ b/docs/log-store.md @@ -0,0 +1,853 @@ +# Log Store Configuration and Usage + +This guide explains how to configure subgraph indexing logs storage in graph-node. + +## Table of Contents + +- [Overview](#overview) +- [How Log Stores Work](#how-log-stores-work) +- [Log Store Types](#log-store-types) + - [File-based Logs](#file-based-logs) + - [Elasticsearch](#elasticsearch) + - [Loki](#loki) + - [Disabled](#disabled) +- [Configuration](#configuration) + - [Environment Variables](#environment-variables) + - [CLI Arguments](#cli-arguments) + - [Configuration Precedence](#configuration-precedence) +- [Querying Logs](#querying-logs) +- [Migrating from Deprecated Configuration](#migrating-from-deprecated-configuration) +- [Choosing the Right Backend](#choosing-the-right-backend) +- [Best Practices](#best-practices) +- [Troubleshooting](#troubleshooting) + +## Overview + +Graph Node supports multiple logs storage backends for subgraph indexing logs. Subgraph indexing logs include: +- **User-generated logs**: Explicit logging from subgraph mapping code (`log.info()`, `log.error()`, etc.) +- **Runtime logs**: Handler execution, event processing, data source activity +- **System logs**: Warnings, errors, and diagnostics from the indexing system + +**Available backends:** +- **File**: JSON Lines files on local filesystem (for local development) +- **Elasticsearch**: Enterprise-grade search and analytics (for production) +- **Loki**: Grafana's lightweight log aggregation system (for production) +- **Disabled**: No log storage (default) + +All backends share the same query interface through GraphQL, making it easy to switch between them. + +**Important Note:** When log storage is disabled (the default), subgraph logs still appear in stdout/stderr as they always have. The "disabled" setting simply means logs are not stored separately in a queryable format. You can still see logs in your terminal or container logs - they just won't be available via the `_logs` GraphQL query. + +## How Log Stores Work + +### Architecture + +``` +┌─────────────────┐ +│ Subgraph Code │ +│ (mappings) │ +└────────┬────────┘ + │ log.info(), log.error(), etc. + ▼ +┌─────────────────┐ +│ Graph Runtime │ +│ (WebAssembly) │ +└────────┬────────┘ + │ Log events + ▼ +┌─────────────────┐ +│ Log Drain │ ◄─── slog-based logging system +└────────┬────────┘ + │ Write + ▼ +┌─────────────────┐ +│ Log Store │ ◄─── Configurable backend +│ (ES/Loki/File) │ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ GraphQL API │ ◄─── Unified query interface +│ (port 8000) │ +└─────────────────┘ +``` + +### Log Flow + +1. **Log sources** generate logs from: + - User mapping code (explicit `log.info()`, `log.error()`, etc. calls) + - Subgraph runtime (handler execution, event processing, data source triggers) + - System warnings and errors (indexing issues, constraint violations, etc.) +2. **Graph runtime** captures these logs with metadata (timestamp, level, source location) +3. **Log drain** formats logs and writes to configured backend +4. **Log store** persists logs and handles queries +5. **GraphQL API** exposes logs through the `_logs` query + +### Log Entry Structure + +Each log entry contains: +- **`id`**: Unique identifier +- **`subgraphId`**: Deployment hash (QmXxx...) +- **`timestamp`**: ISO 8601 timestamp (e.g., `2024-01-15T10:30:00.123456789Z`) +- **`level`**: CRITICAL, ERROR, WARNING, INFO, or DEBUG +- **`text`**: Log message +- **`arguments`**: Key-value pairs from structured logging +- **`meta`**: Source location (module, line, column) + +## Log Store Types + +### File-based Logs + +**Best for:** Local development, testing + +#### How It Works + +File-based logs store each subgraph's logs in a separate JSON Lines (`.jsonl`) file: + +``` +graph-logs/ +├── QmSubgraph1Hash.jsonl +├── QmSubgraph2Hash.jsonl +└── QmSubgraph3Hash.jsonl +``` + +Each line in the file is a complete JSON object representing one log entry. + +#### Storage Format + +```json +{"id":"QmTest-2024-01-15T10:30:00.123456789Z","subgraphId":"QmTest","timestamp":"2024-01-15T10:30:00.123456789Z","level":"error","text":"Handler execution failed, retries: 3","arguments":[{"key":"retries","value":"3"}],"meta":{"module":"mapping.ts","line":42,"column":10}} +``` + +#### Query Performance + +File-based logs stream through files line-by-line with bounded memory usage. + +**Performance characteristics:** +- Query time: O(n) where n = number of log entries +- Memory usage: O(skip + first) - only matching entries kept in memory +- Suitable for: Development and testing + +#### Configuration + +**Minimum configuration (CLI):** +```bash +graph-node \ + --postgres-url postgresql://graph:pass@localhost/graph-node \ + --ethereum-rpc mainnet:https://... \ + --ipfs 127.0.0.1:5001 \ + --log-store-backend file \ + --log-store-file-dir ./graph-logs +``` + +**Full configuration (environment variables):** +```bash +export GRAPH_LOG_STORE_BACKEND=file +export GRAPH_LOG_STORE_FILE_DIR=/var/log/graph-node +export GRAPH_LOG_STORE_FILE_MAX_SIZE=104857600 # 100MB +export GRAPH_LOG_STORE_FILE_RETENTION_DAYS=30 +``` + +#### Features + +**Advantages:** +- No external dependencies +- Simple setup (just specify a directory) +- Human-readable format (JSON Lines) +- Easy to inspect with standard tools (`jq`, `grep`, etc.) +- Good for debugging during development + +**Limitations:** +- Not suitable for production with high log volume +- No indexing (O(n) query time scales with file size) +- No automatic log rotation or retention management +- Single file per subgraph (no sharding) + +#### When to Use + +Use file-based logs when: +- Developing subgraphs locally +- Testing on a development machine +- Running low-traffic subgraphs (< 1000 total logs/day including system logs) +- You want simple log access without external services + +### Elasticsearch + +**Best for:** Production deployments, high log volume, advanced search + +#### How It Works + +Elasticsearch stores logs in indices with full-text search capabilities, making it ideal for production deployments with high log volume. + +**Architecture:** +``` +graph-node → Elasticsearch HTTP API → Elasticsearch cluster + → Index: subgraph-logs-* + → Query DSL for filtering +``` + +#### Features + +**Advantages:** +- **Indexed searching**: Fast queries even with millions of logs +- **Full-text search**: Powerful text search across log messages +- **Scalability**: Handles billions of log entries +- **High availability**: Supports clustering and replication +- **Kibana integration**: Rich visualization and dashboards for operators +- **Time-based indices**: Efficient retention management + +**Considerations:** +- Requires Elasticsearch cluster (infrastructure overhead) +- Resource-intensive (CPU, memory, disk) + +#### Configuration + +**Minimum configuration (CLI):** +```bash +graph-node \ + --postgres-url postgresql://graph:pass@localhost/graph-node \ + --ethereum-rpc mainnet:https://... \ + --ipfs 127.0.0.1:5001 \ + --log-store-backend elasticsearch \ + --log-store-elasticsearch-url http://localhost:9200 +``` + +**Full configuration with authentication:** +```bash +graph-node \ + --postgres-url postgresql://graph:pass@localhost/graph-node \ + --ethereum-rpc mainnet:https://... \ + --ipfs 127.0.0.1:5001 \ + --log-store-backend elasticsearch \ + --log-store-elasticsearch-url https://es.example.com:9200 \ + --log-store-elasticsearch-user elastic \ + --log-store-elasticsearch-password secret \ + --log-store-elasticsearch-index subgraph-logs +``` + +**Environment variables:** +```bash +export GRAPH_LOG_STORE_BACKEND=elasticsearch +export GRAPH_LOG_STORE_ELASTICSEARCH_URL=http://localhost:9200 +export GRAPH_LOG_STORE_ELASTICSEARCH_USER=elastic +export GRAPH_LOG_STORE_ELASTICSEARCH_PASSWORD=secret +export GRAPH_LOG_STORE_ELASTICSEARCH_INDEX=subgraph-logs +``` + +#### Index Configuration + +Logs are stored in the configured index (default: `subgraph`). The index mapping is automatically created. + +**Recommended index settings for production:** +```json +{ + "settings": { + "number_of_shards": 3, + "number_of_replicas": 1, + "refresh_interval": "5s" + } +} +``` + +#### Query Performance + +**Performance characteristics:** +- Query time: O(log n) with indexing +- Memory usage: Minimal (server-side filtering) +- Suitable for: Millions to billions of log entries + +#### When to Use + +Use Elasticsearch when: +- Running production deployments +- High log volume +- Need advanced search and filtering +- Want to build dashboards with Kibana +- Need high availability and scalability +- Have DevOps resources to manage Elasticsearch or can set up a managed ElasticSearch deployment + +### Loki + +**Best for:** Production deployments, Grafana users, cost-effective at scale + +#### How It Works + +Loki is Grafana's log aggregation system, designed to be cost-effective and easy to operate. Unlike Elasticsearch, Loki only indexes metadata (not full-text), making it more efficient for time-series log data. + +**Architecture:** +``` +graph-node → Loki HTTP API → Loki + → Stores compressed chunks + → Indexes labels only +``` + +#### Features + +**Advantages:** +- **Cost-effective**: Lower storage costs than Elasticsearch +- **Grafana integration**: Native integration with Grafana +- **Horizontal scalability**: Designed for cloud-native deployments +- **Multi-tenancy**: Built-in tenant isolation +- **Efficient compression**: Optimized for log data +- **LogQL**: Powerful query language similar to PromQL +- **Lower resource usage**: Less CPU/memory than Elasticsearch + +**Considerations:** +- No full-text indexing (slower text searches) +- Best used with Grafana (less tooling than Elasticsearch) +- Younger ecosystem than Elasticsearch +- Query performance depends on label cardinality + +#### Configuration + +**Minimum configuration (CLI):** +```bash +graph-node \ + --postgres-url postgresql://graph:pass@localhost/graph-node \ + --ethereum-rpc mainnet:https://... \ + --ipfs 127.0.0.1:5001 \ + --log-store-backend loki \ + --log-store-loki-url http://localhost:3100 +``` + +**With multi-tenancy:** +```bash +graph-node \ + --postgres-url postgresql://graph:pass@localhost/graph-node \ + --ethereum-rpc mainnet:https://... \ + --ipfs 127.0.0.1:5001 \ + --log-store-backend loki \ + --log-store-loki-url http://localhost:3100 \ + --log-store-loki-tenant-id my-graph-node +``` + +**Environment variables:** +```bash +export GRAPH_LOG_STORE_BACKEND=loki +export GRAPH_LOG_STORE_LOKI_URL=http://localhost:3100 +export GRAPH_LOG_STORE_LOKI_TENANT_ID=my-graph-node +``` + +#### Labels + +Loki uses labels for indexing. Graph Node automatically creates labels: +- `subgraph_id`: Deployment hash +- `level`: Log level +- `job`: "graph-node" + +#### Query Performance + +**Performance characteristics:** +- Query time: O(n) for text searches, O(log n) for label queries +- Memory usage: Minimal (server-side processing) +- Suitable for: Millions to billions of log entries +- Best performance with label-based filtering + +#### When to Use + +Use Loki when: +- Already using Grafana for monitoring +- Need cost-effective log storage at scale +- Want simpler operations than Elasticsearch +- Multi-tenancy is required +- Log volume is very high (> 1M logs/day) +- Full-text search is not critical + +### Disabled + +**Best for:** Minimalist deployments, reduced overhead + +#### How It Works + +When log storage is disabled (the default), subgraph logs are **still written to stdout/stderr** along with all other graph-node logs. They are just **not stored separately** in a queryable format. + +**Important:** "Disabled" does NOT mean logs are discarded. It means: +- Logs appear in stdout/stderr (traditional behavior) +- Logs are not stored in a separate queryable backend +- The `_logs` GraphQL query returns empty results + +This is the default behavior - logs continue to work exactly as they did before this feature was added. + +#### Configuration + +**Explicitly disable:** +```bash +export GRAPH_LOG_STORE_BACKEND=disabled +``` + +**Or simply don't configure a backend** (defaults to disabled): +```bash +# No log store configuration = disabled +graph-node \ + --postgres-url postgresql://graph:pass@localhost/graph-node \ + --ethereum-rpc mainnet:https://... \ + --ipfs 127.0.0.1:5001 +``` + +#### Features + +**Advantages:** +- Zero additional overhead +- No external dependencies +- Minimal configuration +- Logs still appear in stdout/stderr for debugging + +**Limitations:** +- Cannot query logs via GraphQL (`_logs` returns empty results) +- No separation of subgraph logs from other graph-node logs in stdout +- Logs mixed with system logs (harder to filter programmatically) +- No structured querying or filtering capabilities + +#### When to Use + +Use disabled log storage when: +- Running minimal test deployments with less dependencies +- Exposing logs to users is not required for your use case +- You'd like subgraph logs sent to external log collection (e.g., container logs) + +## Configuration + +### Environment Variables + +Environment variables are the recommended way to configure log stores, especially in containerized deployments. + +#### Backend Selection + +```bash +GRAPH_LOG_STORE_BACKEND= +``` +Valid values: `disabled`, `elasticsearch`, `loki`, `file` + +#### Elasticsearch + +```bash +GRAPH_LOG_STORE_ELASTICSEARCH_URL=http://localhost:9200 +GRAPH_LOG_STORE_ELASTICSEARCH_USER=elastic # Optional +GRAPH_LOG_STORE_ELASTICSEARCH_PASSWORD=secret # Optional +GRAPH_LOG_STORE_ELASTICSEARCH_INDEX=subgraph # Default: "subgraph" +``` + +#### Loki + +```bash +GRAPH_LOG_STORE_LOKI_URL=http://localhost:3100 +GRAPH_LOG_STORE_LOKI_TENANT_ID=my-tenant # Optional +``` + +#### File + +```bash +GRAPH_LOG_STORE_FILE_DIR=/var/log/graph-node +GRAPH_LOG_STORE_FILE_MAX_SIZE=104857600 # Default: 100MB +GRAPH_LOG_STORE_FILE_RETENTION_DAYS=30 # Default: 30 +``` + +### CLI Arguments + +CLI arguments provide the same functionality as environment variables and the two can be mixed together. + +#### Backend Selection + +```bash +--log-store-backend +``` + +#### Elasticsearch + +```bash +--log-store-elasticsearch-url +--log-store-elasticsearch-user +--log-store-elasticsearch-password +--log-store-elasticsearch-index +``` + +#### Loki + +```bash +--log-store-loki-url +--log-store-loki-tenant-id +``` + +#### File + +```bash +--log-store-file-dir +--log-store-file-max-size +--log-store-file-retention-days +``` + +### Configuration Precedence + +When multiple configuration methods are used: + +1. **CLI arguments** take highest precedence +2. **Environment variables** are used if no CLI args provided +3. **Defaults** are used if neither is set + +## Querying Logs + +All log backends share the same GraphQL query interface. Logs are queried through the subgraph-specific GraphQL endpoint: + +- **Subgraph by deployment**: `http://localhost:8000/subgraphs/id/` +- **Subgraph by name**: `http://localhost:8000/subgraphs/name/` + +The `_logs` query is automatically scoped to the subgraph in the URL, so you don't need to pass a `subgraphId` parameter. + +**Note**: Queries return all log types - both user-generated logs from mapping code and system-generated runtime logs (handler execution, events, warnings, etc.). Use the `search` filter to search for specific messages, or `level` to filter by severity. + +### Basic Query + +Query the `_logs` field at your subgraph's GraphQL endpoint: + +```graphql +query { + _logs( + first: 100 + ) { + id + timestamp + level + text + } +} +``` + +**Example endpoint**: `http://localhost:8000/subgraphs/id/QmYourDeploymentHash` + +### Query with Filters + +```graphql +query { + _logs( + level: ERROR + from: "2024-01-01T00:00:00Z" + to: "2024-01-31T23:59:59Z" + search: "timeout" + first: 50 + skip: 0 + ) { + id + timestamp + level + text + arguments { + key + value + } + meta { + module + line + column + } + } +} +``` + +### Available Filters + +| Filter | Type | Description | +|--------|------|-------------| +| `level` | LogLevel | Filter by level: CRITICAL, ERROR, WARNING, INFO, DEBUG | +| `from` | String | Start timestamp (ISO 8601) | +| `to` | String | End timestamp (ISO 8601) | +| `search` | String | Case-insensitive substring search in log messages | +| `first` | Int | Number of results to return (default: 100, max: 1000) | +| `skip` | Int | Number of results to skip for pagination (max: 10000) | + +### Response Fields + +| Field | Type | Description | +|-------|------|-------------| +| `id` | String | Unique log entry ID | +| `timestamp` | String | ISO 8601 timestamp with nanosecond precision | +| `level` | LogLevel | Log level (CRITICAL, ERROR, WARNING, INFO, DEBUG) | +| `text` | String | Complete log message with arguments | +| `arguments` | [(String, String)] | Structured key-value pairs | +| `meta.module` | String | Source file name | +| `meta.line` | Int | Line number | +| `meta.column` | Int | Column number | + +### Query Examples + +#### Recent Errors + +```graphql +query RecentErrors { + _logs( + level: ERROR + first: 20 + ) { + timestamp + text + meta { + module + line + } + } +} +``` + +#### Search for Specific Text + +```graphql +query SearchTimeout { + _logs( + search: "timeout" + first: 50 + ) { + timestamp + level + text + } +} +``` + +#### Handler Execution Logs + +```graphql +query HandlerLogs { + _logs( + search: "handler" + first: 50 + ) { + timestamp + level + text + } +} +``` + +#### Time Range Query + +```graphql +query LogsInRange { + _logs( + from: "2024-01-15T00:00:00Z" + to: "2024-01-15T23:59:59Z" + first: 1000 + ) { + timestamp + level + text + } +} +``` + +#### Pagination + +```graphql +# First page +query Page1 { + _logs( + first: 100 + skip: 0 + ) { + id + text + } +} + +# Second page +query Page2 { + _logs( + first: 100 + skip: 100 + ) { + id + text + } +} +``` + +### Querying the logs store using cURL + +```bash +curl -X POST http://localhost:8000/subgraphs/id/ \ + -H "Content-Type: application/json" \ + -d '{ + "query": "{ _logs(level: ERROR, first: 10) { timestamp level text } }" + }' +``` + +### Performance Considerations + +**File-based:** _for development only_ +- Streams through files line-by-line (bounded memory usage) +- Memory usage limited to O(skip + first) entries +- Query time is O(n) where n = total log entries in file + +**Elasticsearch:** +- Indexed queries are fast regardless of size +- Text searches are optimized with full-text indexing +- Can handle billions of log entries +- Best for production with high query volume + +**Loki:** +- Label-based queries are fast (indexed) +- Text searches scan compressed chunks (slower than Elasticsearch) +- Good performance with proper label filtering +- Best for production with Grafana integration + +## Choosing the Right Backend + +### Decision Matrix + +| Scenario | Recommended Backend | Reason | +|----------|-------------------|-----------------------------------------------------------------------------------| +| Local development | **File** | Simple, no dependencies, easy to inspect | +| Testing/staging | **File** or **Elasticsearch** | File for simplicity, ES if testing production config | +| Production | **Elasticsearch** or **Loki** | Both handle scale well | +| Using Grafana | **Loki** | Native integration | +| Cost-sensitive at scale | **Loki** | Lower storage costs | +| Want rich ecosystem | **Elasticsearch** | More tools and plugins | +| Minimal deployment | **Disabled** | No overhead | + +### Resource Requirements + +#### File-based +- **Disk**: Minimal (log files only) +- **Memory**: Depends on file size during queries +- **CPU**: Minimal +- **Network**: None +- **External services**: None + +#### Elasticsearch +- **Disk**: High (indices + replicas) +- **Memory**: 4-8GB minimum for small deployments +- **CPU**: Medium to high +- **Network**: HTTP API calls +- **External services**: Elasticsearch cluster + +#### Loki +- **Disk**: Medium (compressed chunks) +- **Memory**: 2-4GB minimum +- **CPU**: Low to medium +- **Network**: HTTP API calls +- **External services**: Loki server + +## Best Practices + +### General + +1. **Start with file-based for development** - Simplest setup, easy debugging +2. **Use Elasticsearch or Loki for production** - Better performance and features +3. **Monitor log volume** - Set up alerts if log volume grows unexpectedly (includes both user logs and system-generated runtime logs) +4. **Set retention policies** - Don't keep logs forever (disk space and cost) +5. **Use structured logging** - Pass key-value pairs to log functions for better filtering + +### File-based Logs + +1. **Monitor file size** - While queries use bounded memory, larger files take longer to scan (O(n) query time) +2. **Archive old logs** - Manually archive/delete old files or implement external rotation +3. **Monitor disk usage** - Files can grow quickly with verbose logging +4. **Use JSON tools** - `jq` is excellent for inspecting .jsonl files locally + +**Example local inspection:** +```bash +# Count logs by level +cat graph-logs/QmExample.jsonl | jq -r '.level' | sort | uniq -c + +# Find errors in last 1000 lines +tail -n 1000 graph-logs/QmExample.jsonl | jq 'select(.level == "error")' + +# Search for specific text +cat graph-logs/QmExample.jsonl | jq 'select(.text | contains("timeout"))' +``` + +### Elasticsearch + +1. **Use index patterns** - Time-based indices for easier management +2. **Configure retention** - Use Index Lifecycle Management (ILM) +3. **Monitor cluster health** - Set up Elasticsearch monitoring +4. **Tune for your workload** - Adjust shards/replicas based on log volume +5. **Use Kibana** - Visualize and explore logs effectively + +**Example Elasticsearch retention policy:** +```json +{ + "policy": "graph-logs-policy", + "phases": { + "hot": { "min_age": "0ms", "actions": {} }, + "warm": { "min_age": "7d", "actions": {} }, + "delete": { "min_age": "30d", "actions": { "delete": {} } } + } +} +``` + +### Loki + +1. **Use proper labels** - Don't over-index, keep label cardinality low +2. **Configure retention** - Set retention period in Loki config +3. **Use Grafana** - Native integration provides best experience +4. **Compress efficiently** - Loki's compression works best with batch writes +5. **Multi-tenancy** - Use tenant IDs if running multiple environments + +**Example Grafana query:** +```logql +{subgraph_id="QmExample", level="error"} |= "timeout" +``` + +## Troubleshooting + +### File-based Logs + +**Problem: Log file doesn't exist** +- Check `GRAPH_LOG_STORE_FILE_DIR` is set correctly +- Verify directory is writable by graph-node + +**Problem: Queries are slow** +- Subgraph logs file may be very large +- Consider archiving old logs or implementing retention +- For high-volume production use, switch to Elasticsearch or Loki + +**Problem: Disk filling up** +- Implement log rotation +- Reduce log verbosity in subgraph code +- Set up monitoring for disk usage + +### Elasticsearch + +**Problem: Cannot connect to Elasticsearch** +- Verify `GRAPH_LOG_STORE_ELASTICSEARCH_URL` is correct +- Check Elasticsearch is running: `curl http://localhost:9200` +- Verify authentication credentials if using security features +- Check network connectivity and firewall rules + +**Problem: No logs appearing in Elasticsearch** +- Check Elasticsearch cluster health +- Verify index exists: `curl http://localhost:9200/_cat/indices` +- Check graph-node logs for write errors +- Verify Elasticsearch has disk space + +**Problem: Queries are slow** +- Check Elasticsearch cluster health and resources +- Verify indices are not over-sharded +- Consider adding replicas for query performance +- Review query patterns and add appropriate indices + +### Loki + +**Problem: Cannot connect to Loki** +- Verify `GRAPH_LOG_STORE_LOKI_URL` is correct +- Check Loki is running: `curl http://localhost:3100/ready` +- Verify tenant ID if using multi-tenancy +- Check network connectivity + +**Problem: No logs appearing in Loki** +- Check Loki service health +- Verify Loki has disk space for chunks +- Check graph-node logs for write errors +- Verify Loki retention settings aren't deleting logs immediately + +**Problem: Queries return no results in Grafana** +- Check label selectors match what graph-node is sending +- Verify time range includes when logs were written +- Check Loki retention period +- Verify tenant ID matches if using multi-tenancy + +## Further Reading + +- [Environment Variables Reference](environment-variables.md) +- [Graph Node Configuration](config.md) +- [Elasticsearch Documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html) +- [Grafana Loki Documentation](https://grafana.com/docs/loki/latest/) From 983dfb4a3c4346abdde746326f6ad4b015e07f26 Mon Sep 17 00:00:00 2001 From: Ford Date: Thu, 15 Jan 2026 17:27:17 -0800 Subject: [PATCH 10/28] graphql,tests: Prevent combining _logs with entity queries --- graphql/src/execution/execution.rs | 9 +++++++ tests/tests/integration_tests.rs | 39 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/graphql/src/execution/execution.rs b/graphql/src/execution/execution.rs index ba6f81f4ac5..158f4a4b2e1 100644 --- a/graphql/src/execution/execution.rs +++ b/graphql/src/execution/execution.rs @@ -299,6 +299,15 @@ pub(crate) async fn execute_root_selection_set_uncached( } } + // Validate that _logs queries cannot be combined with regular entity queries + if !logs_fields.is_empty() && !data_set.is_empty() { + return Err(vec![QueryExecutionError::ValidationError( + None, + "The _logs query cannot be combined with other entity queries in the same request" + .to_string(), + )]); + } + // If we are getting regular data, prefetch it from the database let (mut values, trace) = if data_set.is_empty() && meta_items.is_empty() { (Object::default(), Trace::None) diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index ea3be84b654..0cc9cd55549 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -1531,6 +1531,45 @@ async fn test_logs_query(ctx: TestContext) -> anyhow::Result<()> { ); } + // Test 6: Verify that combining _logs with regular entity queries returns a validation error + let query = r#"{ + _logs(first: 10) { + id + text + } + triggers { + id + x + } + }"# + .to_string(); + let resp = subgraph.query(&query).await?; + + // Should have errors, not data + assert!( + resp.get("errors").is_some(), + "Expected errors when combining _logs with entity queries, got: {:?}", + resp + ); + + // Verify the error message mentions the validation issue + let errors = resp["errors"] + .as_array() + .context("Expected errors to be an array")?; + assert!( + !errors.is_empty(), + "Expected at least one error in response" + ); + + let error_msg = errors[0]["message"] + .as_str() + .context("Expected error message to be a string")?; + assert!( + error_msg.contains("_logs") && error_msg.contains("cannot be combined"), + "Expected validation error about _logs combination, got: {}", + error_msg + ); + Ok(()) } From 80602b46a15456136e1148fa3fd529f6a0ac0b00 Mon Sep 17 00:00:00 2001 From: Ford Date: Thu, 15 Jan 2026 18:00:34 -0800 Subject: [PATCH 11/28] graph: Refactor log drains to share common code - Create graph/src/log/common.rs for common log drain functionality - SimpleKVSerializer: Concatenates KV pairs to strings - VecKVSerializer: Collects KV pairs into Vec<(String, String)> - HashMapKVSerializer: Collects KV pairs into HashMap - LogMeta: Shared metadata structure (module, line, column) - LogEntryBuilder: Builder for common log entry fields - level_to_str(): Converts slog::Level to string - create_async_logger(): Consistent async logger creation - Updated FileDrain, LokiDrain, and ElasticDrain to use the log common utilities --- graph/src/log/common.rs | 219 +++++++++++++++++++++++++++++++++++++++ graph/src/log/elastic.rs | 151 ++++----------------------- graph/src/log/file.rs | 132 +++-------------------- graph/src/log/loki.rs | 149 ++++---------------------- graph/src/log/mod.rs | 1 + 5 files changed, 274 insertions(+), 378 deletions(-) create mode 100644 graph/src/log/common.rs diff --git a/graph/src/log/common.rs b/graph/src/log/common.rs new file mode 100644 index 00000000000..1e89a735e85 --- /dev/null +++ b/graph/src/log/common.rs @@ -0,0 +1,219 @@ +use std::collections::HashMap; +use std::fmt; + +use serde::Serialize; +use slog::*; + +/// Serializer for concatenating key-value arguments into a string +pub struct SimpleKVSerializer { + kvs: Vec<(String, String)>, +} + +impl SimpleKVSerializer { + pub fn new() -> Self { + Self { kvs: Vec::new() } + } + + /// Returns the number of key-value pairs and the concatenated string + pub fn finish(self) -> (usize, String) { + ( + self.kvs.len(), + self.kvs + .iter() + .map(|(k, v)| format!("{}: {}", k, v)) + .collect::>() + .join(", "), + ) + } +} + +impl Serializer for SimpleKVSerializer { + fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { + self.kvs.push((key.into(), val.to_string())); + Ok(()) + } +} + +/// Serializer for extracting key-value pairs into a Vec +pub struct VecKVSerializer { + kvs: Vec<(String, String)>, +} + +impl VecKVSerializer { + pub fn new() -> Self { + Self { kvs: Vec::new() } + } + + pub fn finish(self) -> Vec<(String, String)> { + self.kvs + } +} + +impl Serializer for VecKVSerializer { + fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { + self.kvs.push((key.into(), val.to_string())); + Ok(()) + } +} + +/// Serializer for extracting key-value pairs into a HashMap +pub struct HashMapKVSerializer { + kvs: Vec<(String, String)>, +} + +impl HashMapKVSerializer { + pub fn new() -> Self { + HashMapKVSerializer { kvs: Vec::new() } + } + + pub fn finish(self) -> HashMap { + self.kvs.into_iter().collect() + } +} + +impl Serializer for HashMapKVSerializer { + fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { + self.kvs.push((key.into(), val.to_string())); + Ok(()) + } +} + +/// Log metadata structure +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct LogMeta { + pub module: String, + pub line: i64, + pub column: i64, +} + +/// Converts an slog Level to a string representation +pub fn level_to_str(level: Level) -> &'static str { + match level { + Level::Critical => "critical", + Level::Error => "error", + Level::Warning => "warning", + Level::Info => "info", + Level::Debug => "debug", + Level::Trace => "trace", + } +} + +/// Builder for common log entry fields across different drain implementations +pub struct LogEntryBuilder<'a> { + record: &'a Record<'a>, + values: &'a OwnedKVList, + subgraph_id: String, + timestamp: String, +} + +impl<'a> LogEntryBuilder<'a> { + pub fn new( + record: &'a Record<'a>, + values: &'a OwnedKVList, + subgraph_id: String, + timestamp: String, + ) -> Self { + Self { + record, + values, + subgraph_id, + timestamp, + } + } + + /// Builds the log ID in the format: subgraph_id-timestamp + pub fn build_id(&self) -> String { + format!("{}-{}", self.subgraph_id, self.timestamp) + } + + /// Builds the text field by concatenating the message with all key-value pairs + pub fn build_text(&self) -> String { + // Serialize logger arguments + let mut serializer = SimpleKVSerializer::new(); + self.record + .kv() + .serialize(self.record, &mut serializer) + .expect("failed to serialize logger arguments"); + let (n_logger_kvs, logger_kvs) = serializer.finish(); + + // Serialize log message arguments + let mut serializer = SimpleKVSerializer::new(); + self.values + .serialize(self.record, &mut serializer) + .expect("failed to serialize log message arguments"); + let (n_value_kvs, value_kvs) = serializer.finish(); + + // Build text with all key-value pairs + let mut text = format!("{}", self.record.msg()); + if n_logger_kvs > 0 { + use std::fmt::Write; + write!(text, ", {}", logger_kvs).unwrap(); + } + if n_value_kvs > 0 { + use std::fmt::Write; + write!(text, ", {}", value_kvs).unwrap(); + } + + text + } + + /// Builds arguments as a Vec of tuples (for file drain) + pub fn build_arguments_vec(&self) -> Vec<(String, String)> { + let mut serializer = VecKVSerializer::new(); + self.record + .kv() + .serialize(self.record, &mut serializer) + .expect("failed to serialize log message arguments into vec"); + serializer.finish() + } + + /// Builds arguments as a HashMap (for elastic and loki drains) + pub fn build_arguments_map(&self) -> HashMap { + let mut serializer = HashMapKVSerializer::new(); + self.record + .kv() + .serialize(self.record, &mut serializer) + .expect("failed to serialize log message arguments into hash map"); + serializer.finish() + } + + /// Builds metadata from the log record + pub fn build_meta(&self) -> LogMeta { + LogMeta { + module: self.record.module().into(), + line: self.record.line() as i64, + column: self.record.column() as i64, + } + } + + /// Gets the level as a string + pub fn level_str(&self) -> &'static str { + level_to_str(self.record.level()) + } + + /// Gets the timestamp + pub fn timestamp(&self) -> &str { + &self.timestamp + } + + /// Gets the subgraph ID + pub fn subgraph_id(&self) -> &str { + &self.subgraph_id + } +} + +/// Creates a new asynchronous logger with consistent configuration +pub fn create_async_logger(drain: D, chan_size: usize, use_block_overflow: bool) -> Logger +where + D: Drain + Send + 'static, + D::Err: std::fmt::Debug, +{ + let mut builder = slog_async::Async::new(drain.fuse()).chan_size(chan_size); + + if use_block_overflow { + builder = builder.overflow_strategy(slog_async::OverflowStrategy::Block); + } + + Logger::root(builder.build().fuse(), o!()) +} diff --git a/graph/src/log/elastic.rs b/graph/src/log/elastic.rs index eb285d3d6e6..d2506211afd 100644 --- a/graph/src/log/elastic.rs +++ b/graph/src/log/elastic.rs @@ -1,6 +1,4 @@ use std::collections::HashMap; -use std::fmt; -use std::fmt::Write; use std::result::Result; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -15,10 +13,11 @@ use serde::ser::Serializer as SerdeSerializer; use serde::Serialize; use serde_json::json; use slog::*; -use slog_async; use crate::util::futures::retry; +use super::common::{create_async_logger, LogEntryBuilder, LogMeta}; + /// General configuration parameters for Elasticsearch logging. #[derive(Clone, Debug)] pub struct ElasticLoggingConfig { @@ -33,28 +32,14 @@ pub struct ElasticLoggingConfig { } /// Serializes an slog log level using a serde Serializer. -fn serialize_log_level(level: &Level, serializer: S) -> Result +fn serialize_log_level(level: &str, serializer: S) -> Result where S: SerdeSerializer, { - serializer.serialize_str(match level { - Level::Critical => "critical", - Level::Error => "error", - Level::Warning => "warning", - Level::Info => "info", - Level::Debug => "debug", - Level::Trace => "trace", - }) + serializer.serialize_str(level) } -// Log message meta data. -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct ElasticLogMeta { - module: String, - line: i64, - column: i64, -} +type ElasticLogMeta = LogMeta; // Log message to be written to Elasticsearch. #[derive(Clone, Debug, Serialize)] @@ -67,71 +52,10 @@ struct ElasticLog { timestamp: String, text: String, #[serde(serialize_with = "serialize_log_level")] - level: Level, + level: String, meta: ElasticLogMeta, } -struct HashMapKVSerializer { - kvs: Vec<(String, String)>, -} - -impl HashMapKVSerializer { - fn new() -> Self { - HashMapKVSerializer { - kvs: Default::default(), - } - } - - fn finish(self) -> HashMap { - let mut map = HashMap::new(); - self.kvs.into_iter().for_each(|(k, v)| { - map.insert(k, v); - }); - map - } -} - -impl Serializer for HashMapKVSerializer { - fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { - self.kvs.push((key.into(), format!("{}", val))); - Ok(()) - } -} - -/// A super-simple slog Serializer for concatenating key/value arguments. -struct SimpleKVSerializer { - kvs: Vec<(String, String)>, -} - -impl SimpleKVSerializer { - /// Creates a new `SimpleKVSerializer`. - fn new() -> Self { - SimpleKVSerializer { - kvs: Default::default(), - } - } - - /// Collects all key/value arguments into a single, comma-separated string. - /// Returns the number of key/value pairs and the string itself. - fn finish(self) -> (usize, String) { - ( - self.kvs.len(), - self.kvs - .iter() - .map(|(k, v)| format!("{}: {}", k, v)) - .collect::>() - .join(", "), - ) - } -} - -impl Serializer for SimpleKVSerializer { - fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { - self.kvs.push((key.into(), format!("{}", val))); - Ok(()) - } -} - /// Configuration for `ElasticDrain`. #[derive(Clone, Debug)] pub struct ElasticDrainConfig { @@ -309,43 +233,18 @@ impl Drain for ElasticDrain { type Err = (); fn log(&self, record: &Record, values: &OwnedKVList) -> Result { - // Don't sent `trace` logs to ElasticSearch. + // Don't send `trace` logs to ElasticSearch. if record.level() == Level::Trace { return Ok(()); } - let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Nanos, true); - let id = format!("{}-{}", self.config.custom_id_value, timestamp); - - // Serialize logger arguments - let mut serializer = SimpleKVSerializer::new(); - record - .kv() - .serialize(record, &mut serializer) - .expect("failed to serializer logger arguments"); - let (n_logger_kvs, logger_kvs) = serializer.finish(); - - // Serialize log message arguments - let mut serializer = SimpleKVSerializer::new(); - values - .serialize(record, &mut serializer) - .expect("failed to serialize log message arguments"); - let (n_value_kvs, value_kvs) = serializer.finish(); - // Serialize log message arguments into hash map - let mut serializer = HashMapKVSerializer::new(); - record - .kv() - .serialize(record, &mut serializer) - .expect("failed to serialize log message arguments into hash map"); - let arguments = serializer.finish(); - - let mut text = format!("{}", record.msg()); - if n_logger_kvs > 0 { - write!(text, ", {}", logger_kvs).unwrap(); - } - if n_value_kvs > 0 { - write!(text, ", {}", value_kvs).unwrap(); - } + let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Nanos, true); + let builder = LogEntryBuilder::new( + record, + values, + self.config.custom_id_value.clone(), + timestamp.clone(), + ); // Prepare custom id for log document let mut custom_id = HashMap::new(); @@ -356,17 +255,13 @@ impl Drain for ElasticDrain { // Prepare log document let log = ElasticLog { - id, + id: builder.build_id(), custom_id, - arguments, + arguments: builder.build_arguments_map(), timestamp, - text, - level: record.level(), - meta: ElasticLogMeta { - module: record.module().into(), - line: record.line() as i64, - column: record.column() as i64, - }, + text: builder.build_text(), + level: builder.level_str().to_string(), + meta: builder.build_meta(), }; // Push the log into the queue @@ -386,10 +281,6 @@ pub fn elastic_logger( error_logger: Logger, logs_sent_counter: Counter, ) -> Logger { - let elastic_drain = ElasticDrain::new(config, error_logger, logs_sent_counter).fuse(); - let async_drain = slog_async::Async::new(elastic_drain) - .chan_size(20000) - .build() - .fuse(); - Logger::root(async_drain, o!()) + let elastic_drain = ElasticDrain::new(config, error_logger, logs_sent_counter); + create_async_logger(elastic_drain, 20000, false) } diff --git a/graph/src/log/file.rs b/graph/src/log/file.rs index 4bdaddd2948..beb4e218ea4 100644 --- a/graph/src/log/file.rs +++ b/graph/src/log/file.rs @@ -1,5 +1,3 @@ -use std::fmt; -use std::fmt::Write as FmtWrite; use std::fs::{File, OpenOptions}; use std::io::{BufWriter, Write}; use std::path::PathBuf; @@ -9,6 +7,8 @@ use chrono::prelude::{SecondsFormat, Utc}; use serde::Serialize; use slog::*; +use super::common::{create_async_logger, LogEntryBuilder, LogMeta}; + /// Configuration for `FileDrain`. #[derive(Clone, Debug)] pub struct FileDrainConfig { @@ -35,64 +35,7 @@ struct FileLogDocument { meta: FileLogMeta, } -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct FileLogMeta { - module: String, - line: i64, - column: i64, -} - -/// Serializer for extracting key-value pairs into a Vec -struct VecKVSerializer { - kvs: Vec<(String, String)>, -} - -impl VecKVSerializer { - fn new() -> Self { - Self { kvs: Vec::new() } - } - - fn finish(self) -> Vec<(String, String)> { - self.kvs - } -} - -impl Serializer for VecKVSerializer { - fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { - self.kvs.push((key.into(), val.to_string())); - Ok(()) - } -} - -/// Serializer for concatenating key-value arguments into a string -struct SimpleKVSerializer { - kvs: Vec<(String, String)>, -} - -impl SimpleKVSerializer { - fn new() -> Self { - Self { kvs: Vec::new() } - } - - fn finish(self) -> (usize, String) { - ( - self.kvs.len(), - self.kvs - .iter() - .map(|(k, v)| format!("{}: {}", k, v)) - .collect::>() - .join(", "), - ) - } -} - -impl Serializer for SimpleKVSerializer { - fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { - self.kvs.push((key.into(), val.to_string())); - Ok(()) - } -} +type FileLogMeta = LogMeta; /// An slog `Drain` for logging to local files in JSON Lines format. /// @@ -136,62 +79,18 @@ impl Drain for FileDrain { } let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Nanos, true); - let id = format!("{}-{}", self.config.subgraph_id, timestamp); - - let level = match record.level() { - Level::Critical => "critical", - Level::Error => "error", - Level::Warning => "warning", - Level::Info => "info", - Level::Debug => "debug", - Level::Trace => "trace", - }; - - // Serialize logger arguments - let mut serializer = SimpleKVSerializer::new(); - record - .kv() - .serialize(record, &mut serializer) - .expect("failed to serialize logger arguments"); - let (n_logger_kvs, logger_kvs) = serializer.finish(); - - // Serialize log message arguments - let mut serializer = SimpleKVSerializer::new(); - values - .serialize(record, &mut serializer) - .expect("failed to serialize log message arguments"); - let (n_value_kvs, value_kvs) = serializer.finish(); - - // Serialize arguments into vec for storage - let mut serializer = VecKVSerializer::new(); - record - .kv() - .serialize(record, &mut serializer) - .expect("failed to serialize log message arguments into vec"); - let arguments = serializer.finish(); - - // Build text with all key-value pairs - let mut text = format!("{}", record.msg()); - if n_logger_kvs > 0 { - write!(text, ", {}", logger_kvs).unwrap(); - } - if n_value_kvs > 0 { - write!(text, ", {}", value_kvs).unwrap(); - } + let builder = + LogEntryBuilder::new(record, values, self.config.subgraph_id.clone(), timestamp); // Build log document let log_doc = FileLogDocument { - id, - subgraph_id: self.config.subgraph_id.clone(), - timestamp, - level: level.to_string(), - text, - arguments, - meta: FileLogMeta { - module: record.module().into(), - line: record.line() as i64, - column: record.column() as i64, - }, + id: builder.build_id(), + subgraph_id: builder.subgraph_id().to_string(), + timestamp: builder.timestamp().to_string(), + level: builder.level_str().to_string(), + text: builder.build_text(), + arguments: builder.build_arguments_vec(), + meta: builder.build_meta(), }; // Write JSON line (synchronous, buffered) @@ -229,12 +128,7 @@ pub fn file_logger(config: FileDrainConfig, error_logger: Logger) -> Logger { } }; - let async_drain = slog_async::Async::new(file_drain.fuse()) - .chan_size(20000) - .overflow_strategy(slog_async::OverflowStrategy::Block) - .build() - .fuse(); - Logger::root(async_drain, o!()) + create_async_logger(file_drain, 20000, true) } #[cfg(test)] diff --git a/graph/src/log/loki.rs b/graph/src/log/loki.rs index fcc3fe01e63..d921546ca26 100644 --- a/graph/src/log/loki.rs +++ b/graph/src/log/loki.rs @@ -1,6 +1,4 @@ use std::collections::HashMap; -use std::fmt; -use std::fmt::Write as FmtWrite; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -10,6 +8,8 @@ use serde::Serialize; use serde_json::json; use slog::*; +use super::common::{create_async_logger, LogEntryBuilder, LogMeta}; + /// Configuration for `LokiDrain`. #[derive(Clone, Debug)] pub struct LokiDrainConfig { @@ -40,72 +40,7 @@ struct LokiLogDocument { meta: LokiLogMeta, } -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct LokiLogMeta { - module: String, - line: i64, - column: i64, -} - -/// Serializer for extracting key-value pairs into a HashMap -struct HashMapKVSerializer { - kvs: Vec<(String, String)>, -} - -impl HashMapKVSerializer { - fn new() -> Self { - HashMapKVSerializer { - kvs: Default::default(), - } - } - - fn finish(self) -> HashMap { - let mut map = HashMap::new(); - self.kvs.into_iter().for_each(|(k, v)| { - map.insert(k, v); - }); - map - } -} - -impl Serializer for HashMapKVSerializer { - fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { - self.kvs.push((key.into(), format!("{}", val))); - Ok(()) - } -} - -/// Serializer for concatenating key-value arguments into a string -struct SimpleKVSerializer { - kvs: Vec<(String, String)>, -} - -impl SimpleKVSerializer { - fn new() -> Self { - SimpleKVSerializer { - kvs: Default::default(), - } - } - - fn finish(self) -> (usize, String) { - ( - self.kvs.len(), - self.kvs - .iter() - .map(|(k, v)| format!("{}: {}", k, v)) - .collect::>() - .join(", "), - ) - } -} - -impl Serializer for SimpleKVSerializer { - fn emit_arguments(&mut self, key: Key, val: &fmt::Arguments) -> slog::Result { - self.kvs.push((key.into(), format!("{}", val))); - Ok(()) - } -} +type LokiLogMeta = LogMeta; /// A slog `Drain` for logging to Loki. /// @@ -235,62 +170,23 @@ impl Drain for LokiDrain { let now = Utc::now(); let timestamp = now.to_rfc3339_opts(SecondsFormat::Nanos, true); let timestamp_ns = now.timestamp_nanos_opt().unwrap().to_string(); - let id = format!("{}-{}", self.config.subgraph_id, timestamp); - - let level = match record.level() { - Level::Critical => "critical", - Level::Error => "error", - Level::Warning => "warning", - Level::Info => "info", - Level::Debug => "debug", - Level::Trace => "trace", - }; - // Serialize logger arguments - let mut serializer = SimpleKVSerializer::new(); - record - .kv() - .serialize(record, &mut serializer) - .expect("failed to serialize logger arguments"); - let (n_logger_kvs, logger_kvs) = serializer.finish(); - - // Serialize log message arguments - let mut serializer = SimpleKVSerializer::new(); - values - .serialize(record, &mut serializer) - .expect("failed to serialize log message arguments"); - let (n_value_kvs, value_kvs) = serializer.finish(); - - // Serialize arguments into hash map - let mut serializer = HashMapKVSerializer::new(); - record - .kv() - .serialize(record, &mut serializer) - .expect("failed to serialize log message arguments into hash map"); - let arguments = serializer.finish(); - - // Build text with all key-value pairs - let mut text = format!("{}", record.msg()); - if n_logger_kvs > 0 { - write!(text, ", {}", logger_kvs).unwrap(); - } - if n_value_kvs > 0 { - write!(text, ", {}", value_kvs).unwrap(); - } + let builder = LogEntryBuilder::new( + record, + values, + self.config.subgraph_id.clone(), + timestamp.clone(), + ); // Build log document let log_doc = LokiLogDocument { - id, - subgraph_id: self.config.subgraph_id.clone(), - timestamp: timestamp.clone(), - level: level.to_string(), - text, - arguments, - meta: LokiLogMeta { - module: record.module().into(), - line: record.line() as i64, - column: record.column() as i64, - }, + id: builder.build_id(), + subgraph_id: builder.subgraph_id().to_string(), + timestamp, + level: builder.level_str().to_string(), + text: builder.build_text(), + arguments: builder.build_arguments_map(), + meta: builder.build_meta(), }; // Serialize to JSON line @@ -304,8 +200,8 @@ impl Drain for LokiDrain { // Build labels for Loki stream let mut labels = HashMap::new(); - labels.insert("subgraphId".to_string(), self.config.subgraph_id.clone()); - labels.insert("level".to_string(), level.to_string()); + labels.insert("subgraphId".to_string(), builder.subgraph_id().to_string()); + labels.insert("level".to_string(), builder.level_str().to_string()); // Create log entry let entry = LokiLogEntry { @@ -348,13 +244,8 @@ fn group_by_labels( /// Uses `error_logger` to print any Loki logging errors, /// so they don't go unnoticed. pub fn loki_logger(config: LokiDrainConfig, error_logger: Logger) -> Logger { - let loki_drain = LokiDrain::new(config, error_logger).fuse(); - let async_drain = slog_async::Async::new(loki_drain) - .chan_size(20000) - .overflow_strategy(slog_async::OverflowStrategy::Block) - .build() - .fuse(); - Logger::root(async_drain, o!()) + let loki_drain = LokiDrain::new(config, error_logger); + create_async_logger(loki_drain, 20000, true) } #[cfg(test)] diff --git a/graph/src/log/mod.rs b/graph/src/log/mod.rs index 4c58af34bac..1efb2700ba5 100644 --- a/graph/src/log/mod.rs +++ b/graph/src/log/mod.rs @@ -30,6 +30,7 @@ use std::{ use crate::prelude::ENV_VARS; pub mod codes; +pub mod common; pub mod elastic; pub mod factory; pub mod file; From b0728ca9640635f1f54dc7555e4beaa3749240de Mon Sep 17 00:00:00 2001 From: Ford Date: Fri, 16 Jan 2026 09:10:36 -0800 Subject: [PATCH 12/28] graphql, tests: Ensure logs graphql queries work on failed subgraphs - include _logs in the set of special fields that bypass indexing error shortcutting when subgraph failed - add integration test to ensure _log queries return logs after subgraph failed --- graphql/src/store/resolver.rs | 27 +++++++++++-- tests/tests/integration_tests.rs | 65 ++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/graphql/src/store/resolver.rs b/graphql/src/store/resolver.rs index 779a9766fe7..620cd046441 100644 --- a/graphql/src/store/resolver.rs +++ b/graphql/src/store/resolver.rs @@ -13,8 +13,8 @@ use graph::derive::CheapClone; use graph::prelude::alloy::primitives::B256; use graph::prelude::*; use graph::schema::{ - ast as sast, INTROSPECTION_SCHEMA_FIELD_NAME, INTROSPECTION_TYPE_FIELD_NAME, META_FIELD_NAME, - META_FIELD_TYPE, + ast as sast, INTROSPECTION_SCHEMA_FIELD_NAME, INTROSPECTION_TYPE_FIELD_NAME, LOGS_FIELD_NAME, + META_FIELD_NAME, META_FIELD_TYPE, }; use graph::schema::{ErrorPolicy, BLOCK_FIELD_TYPE}; @@ -354,6 +354,23 @@ impl Resolver for StoreResolver { return Ok(()); } + // Check if the query only contains debugging fields (_meta, _logs). + // If so, don't add indexing errors - these queries are specifically for debugging + // failed subgraphs and should work without errors. + // Introspection queries (__schema, __type) still get the indexing_error to inform + // users the subgraph has issues, but they return data. + let only_debugging_fields = result + .data() + .map(|data| { + data.iter() + .all(|(key, _)| key == META_FIELD_NAME || key == LOGS_FIELD_NAME) + }) + .unwrap_or(false); + + if only_debugging_fields { + return Ok(()); + } + // Add the "indexing_error" to the response. assert!(result.errors_mut().is_empty()); *result.errors_mut() = vec![QueryError::IndexingError]; @@ -365,9 +382,10 @@ impl Resolver for StoreResolver { ErrorPolicy::Deny => { let mut data = result.take_data(); - // Only keep the _meta, __schema and __type fields from the data + // Only keep the _meta, _logs, __schema and __type fields from the data let meta_fields = data.as_mut().and_then(|d| { let meta_field = d.remove(META_FIELD_NAME); + let logs_field = d.remove(LOGS_FIELD_NAME); let schema_field = d.remove(INTROSPECTION_SCHEMA_FIELD_NAME); let type_field = d.remove(INTROSPECTION_TYPE_FIELD_NAME); @@ -377,6 +395,9 @@ impl Resolver for StoreResolver { if let Some(meta_field) = meta_field { meta_fields.push((Word::from(META_FIELD_NAME), meta_field)); } + if let Some(logs_field) = logs_field { + meta_fields.push((Word::from(LOGS_FIELD_NAME), logs_field)); + } if let Some(schema_field) = schema_field { meta_fields .push((Word::from(INTROSPECTION_SCHEMA_FIELD_NAME), schema_field)); diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index 0cc9cd55549..7ca6806a25c 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -1136,6 +1136,71 @@ async fn test_poi_for_failed_subgraph(ctx: TestContext) -> anyhow::Result<()> { let resp = Subgraph::query_with_vars(FETCH_POI, vars).await?; assert_eq!(None, resp.get("errors")); assert!(resp["data"]["proofOfIndexing"].is_string()); + + // Test that _logs query works on failed subgraphs (critical for debugging!) + // Wait a moment for logs to be written + sleep(Duration::from_secs(2)).await; + + let query = r#"{ + _logs(first: 100) { + id + timestamp + level + text + } + }"# + .to_string(); + + let resp = subgraph.query(&query).await?; + + // Should not have GraphQL errors when querying logs on failed subgraph + assert!( + resp.get("errors").is_none(), + "Expected no errors when querying _logs on failed subgraph, got: {:?}", + resp.get("errors") + ); + + let logs = resp["data"]["_logs"] + .as_array() + .context("Expected _logs to be an array")?; + + // The critical assertion: _logs query works on failed subgraphs + // This enables debugging even when the subgraph has crashed + println!( + "Successfully queried _logs on failed subgraph, found {} log entries", + logs.len() + ); + + // Print a sample of logs to see what's available (for documentation/debugging) + if !logs.is_empty() { + println!("Sample logs from failed subgraph:"); + for (i, log) in logs.iter().take(5).enumerate() { + println!( + " Log {}: level={:?}, text={:?}", + i + 1, + log["level"].as_str(), + log["text"].as_str() + ); + } + } + + // Verify we can also filter by level on failed subgraphs + let query = r#"{ + _logs(level: ERROR, first: 100) { + level + text + } + }"# + .to_string(); + + let resp = subgraph.query(&query).await?; + assert!( + resp.get("errors").is_none(), + "Expected no errors when filtering _logs by level on failed subgraph" + ); + + println!("✓ _logs query works on failed subgraphs - critical for debugging!"); + Ok(()) } From d8c413d60695dad55a24353b2605619541710d85 Mon Sep 17 00:00:00 2001 From: Ford Date: Fri, 16 Jan 2026 09:11:19 -0800 Subject: [PATCH 13/28] graph: Add Default impls for log serializers --- graph/src/log/common.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/graph/src/log/common.rs b/graph/src/log/common.rs index 1e89a735e85..1d6c921015d 100644 --- a/graph/src/log/common.rs +++ b/graph/src/log/common.rs @@ -9,6 +9,12 @@ pub struct SimpleKVSerializer { kvs: Vec<(String, String)>, } +impl Default for SimpleKVSerializer { + fn default() -> Self { + Self::new() + } +} + impl SimpleKVSerializer { pub fn new() -> Self { Self { kvs: Vec::new() } @@ -39,6 +45,12 @@ pub struct VecKVSerializer { kvs: Vec<(String, String)>, } +impl Default for VecKVSerializer { + fn default() -> Self { + Self::new() + } +} + impl VecKVSerializer { pub fn new() -> Self { Self { kvs: Vec::new() } @@ -61,6 +73,12 @@ pub struct HashMapKVSerializer { kvs: Vec<(String, String)>, } +impl Default for HashMapKVSerializer { + fn default() -> Self { + Self::new() + } +} + impl HashMapKVSerializer { pub fn new() -> Self { HashMapKVSerializer { kvs: Vec::new() } From e645b4c89b7239ecb871c914794f72566edea4b6 Mon Sep 17 00:00:00 2001 From: Ford Date: Sat, 17 Jan 2026 12:58:42 -0800 Subject: [PATCH 14/28] graphql, tests: _logs queries only return fields selected in query --- graphql/src/execution/execution.rs | 92 ++++++++++++++++++++---------- tests/tests/integration_tests.rs | 49 ++++++++++++++++ 2 files changed, 111 insertions(+), 30 deletions(-) diff --git a/graphql/src/execution/execution.rs b/graphql/src/execution/execution.rs index 158f4a4b2e1..ec69fdae9bf 100644 --- a/graphql/src/execution/execution.rs +++ b/graphql/src/execution/execution.rs @@ -345,43 +345,75 @@ pub(crate) async fn execute_root_selection_set_uncached( )] })?; - // Convert log entries to GraphQL values + // Get _Log_ type from schema for field selection + let log_type_def = ctx.query.schema.get_named_type("_Log_").ok_or_else(|| { + vec![QueryExecutionError::AbstractTypeError( + "_Log_ type not found in schema".to_string(), + )] + })?; + let log_object_type = match log_type_def { + s::TypeDefinition::Object(obj) => sast::ObjectType::from(Arc::new(obj.clone())), + _ => { + return Err(vec![QueryExecutionError::AbstractTypeError( + "_Log_ is not an object type".to_string(), + )]) + } + }; + + // Convert log entries to GraphQL values, respecting field selection let log_values: Vec = log_entries .into_iter() .map(|entry| { - // Convert arguments Vec<(String, String)> to GraphQL objects - let arguments: Vec = entry - .arguments - .into_iter() - .map(|(key, value)| { - object! { - key: key, - value: value, - __typename: "_LogArgument_" - } - }) - .collect(); - - // Convert log level to string + // Convert log level to string (needed by multiple fields) let level_str = entry.level.as_str().to_uppercase(); + let mut results = Vec::new(); - object! { - id: entry.id, - subgraphId: entry.subgraph_id.to_string(), - timestamp: entry.timestamp, - level: level_str, - text: entry.text, - arguments: arguments, - meta: object! { - module: entry.meta.module, - line: r::Value::Int(entry.meta.line), - column: r::Value::Int(entry.meta.column), - __typename: "_LogMeta_" - }, - __typename: "_Log_" + // Iterate over requested fields (same pattern as entity queries) + for log_field in field + .selection_set + .fields_for(&log_object_type) + .map_err(|e| vec![e])? + { + let response_key = log_field.response_key(); + + // Map field name to LogEntry value (replaces prefetched_object lookup) + let value = match log_field.name.as_str() { + "id" => entry.id.clone().into_value(), + "subgraphId" => entry.subgraph_id.to_string().into_value(), + "timestamp" => entry.timestamp.clone().into_value(), + "level" => level_str.clone().into_value(), + "text" => entry.text.clone().into_value(), + "arguments" => { + // Convert arguments Vec<(String, String)> to GraphQL objects + let args: Vec = entry + .arguments + .iter() + .map(|(key, value)| { + object! { + key: key.clone(), + value: value.clone(), + __typename: "_LogArgument_" + } + }) + .collect(); + r::Value::List(args) + } + "meta" => object! { + module: entry.meta.module.clone(), + line: r::Value::Int(entry.meta.line), + column: r::Value::Int(entry.meta.column), + __typename: "_LogMeta_" + }, + "__typename" => "_Log_".into_value(), + _ => continue, // Unknown field, skip it + }; + + results.push((Word::from(response_key), value)); } + + Ok(r::Value::Object(Object::from_iter(results))) }) - .collect(); + .collect::, Vec>>()?; let response_key = Word::from(field.response_key()); let logs_object = Object::from_iter(vec![(response_key, r::Value::List(log_values))]); diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index 7ca6806a25c..941edc5fef6 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -1635,6 +1635,55 @@ async fn test_logs_query(ctx: TestContext) -> anyhow::Result<()> { error_msg ); + // Test 7: Field selection - verify only requested fields are returned + let query = r#"{ + _logs(first: 1) { + id + timestamp + } + }"# + .to_string(); + let resp = subgraph.query(&query).await?; + + let logs = resp["data"]["_logs"] + .as_array() + .context("Expected _logs to be an array")?; + + if !logs.is_empty() { + let log = &logs[0]; + + // Verify requested fields are present + assert!(log.get("id").is_some(), "Expected id field to be present"); + assert!( + log.get("timestamp").is_some(), + "Expected timestamp field to be present" + ); + + // Verify non-requested fields are NOT present + assert!( + log.get("text").is_none(), + "Expected text field to NOT be present (field selection bug)" + ); + assert!( + log.get("level").is_none(), + "Expected level field to NOT be present (field selection bug)" + ); + assert!( + log.get("subgraphId").is_none(), + "Expected subgraphId field to NOT be present (field selection bug)" + ); + assert!( + log.get("arguments").is_none(), + "Expected arguments field to NOT be present (field selection bug)" + ); + assert!( + log.get("meta").is_none(), + "Expected meta field to NOT be present (field selection bug)" + ); + + println!("✓ Field selection works correctly - only requested fields returned"); + } + Ok(()) } From ba67ea3dc0d686641e6293aa28f1cbb19b585107 Mon Sep 17 00:00:00 2001 From: Ford Date: Sat, 17 Jan 2026 22:44:36 -0800 Subject: [PATCH 15/28] graph, node: Cleanup FileLogStore files on startup - Keep logs within retention_hours of now, skipping cleanup if --log-store-retention-hours=0 --- graph/src/components/log_store/file.rs | 296 +++++++++++++++++++++++-- graph/src/components/log_store/mod.rs | 68 ++++-- graph/src/log/factory.rs | 6 +- graph/src/log/file.rs | 8 - node/src/launcher.rs | 3 +- node/src/opt.rs | 14 +- 6 files changed, 326 insertions(+), 69 deletions(-) diff --git a/graph/src/components/log_store/file.rs b/graph/src/components/log_store/file.rs index af40ce41329..6564bdb85c0 100644 --- a/graph/src/components/log_store/file.rs +++ b/graph/src/components/log_store/file.rs @@ -12,29 +12,38 @@ use super::{LogEntry, LogMeta, LogQuery, LogStore, LogStoreError}; pub struct FileLogStore { directory: PathBuf, - // TODO: Implement log rotation when file exceeds max_file_size - #[allow(dead_code)] - max_file_size: u64, - // TODO: Implement automatic cleanup of logs older than retention_days - #[allow(dead_code)] - retention_days: u32, + retention_hours: u32, } impl FileLogStore { - pub fn new( - directory: PathBuf, - max_file_size: u64, - retention_days: u32, - ) -> Result { + pub fn new(directory: PathBuf, retention_hours: u32) -> Result { // Create directory if it doesn't exist std::fs::create_dir_all(&directory) .map_err(|e| LogStoreError::InitializationFailed(e.into()))?; - Ok(Self { + let store = Self { directory, - max_file_size, - retention_days, - }) + retention_hours, + }; + + // Run cleanup on startup for all existing log files + if retention_hours > 0 { + if let Ok(entries) = std::fs::read_dir(&store.directory) { + for entry in entries.filter_map(Result::ok) { + let path = entry.path(); + + // Only process .jsonl files + if path.extension().and_then(|s| s.to_str()) == Some("jsonl") { + // Run cleanup, but don't fail initialization if cleanup fails + if let Err(e) = store.cleanup_old_logs(&path) { + eprintln!("Warning: Failed to cleanup old logs for {:?}: {}", path, e); + } + } + } + } + } + + Ok(store) } /// Get log file path for a subgraph @@ -95,6 +104,57 @@ impl FileLogStore { true } + + /// Delete log entries older than retention_hours + fn cleanup_old_logs(&self, file_path: &std::path::Path) -> Result<(), LogStoreError> { + if self.retention_hours == 0 { + return Ok(()); // Cleanup disabled, keep all logs + } + + use chrono::{DateTime, Duration, Utc}; + use std::io::Write; + + // Calculate cutoff time + let cutoff = Utc::now() - Duration::hours(self.retention_hours as i64); + + // Read all log entries + let file = File::open(file_path).map_err(|e| LogStoreError::QueryFailed(e.into()))?; + let reader = BufReader::new(file); + + let kept_entries: Vec = reader + .lines() + .filter_map(|line| line.ok()) + .filter(|line| { + // Parse timestamp from log entry + if let Some(entry) = self.parse_line(line) { + // Parse RFC3339 timestamp + if let Ok(timestamp) = DateTime::parse_from_rfc3339(&entry.timestamp) { + return timestamp.with_timezone(&Utc) >= cutoff; + } + } + // Keep if we can't parse (don't delete on error) + true + }) + .collect(); + + // Write filtered file atomically + let temp_path = file_path.with_extension("jsonl.tmp"); + let mut temp_file = + File::create(&temp_path).map_err(|e| LogStoreError::QueryFailed(e.into()))?; + + for entry in kept_entries { + writeln!(temp_file, "{}", entry).map_err(|e| LogStoreError::QueryFailed(e.into()))?; + } + + temp_file + .sync_all() + .map_err(|e| LogStoreError::QueryFailed(e.into()))?; + + // Atomic rename + std::fs::rename(&temp_path, file_path).map_err(|e| LogStoreError::QueryFailed(e.into()))?; + + Ok(()) + } } /// Helper struct to enable timestamp-based comparisons for BinaryHeap @@ -185,8 +245,11 @@ impl LogStore for FileLogStore { .map(|Reverse(te)| te.entry) .collect(); - // Sort by timestamp descending (most recent first) - result.sort_by(|a, b| b.timestamp.cmp(&a.timestamp)); + // Sort by timestamp (direction based on query) + match query.order_direction { + super::OrderDirection::Desc => result.sort_by(|a, b| b.timestamp.cmp(&a.timestamp)), + super::OrderDirection::Asc => result.sort_by(|a, b| a.timestamp.cmp(&b.timestamp)), + } // Apply skip and take to get the final page Ok(result @@ -231,7 +294,7 @@ mod tests { #[test] fn test_file_log_store_initialization() { let temp_dir = TempDir::new().unwrap(); - let store = FileLogStore::new(temp_dir.path().to_path_buf(), 1024 * 1024, 30); + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 0); assert!(store.is_ok()); let store = store.unwrap(); @@ -241,7 +304,7 @@ mod tests { #[test] fn test_log_file_path() { let temp_dir = TempDir::new().unwrap(); - let store = FileLogStore::new(temp_dir.path().to_path_buf(), 1024 * 1024, 30).unwrap(); + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 0).unwrap(); let subgraph_id = DeploymentHash::new("QmTest").unwrap(); let path = store.log_file_path(&subgraph_id); @@ -252,7 +315,7 @@ mod tests { #[tokio::test] async fn test_query_nonexistent_file() { let temp_dir = TempDir::new().unwrap(); - let store = FileLogStore::new(temp_dir.path().to_path_buf(), 1024 * 1024, 30).unwrap(); + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 0).unwrap(); let query = LogQuery { subgraph_id: DeploymentHash::new("QmNonexistent").unwrap(), @@ -262,6 +325,7 @@ mod tests { search: None, first: 100, skip: 0, + order_direction: super::super::OrderDirection::Desc, }; let result = store.query_logs(query).await; @@ -272,7 +336,7 @@ mod tests { #[tokio::test] async fn test_query_with_sample_data() { let temp_dir = TempDir::new().unwrap(); - let store = FileLogStore::new(temp_dir.path().to_path_buf(), 1024 * 1024, 30).unwrap(); + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 0).unwrap(); let subgraph_id = DeploymentHash::new("QmTest").unwrap(); let file_path = store.log_file_path(&subgraph_id); @@ -303,6 +367,7 @@ mod tests { search: None, first: 100, skip: 0, + order_direction: super::super::OrderDirection::Desc, }; let result = store.query_logs(query).await; @@ -318,7 +383,7 @@ mod tests { #[tokio::test] async fn test_query_with_level_filter() { let temp_dir = TempDir::new().unwrap(); - let store = FileLogStore::new(temp_dir.path().to_path_buf(), 1024 * 1024, 30).unwrap(); + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 0).unwrap(); let subgraph_id = DeploymentHash::new("QmTest").unwrap(); let file_path = store.log_file_path(&subgraph_id); @@ -351,6 +416,7 @@ mod tests { search: None, first: 100, skip: 0, + order_direction: super::super::OrderDirection::Desc, }; let result = store.query_logs(query).await; @@ -360,4 +426,190 @@ mod tests { assert_eq!(entries.len(), 2); assert!(entries.iter().all(|e| e.level == LogLevel::Error)); } + + #[tokio::test] + async fn test_cleanup_old_logs() { + use chrono::{Duration, Utc}; + + let temp_dir = TempDir::new().unwrap(); + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 24).unwrap(); + + let subgraph_id = DeploymentHash::new("QmTest").unwrap(); + let file_path = store.log_file_path(&subgraph_id); + + // Create test data with old and new entries + let mut file = File::create(&file_path).unwrap(); + + // Old entry (48 hours ago) + let old_timestamp = + (Utc::now() - Duration::hours(48)).to_rfc3339_opts(chrono::SecondsFormat::Secs, true); + let old_entry = FileLogDocument { + id: "log-old".to_string(), + subgraph_id: "QmTest".to_string(), + timestamp: old_timestamp, + level: "info".to_string(), + text: "Old log entry".to_string(), + arguments: vec![], + meta: FileLogMeta { + module: "test.ts".to_string(), + line: 1, + column: 1, + }, + }; + writeln!(file, "{}", serde_json::to_string(&old_entry).unwrap()).unwrap(); + + // New entry (12 hours ago) + let new_timestamp = + (Utc::now() - Duration::hours(12)).to_rfc3339_opts(chrono::SecondsFormat::Secs, true); + let new_entry = FileLogDocument { + id: "log-new".to_string(), + subgraph_id: "QmTest".to_string(), + timestamp: new_timestamp, + level: "info".to_string(), + text: "New log entry".to_string(), + arguments: vec![], + meta: FileLogMeta { + module: "test.ts".to_string(), + line: 2, + column: 1, + }, + }; + writeln!(file, "{}", serde_json::to_string(&new_entry).unwrap()).unwrap(); + drop(file); + + // Run cleanup + store.cleanup_old_logs(&file_path).unwrap(); + + // Query to verify only new entry remains + let query = LogQuery { + subgraph_id, + level: None, + from: None, + to: None, + search: None, + first: 100, + skip: 0, + order_direction: super::super::OrderDirection::Desc, + }; + + let result = store.query_logs(query).await.unwrap(); + assert_eq!(result.len(), 1); + assert_eq!(result[0].id, "log-new"); + } + + #[tokio::test] + async fn test_cleanup_keeps_unparseable_entries() { + let temp_dir = TempDir::new().unwrap(); + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 24).unwrap(); + + let subgraph_id = DeploymentHash::new("QmTest").unwrap(); + let file_path = store.log_file_path(&subgraph_id); + + // Create test data with valid and unparseable entries + let mut file = File::create(&file_path).unwrap(); + + // Valid entry + let valid_entry = FileLogDocument { + id: "log-valid".to_string(), + subgraph_id: "QmTest".to_string(), + timestamp: chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true), + level: "info".to_string(), + text: "Valid entry".to_string(), + arguments: vec![], + meta: FileLogMeta { + module: "test.ts".to_string(), + line: 1, + column: 1, + }, + }; + writeln!(file, "{}", serde_json::to_string(&valid_entry).unwrap()).unwrap(); + + // Unparseable entry (invalid JSON) + writeln!(file, "{{invalid json}}").unwrap(); + + // Entry with invalid timestamp + writeln!( + file, + r#"{{"id":"log-bad-time","subgraphId":"QmTest","timestamp":"not-a-timestamp","level":"info","text":"Bad timestamp","arguments":[],"meta":{{"module":"test.ts","line":2,"column":1}}}}"# + ) + .unwrap(); + drop(file); + + // Run cleanup + store.cleanup_old_logs(&file_path).unwrap(); + + // Read file contents directly + let file_contents = std::fs::read_to_string(&file_path).unwrap(); + let lines: Vec<&str> = file_contents.lines().collect(); + + // All 3 entries should be kept (don't delete on error) + assert_eq!(lines.len(), 3); + } + + #[tokio::test] + async fn test_startup_cleanup() { + use chrono::{Duration, Utc}; + + let temp_dir = TempDir::new().unwrap(); + + // Create a log file with old entries before initializing the store + let file_path = temp_dir.path().join("QmTestStartup.jsonl"); + let mut file = File::create(&file_path).unwrap(); + + // Old entry (48 hours ago) + let old_timestamp = + (Utc::now() - Duration::hours(48)).to_rfc3339_opts(chrono::SecondsFormat::Secs, true); + let old_entry = FileLogDocument { + id: "log-old".to_string(), + subgraph_id: "QmTestStartup".to_string(), + timestamp: old_timestamp, + level: "info".to_string(), + text: "Old log entry".to_string(), + arguments: vec![], + meta: FileLogMeta { + module: "test.ts".to_string(), + line: 1, + column: 1, + }, + }; + writeln!(file, "{}", serde_json::to_string(&old_entry).unwrap()).unwrap(); + + // New entry (12 hours ago) + let new_timestamp = + (Utc::now() - Duration::hours(12)).to_rfc3339_opts(chrono::SecondsFormat::Secs, true); + let new_entry = FileLogDocument { + id: "log-new".to_string(), + subgraph_id: "QmTestStartup".to_string(), + timestamp: new_timestamp, + level: "info".to_string(), + text: "New log entry".to_string(), + arguments: vec![], + meta: FileLogMeta { + module: "test.ts".to_string(), + line: 2, + column: 1, + }, + }; + writeln!(file, "{}", serde_json::to_string(&new_entry).unwrap()).unwrap(); + drop(file); + + // Initialize store with 24-hour retention - should cleanup on startup + let store = FileLogStore::new(temp_dir.path().to_path_buf(), 24).unwrap(); + + // Verify old entry was cleaned up + let query = LogQuery { + subgraph_id: DeploymentHash::new("QmTestStartup").unwrap(), + level: None, + from: None, + to: None, + search: None, + first: 100, + skip: 0, + order_direction: super::super::OrderDirection::Desc, + }; + + let result = store.query_logs(query).await.unwrap(); + assert_eq!(result.len(), 1); + assert_eq!(result[0].id, "log-new"); + } } diff --git a/graph/src/components/log_store/mod.rs b/graph/src/components/log_store/mod.rs index e951c37d1df..ec3cbc8c725 100644 --- a/graph/src/components/log_store/mod.rs +++ b/graph/src/components/log_store/mod.rs @@ -4,6 +4,7 @@ pub mod file; pub mod loki; use async_trait::async_trait; +use std::fmt; use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; @@ -50,8 +51,7 @@ pub enum LogStoreConfig { /// File-based logs (JSON lines format) File { directory: PathBuf, - max_file_size: u64, - retention_days: u32, + retention_hours: u32, }, } @@ -91,6 +91,39 @@ impl FromStr for LogLevel { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OrderDirection { + Asc, + Desc, +} + +impl OrderDirection { + pub fn as_str(&self) -> &'static str { + match self { + OrderDirection::Asc => "asc", + OrderDirection::Desc => "desc", + } + } +} + +impl FromStr for OrderDirection { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "asc" | "ascending" => Ok(OrderDirection::Asc), + "desc" | "descending" => Ok(OrderDirection::Desc), + _ => Err(format!("Invalid order direction: {}", s)), + } + } +} + +impl fmt::Display for OrderDirection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + #[derive(Debug, Clone)] pub struct LogMeta { pub module: String, @@ -118,6 +151,7 @@ pub struct LogQuery { pub search: Option, pub first: u32, pub skip: u32, + pub order_direction: OrderDirection, } #[async_trait] @@ -167,12 +201,10 @@ impl LogStoreFactory { LogStoreConfig::File { directory, - max_file_size, - retention_days, + retention_hours, } => Ok(Arc::new(file::FileLogStore::new( directory, - max_file_size, - retention_days, + retention_hours, )?)), } } @@ -282,28 +314,18 @@ impl LogStoreFactory { }) .map(PathBuf::from)?; - // Default: 100MB per file (104857600 bytes) - // Configurable via GRAPH_LOG_STORE_FILE_MAX_SIZE environment variable - let max_file_size = config::read_u64_with_fallback( - &logger, - "GRAPH_LOG_STORE_FILE_MAX_SIZE", - "GRAPH_LOG_FILE_MAX_SIZE", - 100 * 1024 * 1024, - ); - - // Default: 30 days retention - // Configurable via GRAPH_LOG_STORE_FILE_RETENTION_DAYS environment variable - let retention_days = config::read_u32_with_fallback( + // Default: 0 hours (disabled, keep all logs) + // Configurable via GRAPH_LOG_STORE_FILE_RETENTION_HOURS environment variable + let retention_hours = config::read_u32_with_fallback( &logger, - "GRAPH_LOG_STORE_FILE_RETENTION_DAYS", - "GRAPH_LOG_FILE_RETENTION_DAYS", - 30, + "GRAPH_LOG_STORE_FILE_RETENTION_HOURS", + "GRAPH_LOG_FILE_RETENTION_HOURS", + 0, ); Ok(LogStoreConfig::File { directory, - max_file_size, - retention_days, + retention_hours, }) } diff --git a/graph/src/log/factory.rs b/graph/src/log/factory.rs index 58306a448b3..46992a2fea9 100644 --- a/graph/src/log/factory.rs +++ b/graph/src/log/factory.rs @@ -166,16 +166,14 @@ impl LoggerFactory { Some(LogStoreConfig::File { directory, - max_file_size, - retention_days, + retention_hours: _, }) => { // Use File + // Note: Cleanup is handled by FileLogStore on startup based on retention_hours Some(file_logger( FileDrainConfig { directory: directory.clone(), subgraph_id: loc.hash.to_string(), - max_file_size: *max_file_size, - retention_days: *retention_days, }, term_logger.clone(), )) diff --git a/graph/src/log/file.rs b/graph/src/log/file.rs index beb4e218ea4..d81f17899b3 100644 --- a/graph/src/log/file.rs +++ b/graph/src/log/file.rs @@ -16,10 +16,6 @@ pub struct FileDrainConfig { pub directory: PathBuf, /// The subgraph ID used for the log filename pub subgraph_id: String, - /// Maximum file size in bytes - pub max_file_size: u64, - /// Retention period in days - pub retention_days: u32, } /// Log document structure for JSON Lines format @@ -144,8 +140,6 @@ mod tests { let config = FileDrainConfig { directory: temp_dir.path().to_path_buf(), subgraph_id: "QmTest".to_string(), - max_file_size: 1024 * 1024, - retention_days: 30, }; let drain = FileDrain::new(config, error_logger); @@ -195,8 +189,6 @@ mod tests { let config = FileDrainConfig { directory: temp_dir.path().to_path_buf(), subgraph_id: "QmTest".to_string(), - max_file_size: 1024 * 1024, - retention_days: 30, }; let drain = FileDrain::new(config.clone(), error_logger).unwrap(); diff --git a/node/src/launcher.rs b/node/src/launcher.rs index 19e61d5cf10..36571c869cf 100644 --- a/node/src/launcher.rs +++ b/node/src/launcher.rs @@ -505,8 +505,7 @@ pub async fn run( "file" | "files" => opt.log_store_file_dir.clone().map(|directory| { graph::components::log_store::LogStoreConfig::File { directory: std::path::PathBuf::from(directory), - max_file_size: opt.log_store_file_max_size.unwrap_or(100 * 1024 * 1024), - retention_days: opt.log_store_file_retention_days.unwrap_or(30), + retention_hours: opt.log_store_file_retention_hours.unwrap_or(0), } }), diff --git a/node/src/opt.rs b/node/src/opt.rs index 98c4223efe4..947db242ce9 100644 --- a/node/src/opt.rs +++ b/node/src/opt.rs @@ -224,17 +224,11 @@ pub struct Opt { )] pub log_store_file_dir: Option, #[clap( - long = "log-store-file-max-size", - value_name = "BYTES", - help = "Maximum log file size in bytes (default: 104857600 = 100MB)" + long = "log-store-file-retention-hours", + value_name = "HOURS", + help = "Number of hours to retain log files (0 = disabled, keep all logs; default: 0)" )] - pub log_store_file_max_size: Option, - #[clap( - long = "log-store-file-retention-days", - value_name = "DAYS", - help = "Number of days to retain log files (default: 30)" - )] - pub log_store_file_retention_days: Option, + pub log_store_file_retention_hours: Option, // ================================================ // DEPRECATED - OLD ELASTICSEARCH-SPECIFIC ARGS From a97ee5cc66af6e533a5d3e60d336f74fb056bb95 Mon Sep 17 00:00:00 2001 From: Ford Date: Sat, 17 Jan 2026 22:48:29 -0800 Subject: [PATCH 16/28] CLAUDE.md: Add integration test hang handling and port override guidance --- CLAUDE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index edb8dd1ee3c..b8ec983428b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -114,6 +114,8 @@ POSTGRES_TEST_PORT=5432 ETHEREUM_TEST_PORT=8545 IPFS_TEST_PORT=5001 just test-in - Integration tests take significant time (several minutes) - Tests automatically reset the database between runs - Logs are written to `tests/integration-tests/graph-node.log` +- **If a test hangs for >10 minutes**, it's likely stuck - kill with `pkill -9 integration_tests` and check logs +- CI uses the default ports (3011, 3021, 3001) - local development can override with environment variables ### Code Quality From 344270da20d893f450e4032c2d55fa6b50cf94a5 Mon Sep 17 00:00:00 2001 From: Ford Date: Sat, 17 Jan 2026 22:53:37 -0800 Subject: [PATCH 17/28] graph, graphql, tests: Support specifying sort order in _logs queries --- .../src/components/log_store/elasticsearch.rs | 2 +- graph/src/components/log_store/loki.rs | 16 ++++- graph/src/schema/api.rs | 12 ++++ graph/src/schema/logs.graphql | 10 ++++ graphql/src/store/logs.rs | 13 +++++ tests/tests/integration_tests.rs | 58 +++++++++++++++++++ 6 files changed, 108 insertions(+), 3 deletions(-) diff --git a/graph/src/components/log_store/elasticsearch.rs b/graph/src/components/log_store/elasticsearch.rs index 1b3a30e1019..18e891cffd8 100644 --- a/graph/src/components/log_store/elasticsearch.rs +++ b/graph/src/components/log_store/elasticsearch.rs @@ -84,7 +84,7 @@ impl ElasticsearchLogStore { "from": query.skip, "size": query.first, "sort": [ - { "timestamp": { "order": "desc" } } + { "timestamp": { "order": query.order_direction.as_str() } } ] }) } diff --git a/graph/src/components/log_store/loki.rs b/graph/src/components/log_store/loki.rs index e06405feb55..47d3c924cac 100644 --- a/graph/src/components/log_store/loki.rs +++ b/graph/src/components/log_store/loki.rs @@ -55,9 +55,16 @@ impl LokiLogStore { from: &str, to: &str, limit: u32, + order_direction: super::OrderDirection, ) -> Result, LogStoreError> { let url = format!("{}/loki/api/v1/query_range", self.endpoint); + // Map order direction to Loki's direction parameter + let direction = match order_direction { + super::OrderDirection::Desc => "backward", // Most recent first + super::OrderDirection::Asc => "forward", // Oldest first + }; + let mut request = self .client .get(&url) @@ -66,7 +73,7 @@ impl LokiLogStore { ("start", from), ("end", to), ("limit", &limit.to_string()), - ("direction", "backward"), // Most recent first + ("direction", direction), ]) .timeout(Duration::from_secs(10)); @@ -158,7 +165,9 @@ impl LogStore for LokiLogStore { // Execute query with limit + skip to handle pagination let limit = query.first + query.skip; - let mut entries = self.execute_query(&logql_query, from, to, limit).await?; + let mut entries = self + .execute_query(&logql_query, from, to, limit, query.order_direction) + .await?; // Apply skip/first pagination if query.skip > 0 { @@ -239,6 +248,7 @@ mod tests { search: None, first: 100, skip: 0, + order_direction: crate::components::log_store::OrderDirection::Desc, }; let logql = store.build_logql_query(&query); @@ -256,6 +266,7 @@ mod tests { search: None, first: 100, skip: 0, + order_direction: crate::components::log_store::OrderDirection::Desc, }; let logql = store.build_logql_query(&query); @@ -273,6 +284,7 @@ mod tests { search: Some("transaction failed".to_string()), first: 100, skip: 0, + order_direction: crate::components::log_store::OrderDirection::Desc, }; let logql = store.build_logql_query(&query); diff --git a/graph/src/schema/api.rs b/graph/src/schema/api.rs index 4223b556c17..4f31edd318f 100644 --- a/graph/src/schema/api.rs +++ b/graph/src/schema/api.rs @@ -1396,6 +1396,18 @@ fn logs_field() -> s::Field { default_value: Some(s::Value::Int(0.into())), directives: vec![], }, + // orderDirection: OrderDirection (default desc) + s::InputValue { + position: Pos::default(), + description: Some( + "Sort direction for results. Default: desc (newest first)." + .to_string() + ), + name: String::from("orderDirection"), + value_type: s::Type::NamedType(String::from("OrderDirection")), + default_value: Some(s::Value::Enum(String::from("desc"))), + directives: vec![], + }, ], field_type: s::Type::NonNullType(Box::new(s::Type::ListType(Box::new( s::Type::NonNullType(Box::new(s::Type::NamedType(String::from("_Log_")))), diff --git a/graph/src/schema/logs.graphql b/graph/src/schema/logs.graphql index be80e029152..16a12160cd4 100644 --- a/graph/src/schema/logs.graphql +++ b/graph/src/schema/logs.graphql @@ -15,6 +15,16 @@ enum LogLevel { DEBUG } +""" +Sort direction for query results. +""" +enum OrderDirection { + "Ascending order (oldest first for timestamps)" + asc + "Descending order (newest first for timestamps)" + desc +} + """ A log entry emitted by a subgraph during indexing. Logs can be generated by the subgraph's AssemblyScript code using the `log.*` functions. diff --git a/graphql/src/store/logs.rs b/graphql/src/store/logs.rs index 44b60041ffa..489bb145006 100644 --- a/graphql/src/store/logs.rs +++ b/graphql/src/store/logs.rs @@ -63,6 +63,7 @@ pub fn build_log_query( let mut search = None; let mut first = 100; let mut skip = 0; + let mut order_direction = graph::components::log_store::OrderDirection::Desc; // Parse arguments for (name, value) in &field.arguments { @@ -156,6 +157,17 @@ pub fn build_log_query( skip = skip_u32; } } + "orderDirection" => { + if let r::Value::Enum(order_str) = value { + order_direction = order_str.parse().map_err(|e: String| { + QueryExecutionError::InvalidArgumentError( + field.position, + "orderDirection".to_string(), + q::Value::String(e), + ) + })?; + } + } _ => { // Unknown argument, ignore } @@ -170,5 +182,6 @@ pub fn build_log_query( search, first, skip, + order_direction, }) } diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index 941edc5fef6..0d4f8416112 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -1684,6 +1684,64 @@ async fn test_logs_query(ctx: TestContext) -> anyhow::Result<()> { println!("✓ Field selection works correctly - only requested fields returned"); } + // Test 8: Order direction - ascending + let query = r#"{ + _logs(first: 10, orderDirection: asc) { + id + timestamp + } + }"# + .to_string(); + let resp = subgraph.query(&query).await?; + + let logs = resp["data"]["_logs"] + .as_array() + .context("Expected _logs to be an array")?; + + // Verify ascending order (each timestamp >= previous) + if logs.len() > 1 { + for i in 1..logs.len() { + let prev_ts = logs[i - 1]["timestamp"].as_str().unwrap(); + let curr_ts = logs[i]["timestamp"].as_str().unwrap(); + assert!( + curr_ts >= prev_ts, + "Expected ascending order, but {} came before {}", + prev_ts, + curr_ts + ); + } + println!("✓ Ascending order works correctly"); + } + + // Test 9: Order direction - descending (explicit) + let query = r#"{ + _logs(first: 10, orderDirection: desc) { + id + timestamp + } + }"# + .to_string(); + let resp = subgraph.query(&query).await?; + + let logs = resp["data"]["_logs"] + .as_array() + .context("Expected _logs to be an array")?; + + // Verify descending order (each timestamp <= previous) + if logs.len() > 1 { + for i in 1..logs.len() { + let prev_ts = logs[i - 1]["timestamp"].as_str().unwrap(); + let curr_ts = logs[i]["timestamp"].as_str().unwrap(); + assert!( + curr_ts <= prev_ts, + "Expected descending order, but {} came before {}", + prev_ts, + curr_ts + ); + } + println!("✓ Descending order works correctly"); + } + Ok(()) } From ef2b3a1658deba149d527cc86361e6a09cf30760 Mon Sep 17 00:00:00 2001 From: Ford Date: Wed, 21 Jan 2026 08:56:00 -0800 Subject: [PATCH 18/28] graph, tests: Fix clippy warning and update _logs introspection schema Use map_while instead of filter_map on lines() iterator to properly handle read errors, and add missing orderDirection argument to the _logs field in mock introspection JSON. --- graph/src/components/log_store/file.rs | 2 +- store/test-store/tests/graphql/mock_introspection.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/graph/src/components/log_store/file.rs b/graph/src/components/log_store/file.rs index 6564bdb85c0..61bdd789b51 100644 --- a/graph/src/components/log_store/file.rs +++ b/graph/src/components/log_store/file.rs @@ -123,7 +123,7 @@ impl FileLogStore { let kept_entries: Vec = reader .lines() - .filter_map(|line| line.ok()) + .map_while(Result::ok) .filter(|line| { // Parse timestamp from log entry if let Some(entry) = self.parse_line(line) { diff --git a/store/test-store/tests/graphql/mock_introspection.json b/store/test-store/tests/graphql/mock_introspection.json index 6a01143f911..802c423bf63 100644 --- a/store/test-store/tests/graphql/mock_introspection.json +++ b/store/test-store/tests/graphql/mock_introspection.json @@ -848,6 +848,16 @@ "ofType": null }, "defaultValue": "0" + }, + { + "name": "orderDirection", + "description": "Sort direction for results. Default: desc (newest first).", + "type": { + "kind": "ENUM", + "name": "OrderDirection", + "ofType": null + }, + "defaultValue": "desc" } ], "type": { From cbb4a73a331cc6cc0658f848164d048b472b23ff Mon Sep 17 00:00:00 2001 From: Ford Date: Wed, 1 Apr 2026 08:31:49 -0700 Subject: [PATCH 19/28] graph: Replace read_u64/u32_with_fallback w read_parsed_with_fallback --- graph/src/components/log_store/config.rs | 118 ++++++++--------------- graph/src/components/log_store/mod.rs | 4 +- 2 files changed, 40 insertions(+), 82 deletions(-) diff --git a/graph/src/components/log_store/config.rs b/graph/src/components/log_store/config.rs index 84118ca4864..ac77d9fae26 100644 --- a/graph/src/components/log_store/config.rs +++ b/graph/src/components/log_store/config.rs @@ -1,5 +1,6 @@ use slog::{warn, Logger}; use std::env; +use std::str::FromStr; /// Read environment variable with fallback to deprecated key /// @@ -52,39 +53,16 @@ pub fn read_env_with_default( read_env_with_fallback(logger, new_key, old_key).unwrap_or_else(|| default.to_string()) } -/// Parse u64 from environment variable with fallback +/// Parse a value from environment variable with fallback /// -/// Reads an environment variable with fallback support and parses it as a u64. +/// Reads an environment variable with fallback support and parses it using `FromStr`. /// Returns the default value if the variable is not set or cannot be parsed. -/// -/// # Arguments -/// * `logger` - Logger for emitting deprecation warnings -/// * `new_key` - The new environment variable name -/// * `old_key` - The deprecated environment variable name -/// * `default` - Default value to return if parsing fails or neither key is set -/// -/// # Returns -/// The parsed u64 value, or the default if parsing fails or neither key is set -pub fn read_u64_with_fallback(logger: &Logger, new_key: &str, old_key: &str, default: u64) -> u64 { - read_env_with_fallback(logger, new_key, old_key) - .and_then(|s| s.parse().ok()) - .unwrap_or(default) -} - -/// Parse u32 from environment variable with fallback -/// -/// Reads an environment variable with fallback support and parses it as a u32. -/// Returns the default value if the variable is not set or cannot be parsed. -/// -/// # Arguments -/// * `logger` - Logger for emitting deprecation warnings -/// * `new_key` - The new environment variable name -/// * `old_key` - The deprecated environment variable name -/// * `default` - Default value to return if parsing fails or neither key is set -/// -/// # Returns -/// The parsed u32 value, or the default if parsing fails or neither key is set -pub fn read_u32_with_fallback(logger: &Logger, new_key: &str, old_key: &str, default: u32) -> u32 { +pub fn read_parsed_with_fallback( + logger: &Logger, + new_key: &str, + old_key: &str, + default: T, +) -> T { read_env_with_fallback(logger, new_key, old_key) .and_then(|s| s.parse().ok()) .unwrap_or(default) @@ -148,69 +126,49 @@ mod tests { } #[test] - fn test_read_u64_with_fallback() { + fn test_read_parsed_with_fallback() { let logger = crate::log::logger(true); - std::env::set_var("NEW_KEY_U64", "12345"); - let result = read_u64_with_fallback(&logger, "NEW_KEY_U64", "OLD_KEY_U64", 999); + // Test u64 parsing + std::env::set_var("NEW_KEY_PARSED", "12345"); + let result: u64 = + read_parsed_with_fallback(&logger, "NEW_KEY_PARSED", "OLD_KEY_PARSED", 999); assert_eq!(result, 12345); + std::env::remove_var("NEW_KEY_PARSED"); - std::env::remove_var("NEW_KEY_U64"); - - // Test with old key - std::env::set_var("OLD_KEY_U64", "67890"); - let result = read_u64_with_fallback(&logger, "NEW_KEY_U64", "OLD_KEY_U64", 999); - assert_eq!(result, 67890); - - std::env::remove_var("OLD_KEY_U64"); - - // Test with default - let result = read_u64_with_fallback(&logger, "NEW_KEY_U64", "OLD_KEY_U64", 999); - assert_eq!(result, 999); - } - - #[test] - fn test_read_u32_with_fallback() { - let logger = crate::log::logger(true); - std::env::set_var("NEW_KEY_U32", "123"); - - let result = read_u32_with_fallback(&logger, "NEW_KEY_U32", "OLD_KEY_U32", 999); + // Test u32 parsing + std::env::set_var("NEW_KEY_PARSED", "123"); + let result: u32 = + read_parsed_with_fallback(&logger, "NEW_KEY_PARSED", "OLD_KEY_PARSED", 999); assert_eq!(result, 123); + std::env::remove_var("NEW_KEY_PARSED"); - std::env::remove_var("NEW_KEY_U32"); - - // Test with old key - std::env::set_var("OLD_KEY_U32", "456"); - let result = read_u32_with_fallback(&logger, "NEW_KEY_U32", "OLD_KEY_U32", 999); - assert_eq!(result, 456); - - std::env::remove_var("OLD_KEY_U32"); - - // Test with default - let result = read_u32_with_fallback(&logger, "NEW_KEY_U32", "OLD_KEY_U32", 999); - assert_eq!(result, 999); - } - - #[test] - fn test_invalid_u64_uses_default() { - let logger = crate::log::logger(true); - std::env::set_var("NEW_KEY_INVALID", "not_a_number"); + // Test with old key fallback + std::env::set_var("OLD_KEY_PARSED", "67890"); + let result: u64 = + read_parsed_with_fallback(&logger, "NEW_KEY_PARSED", "OLD_KEY_PARSED", 999); + assert_eq!(result, 67890); + std::env::remove_var("OLD_KEY_PARSED"); - let result = read_u64_with_fallback(&logger, "NEW_KEY_INVALID", "OLD_KEY_INVALID", 999); + // Test with default when neither key is set + let result: u64 = + read_parsed_with_fallback(&logger, "NEW_KEY_PARSED", "OLD_KEY_PARSED", 999); assert_eq!(result, 999); - - std::env::remove_var("NEW_KEY_INVALID"); } #[test] - fn test_invalid_u32_uses_default() { + fn test_read_parsed_invalid_uses_default() { let logger = crate::log::logger(true); - std::env::set_var("NEW_KEY_INVALID_U32", "not_a_number"); + std::env::set_var("NEW_KEY_INVALID_PARSED", "not_a_number"); - let result = - read_u32_with_fallback(&logger, "NEW_KEY_INVALID_U32", "OLD_KEY_INVALID_U32", 999); + let result: u64 = read_parsed_with_fallback( + &logger, + "NEW_KEY_INVALID_PARSED", + "OLD_KEY_INVALID_PARSED", + 999, + ); assert_eq!(result, 999); - std::env::remove_var("NEW_KEY_INVALID_U32"); + std::env::remove_var("NEW_KEY_INVALID_PARSED"); } } diff --git a/graph/src/components/log_store/mod.rs b/graph/src/components/log_store/mod.rs index ec3cbc8c725..7820e7fde49 100644 --- a/graph/src/components/log_store/mod.rs +++ b/graph/src/components/log_store/mod.rs @@ -261,7 +261,7 @@ impl LogStoreFactory { // Default: 10 seconds query timeout // Configurable via GRAPH_LOG_STORE_ELASTICSEARCH_TIMEOUT environment variable - let timeout_secs = config::read_u64_with_fallback( + let timeout_secs = config::read_parsed_with_fallback( &logger, "GRAPH_LOG_STORE_ELASTICSEARCH_TIMEOUT", "GRAPH_ELASTICSEARCH_TIMEOUT", @@ -316,7 +316,7 @@ impl LogStoreFactory { // Default: 0 hours (disabled, keep all logs) // Configurable via GRAPH_LOG_STORE_FILE_RETENTION_HOURS environment variable - let retention_hours = config::read_u32_with_fallback( + let retention_hours = config::read_parsed_with_fallback( &logger, "GRAPH_LOG_STORE_FILE_RETENTION_HOURS", "GRAPH_LOG_FILE_RETENTION_HOURS", From 49feba6685b5a5d96f366601681513f4a97b0922 Mon Sep 17 00:00:00 2001 From: Ford Date: Wed, 1 Apr 2026 08:51:08 -0700 Subject: [PATCH 20/28] graph, graphql: Replace custom LogLevel enum with slog::Level --- .../src/components/log_store/elasticsearch.rs | 2 +- graph/src/components/log_store/file.rs | 8 ++-- graph/src/components/log_store/loki.rs | 6 +-- graph/src/components/log_store/mod.rs | 41 ++----------------- graphql/src/store/logs.rs | 4 +- 5 files changed, 13 insertions(+), 48 deletions(-) diff --git a/graph/src/components/log_store/elasticsearch.rs b/graph/src/components/log_store/elasticsearch.rs index 18e891cffd8..6e9113deaa7 100644 --- a/graph/src/components/log_store/elasticsearch.rs +++ b/graph/src/components/log_store/elasticsearch.rs @@ -45,7 +45,7 @@ impl ElasticsearchLogStore { if let Some(level) = &query.level { must_clauses.push(json!({ "term": { - "level": level.as_str() + "level": level.as_str().to_ascii_lowercase() } })); } diff --git a/graph/src/components/log_store/file.rs b/graph/src/components/log_store/file.rs index 61bdd789b51..b3489f96060 100644 --- a/graph/src/components/log_store/file.rs +++ b/graph/src/components/log_store/file.rs @@ -286,8 +286,8 @@ struct FileLogMeta { #[cfg(test)] mod tests { - use super::super::LogLevel; use super::*; + use slog::Level; use std::io::Write; use tempfile::TempDir; @@ -377,7 +377,7 @@ mod tests { assert_eq!(entries.len(), 1); assert_eq!(entries[0].id, "log-1"); assert_eq!(entries[0].text, "Test error message"); - assert_eq!(entries[0].level, LogLevel::Error); + assert_eq!(entries[0].level, Level::Error); } #[tokio::test] @@ -410,7 +410,7 @@ mod tests { // Query for errors only let query = LogQuery { subgraph_id, - level: Some(LogLevel::Error), + level: Some(Level::Error), from: None, to: None, search: None, @@ -424,7 +424,7 @@ mod tests { let entries = result.unwrap(); assert_eq!(entries.len(), 2); - assert!(entries.iter().all(|e| e.level == LogLevel::Error)); + assert!(entries.iter().all(|e| e.level == Level::Error)); } #[tokio::test] diff --git a/graph/src/components/log_store/loki.rs b/graph/src/components/log_store/loki.rs index 47d3c924cac..da5f101ce6c 100644 --- a/graph/src/components/log_store/loki.rs +++ b/graph/src/components/log_store/loki.rs @@ -33,7 +33,7 @@ impl LokiLogStore { // Add log level selector if specified if let Some(level) = &query.level { - selectors.push(format!("level=\"{}\"", level.as_str())); + selectors.push(format!("level=\"{}\"", level.as_str().to_ascii_lowercase())); } // Base selector @@ -234,8 +234,8 @@ struct LokiLogMeta { #[cfg(test)] mod tests { - use super::super::LogLevel; use super::*; + use slog::Level; #[test] fn test_build_logql_query_basic() { @@ -260,7 +260,7 @@ mod tests { let store = LokiLogStore::new("http://localhost:3100".to_string(), None).unwrap(); let query = LogQuery { subgraph_id: DeploymentHash::new("QmTest").unwrap(), - level: Some(LogLevel::Error), + level: Some(Level::Error), from: None, to: None, search: None, diff --git a/graph/src/components/log_store/mod.rs b/graph/src/components/log_store/mod.rs index 7820e7fde49..2d37259a575 100644 --- a/graph/src/components/log_store/mod.rs +++ b/graph/src/components/log_store/mod.rs @@ -4,6 +4,7 @@ pub mod file; pub mod loki; use async_trait::async_trait; +use slog::Level; use std::fmt; use std::path::PathBuf; use std::str::FromStr; @@ -55,42 +56,6 @@ pub enum LogStoreConfig { }, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum LogLevel { - Critical, - Error, - Warning, - Info, - Debug, -} - -impl LogLevel { - pub fn as_str(&self) -> &'static str { - match self { - LogLevel::Critical => "critical", - LogLevel::Error => "error", - LogLevel::Warning => "warning", - LogLevel::Info => "info", - LogLevel::Debug => "debug", - } - } -} - -impl FromStr for LogLevel { - type Err = String; - - fn from_str(s: &str) -> Result { - match s.trim().to_lowercase().as_str() { - "critical" => Ok(LogLevel::Critical), - "error" => Ok(LogLevel::Error), - "warning" => Ok(LogLevel::Warning), - "info" => Ok(LogLevel::Info), - "debug" => Ok(LogLevel::Debug), - _ => Err(format!("Invalid log level: {}", s)), - } - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum OrderDirection { Asc, @@ -136,7 +101,7 @@ pub struct LogEntry { pub id: String, pub subgraph_id: DeploymentHash, pub timestamp: String, - pub level: LogLevel, + pub level: Level, pub text: String, pub arguments: Vec<(String, String)>, pub meta: LogMeta, @@ -145,7 +110,7 @@ pub struct LogEntry { #[derive(Debug, Clone)] pub struct LogQuery { pub subgraph_id: DeploymentHash, - pub level: Option, + pub level: Option, pub from: Option, pub to: Option, pub search: Option, diff --git a/graphql/src/store/logs.rs b/graphql/src/store/logs.rs index 489bb145006..8f505fe14be 100644 --- a/graphql/src/store/logs.rs +++ b/graphql/src/store/logs.rs @@ -70,11 +70,11 @@ pub fn build_log_query( match name.as_str() { "level" => { if let r::Value::Enum(level_str) = value { - level = Some(level_str.parse().map_err(|e: String| { + level = Some(level_str.parse().map_err(|_| { QueryExecutionError::InvalidArgumentError( field.position, "level".to_string(), - q::Value::String(e), + q::Value::String(format!("Invalid log level: {}", level_str)), ) })?); } From 7bbbf9300a75fc1cc8b5942613ba771dc8d04057 Mon Sep 17 00:00:00 2001 From: Ford Date: Wed, 1 Apr 2026 10:05:16 -0700 Subject: [PATCH 21/28] graph: Use slog::Level directly in log drain structs - Replace level: String with level: Level in ElasticLog, FileLogDocument, and LokiLogDocument - Add shared serialize_log_level to common.rs that serializes Level as lowercase - Remove level_to_str() and level_str() --- graph/src/log/common.rs | 23 ++++++++++------------- graph/src/log/elastic.rs | 15 +++------------ graph/src/log/file.rs | 7 ++++--- graph/src/log/loki.rs | 12 ++++++++---- 4 files changed, 25 insertions(+), 32 deletions(-) diff --git a/graph/src/log/common.rs b/graph/src/log/common.rs index 1d6c921015d..ad7e738502c 100644 --- a/graph/src/log/common.rs +++ b/graph/src/log/common.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::fmt; +use serde::ser::Serializer as SerdeSerializer; use serde::Serialize; use slog::*; @@ -105,16 +106,12 @@ pub struct LogMeta { pub column: i64, } -/// Converts an slog Level to a string representation -pub fn level_to_str(level: Level) -> &'static str { - match level { - Level::Critical => "critical", - Level::Error => "error", - Level::Warning => "warning", - Level::Info => "info", - Level::Debug => "debug", - Level::Trace => "trace", - } +/// Serializes an slog Level as a lowercase string +pub fn serialize_log_level(level: &Level, serializer: S) -> std::result::Result +where + S: SerdeSerializer, +{ + serializer.serialize_str(&level.as_str().to_ascii_lowercase()) } /// Builder for common log entry fields across different drain implementations @@ -205,9 +202,9 @@ impl<'a> LogEntryBuilder<'a> { } } - /// Gets the level as a string - pub fn level_str(&self) -> &'static str { - level_to_str(self.record.level()) + /// Gets the log level + pub fn level(&self) -> Level { + self.record.level() } /// Gets the timestamp diff --git a/graph/src/log/elastic.rs b/graph/src/log/elastic.rs index d2506211afd..817b490ca21 100644 --- a/graph/src/log/elastic.rs +++ b/graph/src/log/elastic.rs @@ -9,7 +9,6 @@ use http::header::CONTENT_TYPE; use prometheus::Counter; use reqwest; use reqwest::Client; -use serde::ser::Serializer as SerdeSerializer; use serde::Serialize; use serde_json::json; use slog::*; @@ -31,14 +30,6 @@ pub struct ElasticLoggingConfig { pub client: Client, } -/// Serializes an slog log level using a serde Serializer. -fn serialize_log_level(level: &str, serializer: S) -> Result -where - S: SerdeSerializer, -{ - serializer.serialize_str(level) -} - type ElasticLogMeta = LogMeta; // Log message to be written to Elasticsearch. @@ -51,8 +42,8 @@ struct ElasticLog { arguments: HashMap, timestamp: String, text: String, - #[serde(serialize_with = "serialize_log_level")] - level: String, + #[serde(serialize_with = "super::common::serialize_log_level")] + level: Level, meta: ElasticLogMeta, } @@ -260,7 +251,7 @@ impl Drain for ElasticDrain { arguments: builder.build_arguments_map(), timestamp, text: builder.build_text(), - level: builder.level_str().to_string(), + level: builder.level(), meta: builder.build_meta(), }; diff --git a/graph/src/log/file.rs b/graph/src/log/file.rs index d81f17899b3..a718dd084a7 100644 --- a/graph/src/log/file.rs +++ b/graph/src/log/file.rs @@ -25,7 +25,8 @@ struct FileLogDocument { id: String, subgraph_id: String, timestamp: String, - level: String, + #[serde(serialize_with = "super::common::serialize_log_level")] + level: Level, text: String, arguments: Vec<(String, String)>, meta: FileLogMeta, @@ -83,7 +84,7 @@ impl Drain for FileDrain { id: builder.build_id(), subgraph_id: builder.subgraph_id().to_string(), timestamp: builder.timestamp().to_string(), - level: builder.level_str().to_string(), + level: builder.level(), text: builder.build_text(), arguments: builder.build_arguments_vec(), meta: builder.build_meta(), @@ -161,7 +162,7 @@ mod tests { id: "test-id".to_string(), subgraph_id: "QmTest".to_string(), timestamp: "2024-01-15T10:30:00Z".to_string(), - level: "error".to_string(), + level: Level::Error, text: "Test error message".to_string(), arguments, meta: FileLogMeta { diff --git a/graph/src/log/loki.rs b/graph/src/log/loki.rs index d921546ca26..ebf7f47a332 100644 --- a/graph/src/log/loki.rs +++ b/graph/src/log/loki.rs @@ -34,7 +34,8 @@ struct LokiLogDocument { id: String, subgraph_id: String, timestamp: String, - level: String, + #[serde(serialize_with = "super::common::serialize_log_level")] + level: Level, text: String, arguments: HashMap, meta: LokiLogMeta, @@ -183,7 +184,7 @@ impl Drain for LokiDrain { id: builder.build_id(), subgraph_id: builder.subgraph_id().to_string(), timestamp, - level: builder.level_str().to_string(), + level: builder.level(), text: builder.build_text(), arguments: builder.build_arguments_map(), meta: builder.build_meta(), @@ -201,7 +202,10 @@ impl Drain for LokiDrain { // Build labels for Loki stream let mut labels = HashMap::new(); labels.insert("subgraphId".to_string(), builder.subgraph_id().to_string()); - labels.insert("level".to_string(), builder.level_str().to_string()); + labels.insert( + "level".to_string(), + builder.level().as_str().to_ascii_lowercase(), + ); // Create log entry let entry = LokiLogEntry { @@ -306,7 +310,7 @@ mod tests { id: "test-id".to_string(), subgraph_id: "QmTest".to_string(), timestamp: "2024-01-15T10:30:00Z".to_string(), - level: "error".to_string(), + level: Level::Error, text: "Test error".to_string(), arguments, meta: LokiLogMeta { From 4ca854adca4a6453a01d7fd4becbdb37041f0ee2 Mon Sep 17 00:00:00 2001 From: Ford Date: Wed, 1 Apr 2026 10:05:22 -0700 Subject: [PATCH 22/28] node, graph, tests: Configure log store via graph-node.toml - Add [log_store] section to graph-node.toml as the sole configuration path for log store backends - Remove env var (GRAPH_LOG_STORE_*) and CLI arg (--log-store-*) config paths, LogStoreConfigProvider, and env var helper utilities --- graph/src/components/log_store/config.rs | 174 ----------------- graph/src/components/log_store/mod.rs | 128 ------------- node/resources/tests/full_config.toml | 5 + node/src/config.rs | 75 ++++++++ node/src/launcher.rs | 138 +++----------- node/src/lib.rs | 1 - node/src/log_config_provider.rs | 232 ----------------------- node/src/opt.rs | 90 --------- tests/src/config.rs | 4 +- 9 files changed, 112 insertions(+), 735 deletions(-) delete mode 100644 graph/src/components/log_store/config.rs delete mode 100644 node/src/log_config_provider.rs diff --git a/graph/src/components/log_store/config.rs b/graph/src/components/log_store/config.rs deleted file mode 100644 index ac77d9fae26..00000000000 --- a/graph/src/components/log_store/config.rs +++ /dev/null @@ -1,174 +0,0 @@ -use slog::{warn, Logger}; -use std::env; -use std::str::FromStr; - -/// Read environment variable with fallback to deprecated key -/// -/// This helper function implements backward compatibility for environment variables. -/// It first tries the new key, then falls back to the old (deprecated) key with a warning. -/// -/// # Arguments -/// * `logger` - Logger for emitting deprecation warnings -/// * `new_key` - The new environment variable name -/// * `old_key` - The deprecated environment variable name -/// -/// # Returns -/// The value of the environment variable if found, or None if neither key is set -pub fn read_env_with_fallback(logger: &Logger, new_key: &str, old_key: &str) -> Option { - // Try new key first - if let Ok(value) = env::var(new_key) { - return Some(value); - } - - // Fall back to old key with deprecation warning - if let Ok(value) = env::var(old_key) { - warn!( - logger, - "Using deprecated environment variable '{}', please use '{}' instead", old_key, new_key - ); - return Some(value); - } - - None -} - -/// Read environment variable with default value and fallback -/// -/// Similar to `read_env_with_fallback`, but returns a default value if neither key is set. -/// -/// # Arguments -/// * `logger` - Logger for emitting deprecation warnings -/// * `new_key` - The new environment variable name -/// * `old_key` - The deprecated environment variable name -/// * `default` - Default value to return if neither key is set -/// -/// # Returns -/// The value of the environment variable, or the default if neither key is set -pub fn read_env_with_default( - logger: &Logger, - new_key: &str, - old_key: &str, - default: &str, -) -> String { - read_env_with_fallback(logger, new_key, old_key).unwrap_or_else(|| default.to_string()) -} - -/// Parse a value from environment variable with fallback -/// -/// Reads an environment variable with fallback support and parses it using `FromStr`. -/// Returns the default value if the variable is not set or cannot be parsed. -pub fn read_parsed_with_fallback( - logger: &Logger, - new_key: &str, - old_key: &str, - default: T, -) -> T { - read_env_with_fallback(logger, new_key, old_key) - .and_then(|s| s.parse().ok()) - .unwrap_or(default) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_read_new_key_takes_precedence() { - let logger = crate::log::logger(true); - std::env::set_var("NEW_KEY_PRECEDENCE", "new_value"); - std::env::set_var("OLD_KEY_PRECEDENCE", "old_value"); - - let result = read_env_with_fallback(&logger, "NEW_KEY_PRECEDENCE", "OLD_KEY_PRECEDENCE"); - assert_eq!(result, Some("new_value".to_string())); - - std::env::remove_var("NEW_KEY_PRECEDENCE"); - std::env::remove_var("OLD_KEY_PRECEDENCE"); - } - - #[test] - fn test_read_old_key_when_new_not_present() { - let logger = crate::log::logger(true); - std::env::remove_var("NEW_KEY_FALLBACK"); - std::env::set_var("OLD_KEY_FALLBACK", "old_value"); - - let result = read_env_with_fallback(&logger, "NEW_KEY_FALLBACK", "OLD_KEY_FALLBACK"); - assert_eq!(result, Some("old_value".to_string())); - - std::env::remove_var("OLD_KEY_FALLBACK"); - } - - #[test] - fn test_read_returns_none_when_neither_present() { - let logger = crate::log::logger(true); - std::env::remove_var("NEW_KEY_NONE"); - std::env::remove_var("OLD_KEY_NONE"); - - let result = read_env_with_fallback(&logger, "NEW_KEY_NONE", "OLD_KEY_NONE"); - assert_eq!(result, None); - } - - #[test] - fn test_read_with_default() { - let logger = crate::log::logger(true); - std::env::remove_var("NEW_KEY_DEFAULT"); - std::env::remove_var("OLD_KEY_DEFAULT"); - - let result = read_env_with_default( - &logger, - "NEW_KEY_DEFAULT", - "OLD_KEY_DEFAULT", - "default_value", - ); - assert_eq!(result, "default_value"); - - std::env::remove_var("NEW_KEY_DEFAULT"); - std::env::remove_var("OLD_KEY_DEFAULT"); - } - - #[test] - fn test_read_parsed_with_fallback() { - let logger = crate::log::logger(true); - - // Test u64 parsing - std::env::set_var("NEW_KEY_PARSED", "12345"); - let result: u64 = - read_parsed_with_fallback(&logger, "NEW_KEY_PARSED", "OLD_KEY_PARSED", 999); - assert_eq!(result, 12345); - std::env::remove_var("NEW_KEY_PARSED"); - - // Test u32 parsing - std::env::set_var("NEW_KEY_PARSED", "123"); - let result: u32 = - read_parsed_with_fallback(&logger, "NEW_KEY_PARSED", "OLD_KEY_PARSED", 999); - assert_eq!(result, 123); - std::env::remove_var("NEW_KEY_PARSED"); - - // Test with old key fallback - std::env::set_var("OLD_KEY_PARSED", "67890"); - let result: u64 = - read_parsed_with_fallback(&logger, "NEW_KEY_PARSED", "OLD_KEY_PARSED", 999); - assert_eq!(result, 67890); - std::env::remove_var("OLD_KEY_PARSED"); - - // Test with default when neither key is set - let result: u64 = - read_parsed_with_fallback(&logger, "NEW_KEY_PARSED", "OLD_KEY_PARSED", 999); - assert_eq!(result, 999); - } - - #[test] - fn test_read_parsed_invalid_uses_default() { - let logger = crate::log::logger(true); - std::env::set_var("NEW_KEY_INVALID_PARSED", "not_a_number"); - - let result: u64 = read_parsed_with_fallback( - &logger, - "NEW_KEY_INVALID_PARSED", - "OLD_KEY_INVALID_PARSED", - 999, - ); - assert_eq!(result, 999); - - std::env::remove_var("NEW_KEY_INVALID_PARSED"); - } -} diff --git a/graph/src/components/log_store/mod.rs b/graph/src/components/log_store/mod.rs index 2d37259a575..e8e6118812f 100644 --- a/graph/src/components/log_store/mod.rs +++ b/graph/src/components/log_store/mod.rs @@ -1,4 +1,3 @@ -pub mod config; pub mod elasticsearch; pub mod file; pub mod loki; @@ -173,133 +172,6 @@ impl LogStoreFactory { )?)), } } - - /// Parse configuration from environment variables - /// - /// Supports both new (GRAPH_LOG_STORE_*) and old (deprecated) environment variable names - /// for backward compatibility. The new keys take precedence when both are set. - pub fn from_env() -> Result { - // Logger for deprecation warnings - let logger = crate::log::logger(false); - - // Read backend selector with backward compatibility - let backend = config::read_env_with_default( - &logger, - "GRAPH_LOG_STORE_BACKEND", - "GRAPH_LOG_STORE", - "disabled", - ); - - match backend.to_lowercase().as_str() { - "disabled" | "none" => Ok(LogStoreConfig::Disabled), - - "elasticsearch" | "elastic" | "es" => { - let endpoint = config::read_env_with_fallback( - &logger, - "GRAPH_LOG_STORE_ELASTICSEARCH_URL", - "GRAPH_ELASTICSEARCH_URL", - ) - .ok_or_else(|| { - LogStoreError::ConfigurationError(anyhow::anyhow!( - "Elasticsearch endpoint not set. Use GRAPH_LOG_STORE_ELASTICSEARCH_URL environment variable" - )) - })?; - - let username = config::read_env_with_fallback( - &logger, - "GRAPH_LOG_STORE_ELASTICSEARCH_USER", - "GRAPH_ELASTICSEARCH_USER", - ); - - let password = config::read_env_with_fallback( - &logger, - "GRAPH_LOG_STORE_ELASTICSEARCH_PASSWORD", - "GRAPH_ELASTICSEARCH_PASSWORD", - ); - - let index = config::read_env_with_default( - &logger, - "GRAPH_LOG_STORE_ELASTICSEARCH_INDEX", - "GRAPH_ELASTIC_SEARCH_INDEX", - "subgraph", - ); - - // Default: 10 seconds query timeout - // Configurable via GRAPH_LOG_STORE_ELASTICSEARCH_TIMEOUT environment variable - let timeout_secs = config::read_parsed_with_fallback( - &logger, - "GRAPH_LOG_STORE_ELASTICSEARCH_TIMEOUT", - "GRAPH_ELASTICSEARCH_TIMEOUT", - 10, - ); - - Ok(LogStoreConfig::Elasticsearch { - endpoint, - username, - password, - index, - timeout_secs, - }) - } - - "loki" => { - let endpoint = config::read_env_with_fallback( - &logger, - "GRAPH_LOG_STORE_LOKI_URL", - "GRAPH_LOG_LOKI_ENDPOINT", - ) - .ok_or_else(|| { - LogStoreError::ConfigurationError(anyhow::anyhow!( - "Loki endpoint not set. Use GRAPH_LOG_STORE_LOKI_URL environment variable" - )) - })?; - - let tenant_id = config::read_env_with_fallback( - &logger, - "GRAPH_LOG_STORE_LOKI_TENANT_ID", - "GRAPH_LOG_LOKI_TENANT", - ); - - Ok(LogStoreConfig::Loki { - endpoint, - tenant_id, - }) - } - - "file" | "files" => { - let directory = config::read_env_with_fallback( - &logger, - "GRAPH_LOG_STORE_FILE_DIR", - "GRAPH_LOG_FILE_DIR", - ) - .ok_or_else(|| { - LogStoreError::ConfigurationError(anyhow::anyhow!( - "File log directory not set. Use GRAPH_LOG_STORE_FILE_DIR environment variable" - )) - }) - .map(PathBuf::from)?; - - // Default: 0 hours (disabled, keep all logs) - // Configurable via GRAPH_LOG_STORE_FILE_RETENTION_HOURS environment variable - let retention_hours = config::read_parsed_with_fallback( - &logger, - "GRAPH_LOG_STORE_FILE_RETENTION_HOURS", - "GRAPH_LOG_FILE_RETENTION_HOURS", - 0, - ); - - Ok(LogStoreConfig::File { - directory, - retention_hours, - }) - } - - _ => Err(LogStoreError::ConfigurationError(anyhow::anyhow!( - "Unknown log store backend: {}. Valid options: disabled, elasticsearch, loki, file", - backend - ))), - } - } } /// A no-op LogStore that returns empty results. diff --git a/node/resources/tests/full_config.toml b/node/resources/tests/full_config.toml index 4624af467c7..6292770a087 100644 --- a/node/resources/tests/full_config.toml +++ b/node/resources/tests/full_config.toml @@ -80,3 +80,8 @@ shard = "primary" provider = [ { label = "kovan-0", url = "http://rpc.kovan.io", transport = "ws", features = [] } ] + +[log_store] +backend = "file" +directory = "/var/log/graph-node/subgraph-logs" +retention_hours = 72 diff --git a/node/src/config.rs b/node/src/config.rs index 1da6ce6cd05..5bd3b5b2372 100644 --- a/node/src/config.rs +++ b/node/src/config.rs @@ -82,6 +82,7 @@ pub struct Config { pub stores: BTreeMap, pub chains: ChainSection, pub deployment: Deployment, + pub log_store: Option, } fn validate_name(s: &str) -> Result<()> { @@ -234,6 +235,7 @@ impl Config { stores, chains, deployment, + log_store: None, }) } @@ -270,6 +272,79 @@ pub struct GeneralSection { query: Regex, } +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct LogStoreSection { + pub backend: String, + + // File backend + pub directory: Option, + pub retention_hours: Option, + + // Elasticsearch backend + pub url: Option, + pub username: Option, + pub password: Option, + pub index: Option, + pub timeout_secs: Option, + + // Loki backend (url shared with elasticsearch, differentiated by backend) + pub tenant_id: Option, +} + +impl LogStoreSection { + pub fn to_log_store_config(&self) -> Result { + use graph::components::log_store::LogStoreConfig; + + match self.backend.to_lowercase().as_str() { + "disabled" | "none" => Ok(LogStoreConfig::Disabled), + + "elasticsearch" | "elastic" | "es" => { + let endpoint = self + .url + .clone() + .ok_or_else(|| anyhow!("log_store: 'url' is required for elasticsearch backend"))?; + + Ok(LogStoreConfig::Elasticsearch { + endpoint, + username: self.username.clone(), + password: self.password.clone(), + index: self.index.clone().unwrap_or_else(|| "subgraph".to_string()), + timeout_secs: self.timeout_secs.unwrap_or(10), + }) + } + + "loki" => { + let endpoint = self + .url + .clone() + .ok_or_else(|| anyhow!("log_store: 'url' is required for loki backend"))?; + + Ok(LogStoreConfig::Loki { + endpoint, + tenant_id: self.tenant_id.clone(), + }) + } + + "file" | "files" => { + let directory = self + .directory + .clone() + .ok_or_else(|| anyhow!("log_store: 'directory' is required for file backend"))?; + + Ok(LogStoreConfig::File { + directory: std::path::PathBuf::from(directory), + retention_hours: self.retention_hours.unwrap_or(0), + }) + } + + other => Err(anyhow!( + "log_store: unknown backend '{}'. Valid options: disabled, elasticsearch, loki, file", + other + )), + } + } +} + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Shard { pub connection: String, diff --git a/node/src/launcher.rs b/node/src/launcher.rs index 36571c869cf..6253f2f1f63 100644 --- a/node/src/launcher.rs +++ b/node/src/launcher.rs @@ -37,7 +37,6 @@ use tokio_util::sync::CancellationToken; use crate::config::Config; use crate::helpers::watch_subgraph_updates; -use crate::log_config_provider::{LogStoreConfigProvider, LogStoreConfigSources}; use crate::network_setup::Networks; use crate::opt::Opt; use crate::store_builder::StoreBuilder; @@ -446,117 +445,42 @@ pub async fn run( info!(logger, "Starting up"; "node_id" => &node_id); - // Create log store configuration provider - // Build LogStoreConfig from CLI args with backward compatibility - let cli_config = if let Some(backend) = opt.log_store_backend.as_ref() { - // New generic CLI args used - match backend.to_lowercase().as_str() { - "elasticsearch" | "elastic" | "es" => { - let url = opt - .log_store_elasticsearch_url - .clone() - .or_else(|| { - if opt.elasticsearch_url.is_some() { - warn!( - logger, - "Using deprecated --elasticsearch-url, use --log-store-elasticsearch-url instead" - ); - } - opt.elasticsearch_url.clone() - }); - - url.map(|endpoint| { - let index = opt - .log_store_elasticsearch_index - .clone() - .or_else(|| std::env::var("GRAPH_LOG_STORE_ELASTICSEARCH_INDEX").ok()) - .or_else(|| std::env::var("GRAPH_ELASTIC_SEARCH_INDEX").ok()) - .unwrap_or_else(|| "subgraph".to_string()); - - let timeout_secs = std::env::var("GRAPH_LOG_STORE_ELASTICSEARCH_TIMEOUT") - .or_else(|_| std::env::var("GRAPH_ELASTICSEARCH_TIMEOUT")) - .ok() - .and_then(|s| s.parse::().ok()) - .unwrap_or(10); - - graph::components::log_store::LogStoreConfig::Elasticsearch { - endpoint, - username: opt - .log_store_elasticsearch_user - .clone() - .or_else(|| opt.elasticsearch_user.clone()), - password: opt - .log_store_elasticsearch_password - .clone() - .or_else(|| opt.elasticsearch_password.clone()), - index, - timeout_secs, - } - }) + // Resolve log store configuration from [log_store] TOML section + let (log_store, log_store_config) = match &config.log_store { + Some(section) => match section.to_log_store_config() { + Ok(store_config) => { + let store = graph::components::log_store::LogStoreFactory::from_config( + store_config.clone(), + ) + .unwrap_or_else(|e| { + warn!(logger, "Failed to initialize log store: {}", e); + Arc::new(graph::components::log_store::NoOpLogStore) + }); + info!(logger, "Log store initialized"; "backend" => format!("{:?}", store_config)); + (store, Some(store_config)) } - - "loki" => opt.log_store_loki_url.clone().map(|endpoint| { - graph::components::log_store::LogStoreConfig::Loki { - endpoint, - tenant_id: opt.log_store_loki_tenant_id.clone(), - } - }), - - "file" | "files" => opt.log_store_file_dir.clone().map(|directory| { - graph::components::log_store::LogStoreConfig::File { - directory: std::path::PathBuf::from(directory), - retention_hours: opt.log_store_file_retention_hours.unwrap_or(0), - } - }), - - "disabled" | "none" => None, - - other => { - warn!(logger, "Invalid log store backend: {}", other); - None + Err(e) => { + warn!(logger, "Invalid [log_store] configuration: {}", e); + ( + Arc::new(graph::components::log_store::NoOpLogStore) + as Arc, + None, + ) } + }, + None => { + info!( + logger, + "No [log_store] section in config, log queries will return empty results" + ); + ( + Arc::new(graph::components::log_store::NoOpLogStore) + as Arc, + None, + ) } - } else if opt.elasticsearch_url.is_some() { - // Old Elasticsearch-specific CLI args used (backward compatibility) - warn!( - logger, - "Using deprecated --elasticsearch-url CLI argument, \ - please use --log-store-backend elasticsearch --log-store-elasticsearch-url instead" - ); - - let index = opt - .log_store_elasticsearch_index - .clone() - .or_else(|| std::env::var("GRAPH_LOG_STORE_ELASTICSEARCH_INDEX").ok()) - .or_else(|| std::env::var("GRAPH_ELASTIC_SEARCH_INDEX").ok()) - .unwrap_or_else(|| "subgraph".to_string()); - - let timeout_secs = std::env::var("GRAPH_LOG_STORE_ELASTICSEARCH_TIMEOUT") - .or_else(|_| std::env::var("GRAPH_ELASTICSEARCH_TIMEOUT")) - .ok() - .and_then(|s| s.parse::().ok()) - .unwrap_or(10); - - Some( - graph::components::log_store::LogStoreConfig::Elasticsearch { - endpoint: opt.elasticsearch_url.clone().unwrap(), - username: opt.elasticsearch_user.clone(), - password: opt.elasticsearch_password.clone(), - index, - timeout_secs, - }, - ) - } else { - // No CLI config provided - None }; - let log_config_provider = LogStoreConfigProvider::new(LogStoreConfigSources { cli_config }); - - // Resolve log store (for querying) and config (for drains) - // Priority: GRAPH_LOG_STORE env var → CLI config → NoOp/None - let (log_store, log_store_config) = log_config_provider.resolve(&logger); - // Create a component and subgraph logger factory let logger_factory = LoggerFactory::new(logger.clone(), log_store_config, metrics_registry.clone()); diff --git a/node/src/lib.rs b/node/src/lib.rs index 7344fc89a04..a0fe189f1f7 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -9,7 +9,6 @@ pub mod chain; pub mod config; mod helpers; pub mod launcher; -pub mod log_config_provider; pub mod manager; pub mod network_setup; pub mod opt; diff --git a/node/src/log_config_provider.rs b/node/src/log_config_provider.rs deleted file mode 100644 index 133be35af14..00000000000 --- a/node/src/log_config_provider.rs +++ /dev/null @@ -1,232 +0,0 @@ -use graph::components::log_store::{LogStore, LogStoreConfig, LogStoreFactory, NoOpLogStore}; -use graph::prelude::*; -use slog::{info, warn, Logger}; -use std::sync::Arc; - -/// Configuration sources for log store resolution -pub struct LogStoreConfigSources { - /// Log store config from CLI arguments (any backend) - pub cli_config: Option, -} - -/// Provider for resolving log store configuration from multiple sources -/// -/// It handles multi-source configuration with the following priority: -/// 1. GRAPH_LOG_STORE environment variable (supports all backends) -/// 2. CLI configuration (any backend) -/// 3. NoOp/None (disabled) -pub struct LogStoreConfigProvider { - sources: LogStoreConfigSources, -} - -impl LogStoreConfigProvider { - /// Create a new provider with given configuration sources - pub fn new(sources: LogStoreConfigSources) -> Self { - Self { sources } - } - - /// Resolve and create a LogStore for querying logs - /// - /// Priority: GRAPH_LOG_STORE env var → CLI config → NoOp - pub fn resolve_log_store(&self, logger: &Logger) -> Arc { - // Try GRAPH_LOG_STORE environment variable - match LogStoreFactory::from_env() { - Ok(config) => match LogStoreFactory::from_config(config) { - Ok(store) => { - info!( - logger, - "Log store initialized from GRAPH_LOG_STORE environment variable" - ); - return store; - } - Err(e) => { - warn!( - logger, - "Failed to initialize log store from GRAPH_LOG_STORE: {}, falling back to CLI config", - e - ); - // Fall through to CLI fallback - } - }, - Err(_) => { - // No GRAPH_LOG_STORE env var, fall through to CLI config - } - } - - // Try CLI config - if let Some(cli_store) = self.resolve_cli_store(logger) { - return cli_store; - } - - // Default to NoOp - info!( - logger, - "No log store configured, queries will return empty results" - ); - Arc::new(NoOpLogStore) - } - - /// Resolve LogStoreConfig for drain selection (write side) - /// - /// Priority: GRAPH_LOG_STORE env var → CLI config → None - pub fn resolve_log_store_config(&self, _logger: &Logger) -> Option { - // Try GRAPH_LOG_STORE environment variable - // Note: from_env() returns Ok(Disabled) when GRAPH_LOG_STORE is not set, - // so we need to check if it's actually configured - if let Ok(config) = LogStoreFactory::from_env() { - if !matches!(config, LogStoreConfig::Disabled) { - return Some(config); - } - } - - // Fallback to CLI config (any backend) - self.sources.cli_config.clone() - } - - /// Convenience method: Resolve both log store and config at once - /// - /// This is the primary entry point for most callers, as it resolves both - /// the LogStore (for querying) and LogStoreConfig (for drain selection) - /// in a single call. - pub fn resolve(&self, logger: &Logger) -> (Arc, Option) { - let store = self.resolve_log_store(logger); - let config = self.resolve_log_store_config(logger); - - if let Some(ref cfg) = config { - info!(logger, "Log drain initialized"; "backend" => format!("{:?}", cfg)); - } - - (store, config) - } - - /// Helper: Try to create log store from CLI config (any backend) - fn resolve_cli_store(&self, logger: &Logger) -> Option> { - self.sources.cli_config.as_ref().map(|config| { - match LogStoreFactory::from_config(config.clone()) { - Ok(store) => { - info!(logger, "Log store initialized from CLI configuration"); - store - } - Err(e) => { - warn!( - logger, - "Failed to initialize log store from CLI config: {}, using NoOp", e - ); - Arc::new(NoOpLogStore) - } - } - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_no_config_returns_noop() { - std::env::remove_var("GRAPH_LOG_STORE"); - - let logger = graph::log::logger(true); - let provider = LogStoreConfigProvider::new(LogStoreConfigSources { cli_config: None }); - - let store = provider.resolve_log_store(&logger); - assert!(!store.is_available()); - - let config = provider.resolve_log_store_config(&logger); - assert!(config.is_none()); - } - - #[test] - fn test_elastic_from_cli() { - std::env::remove_var("GRAPH_LOG_STORE"); - - let logger = graph::log::logger(true); - let cli_config = LogStoreConfig::Elasticsearch { - endpoint: "http://localhost:9200".to_string(), - username: Some("user".to_string()), - password: Some("pass".to_string()), - index: "test-index".to_string(), - timeout_secs: 10, - }; - - let provider = LogStoreConfigProvider::new(LogStoreConfigSources { - cli_config: Some(cli_config), - }); - - let config = provider.resolve_log_store_config(&logger); - assert!(config.is_some()); - - if let Some(LogStoreConfig::Elasticsearch { - endpoint, - username, - password, - index, - .. - }) = config - { - assert_eq!(endpoint, "http://localhost:9200"); - assert_eq!(username, Some("user".to_string())); - assert_eq!(password, Some("pass".to_string())); - assert_eq!(index, "test-index"); - } else { - panic!("Expected Elasticsearch config"); - } - } - - #[test] - fn test_resolve_convenience_method() { - std::env::remove_var("GRAPH_LOG_STORE"); - - let logger = graph::log::logger(true); - let cli_config = LogStoreConfig::Elasticsearch { - endpoint: "http://localhost:9200".to_string(), - username: None, - password: None, - index: "test-index".to_string(), - timeout_secs: 10, - }; - - let provider = LogStoreConfigProvider::new(LogStoreConfigSources { - cli_config: Some(cli_config), - }); - - let (_store, config) = provider.resolve(&logger); - assert!(config.is_some()); - - if let Some(LogStoreConfig::Elasticsearch { endpoint, .. }) = config { - assert_eq!(endpoint, "http://localhost:9200"); - } else { - panic!("Expected Elasticsearch config"); - } - } - - #[test] - fn test_loki_from_cli() { - std::env::remove_var("GRAPH_LOG_STORE"); - - let logger = graph::log::logger(true); - let cli_config = LogStoreConfig::Loki { - endpoint: "http://localhost:3100".to_string(), - tenant_id: Some("test-tenant".to_string()), - }; - - let provider = LogStoreConfigProvider::new(LogStoreConfigSources { - cli_config: Some(cli_config), - }); - - let config = provider.resolve_log_store_config(&logger); - assert!(config.is_some()); - - if let Some(LogStoreConfig::Loki { - endpoint, - tenant_id, - }) = config - { - assert_eq!(endpoint, "http://localhost:3100"); - assert_eq!(tenant_id, Some("test-tenant".to_string())); - } else { - panic!("Expected Loki config"); - } - } -} diff --git a/node/src/opt.rs b/node/src/opt.rs index 947db242ce9..dbcd3b68cf9 100644 --- a/node/src/opt.rs +++ b/node/src/opt.rs @@ -165,96 +165,6 @@ pub struct Opt { #[clap(long, help = "Enable debug logging")] pub debug: bool, - // ============================================ - // Log Store Configuration - NEW GENERIC ARGS - // ============================================ - #[clap( - long = "log-store-backend", - value_name = "BACKEND", - help = "Log store backend to use (disabled, elasticsearch, loki, file)" - )] - pub log_store_backend: Option, - - // --- Elasticsearch Configuration --- - #[clap( - long = "log-store-elasticsearch-url", - value_name = "URL", - help = "Elasticsearch URL for log storage" - )] - pub log_store_elasticsearch_url: Option, - #[clap( - long = "log-store-elasticsearch-user", - value_name = "USER", - help = "Elasticsearch username for authentication" - )] - pub log_store_elasticsearch_user: Option, - #[clap( - long = "log-store-elasticsearch-password", - value_name = "PASSWORD", - hide_env_values = true, - help = "Elasticsearch password for authentication" - )] - pub log_store_elasticsearch_password: Option, - #[clap( - long = "log-store-elasticsearch-index", - value_name = "INDEX", - help = "Elasticsearch index name (default: subgraph)" - )] - pub log_store_elasticsearch_index: Option, - - // --- Loki Configuration --- - #[clap( - long = "log-store-loki-url", - value_name = "URL", - help = "Loki URL for log storage" - )] - pub log_store_loki_url: Option, - #[clap( - long = "log-store-loki-tenant-id", - value_name = "TENANT_ID", - help = "Loki tenant ID for multi-tenancy" - )] - pub log_store_loki_tenant_id: Option, - - // --- File Configuration --- - #[clap( - long = "log-store-file-dir", - value_name = "DIR", - help = "Directory for file-based log storage" - )] - pub log_store_file_dir: Option, - #[clap( - long = "log-store-file-retention-hours", - value_name = "HOURS", - help = "Number of hours to retain log files (0 = disabled, keep all logs; default: 0)" - )] - pub log_store_file_retention_hours: Option, - - // ================================================ - // DEPRECATED - OLD ELASTICSEARCH-SPECIFIC ARGS - // ================================================ - #[clap( - long, - value_name = "URL", - env = "ELASTICSEARCH_URL", - help = "DEPRECATED: Use --log-store-elasticsearch-url instead. Elasticsearch service to write subgraph logs to" - )] - pub elasticsearch_url: Option, - #[clap( - long, - value_name = "USER", - env = "ELASTICSEARCH_USER", - help = "DEPRECATED: Use --log-store-elasticsearch-user instead. User to use for Elasticsearch logging" - )] - pub elasticsearch_user: Option, - #[clap( - long, - value_name = "PASSWORD", - env = "ELASTICSEARCH_PASSWORD", - hide_env_values = true, - help = "DEPRECATED: Use --log-store-elasticsearch-password instead. Password to use for Elasticsearch logging" - )] - pub elasticsearch_password: Option, #[clap( long, value_name = "DISABLE_BLOCK_INGESTOR", diff --git a/tests/src/config.rs b/tests/src/config.rs index e6d05ef54cf..2e1fa1a1fde 100644 --- a/tests/src/config.rs +++ b/tests/src/config.rs @@ -233,9 +233,7 @@ impl Config { .stderr(stderr) .args(args.clone()) .env("GRAPH_STORE_WRITE_BATCH_DURATION", "5") - .env("ETHEREUM_REORG_THRESHOLD", "0") - .env("GRAPH_LOG_STORE_BACKEND", "file") - .env("GRAPH_LOG_STORE_FILE_DIR", "/tmp/integration-test-logs"); + .env("ETHEREUM_REORG_THRESHOLD", "0"); status!( "graph-node", From 2285dabe0fb7c408f5a10d718ff095ddf6de1798 Mon Sep 17 00:00:00 2001 From: Ford Date: Wed, 1 Apr 2026 10:15:18 -0700 Subject: [PATCH 23/28] graph, node: Add basic auth support for Loki log store --- graph/src/components/log_store/loki.rs | 25 +++++++++++++++++++++---- graph/src/components/log_store/mod.rs | 8 +++++++- graph/src/log/factory.rs | 4 ++++ graph/src/log/loki.rs | 6 ++++++ node/src/config.rs | 2 ++ 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/graph/src/components/log_store/loki.rs b/graph/src/components/log_store/loki.rs index da5f101ce6c..72b3c588a81 100644 --- a/graph/src/components/log_store/loki.rs +++ b/graph/src/components/log_store/loki.rs @@ -11,11 +11,18 @@ use super::{LogEntry, LogMeta, LogQuery, LogStore, LogStoreError}; pub struct LokiLogStore { endpoint: String, tenant_id: Option, + username: Option, + password: Option, client: Client, } impl LokiLogStore { - pub fn new(endpoint: String, tenant_id: Option) -> Result { + pub fn new( + endpoint: String, + tenant_id: Option, + username: Option, + password: Option, + ) -> Result { let client = Client::builder() .timeout(Duration::from_secs(10)) .build() @@ -24,6 +31,8 @@ impl LokiLogStore { Ok(Self { endpoint, tenant_id, + username, + password, client, }) } @@ -82,6 +91,11 @@ impl LokiLogStore { request = request.header("X-Scope-OrgID", tenant_id); } + // Add basic auth if configured + if let Some(username) = &self.username { + request = request.basic_auth(username, self.password.as_ref()); + } + let response = request.send().await.map_err(|e| { LogStoreError::QueryFailed(anyhow::Error::from(e).context("Loki request failed")) })?; @@ -239,7 +253,8 @@ mod tests { #[test] fn test_build_logql_query_basic() { - let store = LokiLogStore::new("http://localhost:3100".to_string(), None).unwrap(); + let store = + LokiLogStore::new("http://localhost:3100".to_string(), None, None, None).unwrap(); let query = LogQuery { subgraph_id: DeploymentHash::new("QmTest").unwrap(), level: None, @@ -257,7 +272,8 @@ mod tests { #[test] fn test_build_logql_query_with_level() { - let store = LokiLogStore::new("http://localhost:3100".to_string(), None).unwrap(); + let store = + LokiLogStore::new("http://localhost:3100".to_string(), None, None, None).unwrap(); let query = LogQuery { subgraph_id: DeploymentHash::new("QmTest").unwrap(), level: Some(Level::Error), @@ -275,7 +291,8 @@ mod tests { #[test] fn test_build_logql_query_with_text_filter() { - let store = LokiLogStore::new("http://localhost:3100".to_string(), None).unwrap(); + let store = + LokiLogStore::new("http://localhost:3100".to_string(), None, None, None).unwrap(); let query = LogQuery { subgraph_id: DeploymentHash::new("QmTest").unwrap(), level: None, diff --git a/graph/src/components/log_store/mod.rs b/graph/src/components/log_store/mod.rs index e8e6118812f..b1d8dde2f16 100644 --- a/graph/src/components/log_store/mod.rs +++ b/graph/src/components/log_store/mod.rs @@ -46,6 +46,8 @@ pub enum LogStoreConfig { Loki { endpoint: String, tenant_id: Option, + username: Option, + password: Option, }, /// File-based logs (JSON lines format) @@ -161,7 +163,11 @@ impl LogStoreFactory { LogStoreConfig::Loki { endpoint, tenant_id, - } => Ok(Arc::new(loki::LokiLogStore::new(endpoint, tenant_id)?)), + username, + password, + } => Ok(Arc::new(loki::LokiLogStore::new( + endpoint, tenant_id, username, password, + )?)), LogStoreConfig::File { directory, diff --git a/graph/src/log/factory.rs b/graph/src/log/factory.rs index 46992a2fea9..33076d0d576 100644 --- a/graph/src/log/factory.rs +++ b/graph/src/log/factory.rs @@ -151,12 +151,16 @@ impl LoggerFactory { Some(LogStoreConfig::Loki { endpoint, tenant_id, + username, + password, }) => { // Use Loki Some(loki_logger( LokiDrainConfig { endpoint: endpoint.clone(), tenant_id: tenant_id.clone(), + username: username.clone(), + password: password.clone(), flush_interval: Duration::from_secs(5), subgraph_id: loc.hash.to_string(), }, diff --git a/graph/src/log/loki.rs b/graph/src/log/loki.rs index ebf7f47a332..034626ceafa 100644 --- a/graph/src/log/loki.rs +++ b/graph/src/log/loki.rs @@ -15,6 +15,8 @@ use super::common::{create_async_logger, LogEntryBuilder, LogMeta}; pub struct LokiDrainConfig { pub endpoint: String, pub tenant_id: Option, + pub username: Option, + pub password: Option, pub flush_interval: Duration, pub subgraph_id: String, } @@ -138,6 +140,10 @@ impl LokiDrain { request = request.header("X-Scope-OrgID", tenant_id); } + if let Some(ref username) = config.username { + request = request.basic_auth(username, config.password.as_ref()); + } + match request.send().await { Ok(resp) if resp.status().is_success() => { // Success diff --git a/node/src/config.rs b/node/src/config.rs index 5bd3b5b2372..9d0baa8ed1b 100644 --- a/node/src/config.rs +++ b/node/src/config.rs @@ -322,6 +322,8 @@ impl LogStoreSection { Ok(LogStoreConfig::Loki { endpoint, tenant_id: self.tenant_id.clone(), + username: self.username.clone(), + password: self.password.clone(), }) } From 713a5f2ddc34c086f142aab91fc969cc473a913a Mon Sep 17 00:00:00 2001 From: Ford Date: Wed, 1 Apr 2026 10:25:14 -0700 Subject: [PATCH 24/28] graph: Warn on log entry parse failures instead of silently skipping --- .../src/components/log_store/elasticsearch.rs | 20 +++++++++++-- graph/src/components/log_store/file.rs | 28 +++++++++++++++++-- graph/src/components/log_store/loki.rs | 28 +++++++++++++++++-- 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/graph/src/components/log_store/elasticsearch.rs b/graph/src/components/log_store/elasticsearch.rs index 6e9113deaa7..459a48904c4 100644 --- a/graph/src/components/log_store/elasticsearch.rs +++ b/graph/src/components/log_store/elasticsearch.rs @@ -2,6 +2,7 @@ use async_trait::async_trait; use reqwest::Client; use serde::Deserialize; use serde_json::json; +use slog::{warn, Logger}; use std::collections::HashMap; use std::time::Duration; @@ -17,11 +18,13 @@ pub struct ElasticsearchLogStore { client: Client, index: String, timeout: Duration, + logger: Logger, } impl ElasticsearchLogStore { pub fn new(config: ElasticLoggingConfig, index: String, timeout: Duration) -> Self { Self { + logger: crate::log::logger(false), endpoint: config.endpoint, username: config.username, password: config.password, @@ -146,8 +149,21 @@ impl ElasticsearchLogStore { } fn parse_log_entry(&self, source: ElasticsearchLogDocument) -> Option { - let level = source.level.parse().ok()?; - let subgraph_id = DeploymentHash::new(&source.subgraph_id).ok()?; + let level = match source.level.parse() { + Ok(l) => l, + Err(_) => { + warn!(self.logger, "Invalid log level in Elasticsearch entry"; "level" => &source.level); + return None; + } + }; + + let subgraph_id = match DeploymentHash::new(&source.subgraph_id) { + Ok(id) => id, + Err(_) => { + warn!(self.logger, "Invalid subgraph ID in Elasticsearch entry"; "subgraph_id" => &source.subgraph_id); + return None; + } + }; // Convert arguments HashMap to Vec<(String, String)> let arguments: Vec<(String, String)> = source.arguments.into_iter().collect(); diff --git a/graph/src/components/log_store/file.rs b/graph/src/components/log_store/file.rs index b3489f96060..6b2f2a0a7b8 100644 --- a/graph/src/components/log_store/file.rs +++ b/graph/src/components/log_store/file.rs @@ -1,5 +1,6 @@ use async_trait::async_trait; use serde::{Deserialize, Serialize}; +use slog::{warn, Logger}; use std::cmp::Reverse; use std::collections::BinaryHeap; use std::fs::File; @@ -13,6 +14,7 @@ use super::{LogEntry, LogMeta, LogQuery, LogStore, LogStoreError}; pub struct FileLogStore { directory: PathBuf, retention_hours: u32, + logger: Logger, } impl FileLogStore { @@ -24,6 +26,7 @@ impl FileLogStore { let store = Self { directory, retention_hours, + logger: crate::log::logger(false), }; // Run cleanup on startup for all existing log files @@ -53,10 +56,29 @@ impl FileLogStore { /// Parse a JSON line into a LogEntry fn parse_line(&self, line: &str) -> Option { - let doc: FileLogDocument = serde_json::from_str(line).ok()?; + let doc: FileLogDocument = match serde_json::from_str(line) { + Ok(doc) => doc, + Err(e) => { + warn!(self.logger, "Failed to parse log line"; "error" => e.to_string()); + return None; + } + }; - let level = doc.level.parse().ok()?; - let subgraph_id = DeploymentHash::new(&doc.subgraph_id).ok()?; + let level = match doc.level.parse() { + Ok(l) => l, + Err(_) => { + warn!(self.logger, "Invalid log level"; "level" => &doc.level); + return None; + } + }; + + let subgraph_id = match DeploymentHash::new(&doc.subgraph_id) { + Ok(id) => id, + Err(_) => { + warn!(self.logger, "Invalid subgraph ID"; "subgraph_id" => &doc.subgraph_id); + return None; + } + }; Some(LogEntry { id: doc.id, diff --git a/graph/src/components/log_store/loki.rs b/graph/src/components/log_store/loki.rs index 72b3c588a81..f8a7f93e1cb 100644 --- a/graph/src/components/log_store/loki.rs +++ b/graph/src/components/log_store/loki.rs @@ -1,6 +1,7 @@ use async_trait::async_trait; use reqwest::Client; use serde::Deserialize; +use slog::{warn, Logger}; use std::collections::HashMap; use std::time::Duration; @@ -14,6 +15,7 @@ pub struct LokiLogStore { username: Option, password: Option, client: Client, + logger: Logger, } impl LokiLogStore { @@ -34,6 +36,7 @@ impl LokiLogStore { username, password, client, + logger: crate::log::logger(false), }) } @@ -146,10 +149,29 @@ impl LokiLogStore { ) -> Option { // value is [timestamp_ns, log_line] // We expect the log line to be JSON with our log entry structure - let log_data: LokiLogDocument = serde_json::from_str(&value.1).ok()?; + let log_data: LokiLogDocument = match serde_json::from_str(&value.1) { + Ok(doc) => doc, + Err(e) => { + warn!(self.logger, "Failed to parse Loki log entry"; "error" => e.to_string()); + return None; + } + }; + + let level = match log_data.level.parse() { + Ok(l) => l, + Err(_) => { + warn!(self.logger, "Invalid log level in Loki entry"; "level" => &log_data.level); + return None; + } + }; - let level = log_data.level.parse().ok()?; - let subgraph_id = DeploymentHash::new(&log_data.subgraph_id).ok()?; + let subgraph_id = match DeploymentHash::new(&log_data.subgraph_id) { + Ok(id) => id, + Err(_) => { + warn!(self.logger, "Invalid subgraph ID in Loki entry"; "subgraph_id" => &log_data.subgraph_id); + return None; + } + }; Some(LogEntry { id: log_data.id, From 796886c56585a7fb583bafe7247ecb294190e654 Mon Sep 17 00:00:00 2001 From: Ford Date: Wed, 1 Apr 2026 11:05:29 -0700 Subject: [PATCH 25/28] all: Fix build after rebase on master - Use q::Pos::default() instead of Pos::default() in api.rs - Add log_store field to Config constructors in tests - Pass NoOpLogStore to GraphQlRunner in gnd test runner - Update Cargo.lock --- Cargo.lock | 934 +++++++++++++------------------- gnd/src/commands/test/runner.rs | 1 + graph/src/schema/api.rs | 16 +- node/src/config.rs | 2 + 4 files changed, 376 insertions(+), 577 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 037038ddea0..e5110f3676c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,9 +73,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50ab0cd8afe573d1f7dc2353698a51b1f93aec362c8211e28cfd3948c6adba39" +checksum = "4973038846323e4e69a433916522195dce2947770076c03078fc21c80ea0f1c4" dependencies = [ "alloy-consensus", "alloy-contract", @@ -100,9 +100,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.2.33" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e9e31d834fe25fe991b8884e4b9f0e59db4a97d86e05d1464d6899c013cd62" +checksum = "ef3a72a2247c34a8545ee99e562b1b9b69168e5000567257ae51e91b4e6b1193" dependencies = [ "alloy-primitives", "num_enum", @@ -111,9 +111,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f16daaf7e1f95f62c6c3bf8a3fc3d78b08ae9777810c0bb5e94966c7cd57ef0" +checksum = "b0c0dc44157867da82c469c13186015b86abef209bf0e41625e4b68bac61d728" dependencies = [ "alloy-eips", "alloy-primitives", @@ -139,9 +139,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "118998d9015332ab1b4720ae1f1e3009491966a0349938a1f43ff45a8a4c6299" +checksum = "ba4cdb42df3871cd6b346d6a938ec2ba69a9a0f49d1f82714bc5c48349268434" dependencies = [ "alloy-consensus", "alloy-eips", @@ -154,9 +154,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ac9e0c34dc6bce643b182049cdfcca1b8ce7d9c260cbdd561f511873b7e26cd" +checksum = "ca63b7125a981415898ffe2a2a696c83696c9c6bdb1671c8a912946bbd8e49e7" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -173,7 +173,6 @@ dependencies = [ "futures-util", "serde_json", "thiserror 2.0.18", - "tracing", ] [[package]] @@ -191,9 +190,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.5.7" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2db5c583aaef0255aa63a4fe827f826090142528bba48d1bf4119b62780cad" +checksum = "0d48a9101f4a67c22fae57489f1ddf3057b8ab4a368d8eac3be088b6e9d9c9d9" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -254,9 +253,9 @@ dependencies = [ [[package]] name = "alloy-eip7928" -version = "0.3.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" +checksum = "d3231de68d5d6e75332b7489cfcc7f4dfabeba94d990a10e4b923af0e6623540" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -267,9 +266,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ef28c9fdad22d4eec52d894f5f2673a0895f1e5ef196734568e68c0f6caca8" +checksum = "b9f7ef09f21bd1e9cb8a686f168cb4a206646804567f0889eadb8dcc4c9288c8" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -287,13 +286,14 @@ dependencies = [ "serde", "serde_with", "sha2 0.10.9", + "thiserror 2.0.18", ] [[package]] name = "alloy-genesis" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf9480307b09d22876efb67d30cadd9013134c21f3a17ec9f93fd7536d38024" +checksum = "7c9cf3b99f46615fbf7dc1add0c96553abb7bf88fc9ec70dfbe7ad0b47ba7fe8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -306,9 +306,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.5.7" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dbe713da0c737d9e5e387b0ba790eb98b14dd207fe53eef50e19a5a8ec3dac" +checksum = "9914c147bb9b25f440eca68a31dc29f5c22298bfa7754aa802965695384122b0" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -318,9 +318,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422d110f1c40f1f8d0e5562b0b649c35f345fccb7093d9f02729943dcd1eef71" +checksum = "ff42cd777eea61f370c0b10f2648a1c81e0b783066cd7269228aa993afd487f7" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -333,9 +333,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7197a66d94c4de1591cdc16a9bcea5f8cccd0da81b865b49aef97b1b4016e0fa" +checksum = "8cbca04f9b410fdc51aaaf88433cbac761213905a65fe832058bcf6690585762" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -359,9 +359,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb82711d59a43fdfd79727c99f270b974c784ec4eb5728a0d0d22f26716c87ef" +checksum = "42d6d15e069a8b11f56bef2eccbad2a873c6dd4d4c81d04dda29710f5ea52f04" dependencies = [ "alloy-consensus", "alloy-eips", @@ -372,9 +372,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.5.7" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3b431b4e72cd8bd0ec7a50b4be18e73dab74de0dba180eef171055e5d5926e" +checksum = "7db950a29746be9e2f2c6288c8bd7a6202a81f999ce109a2933d2379970ec0fa" dependencies = [ "alloy-rlp", "arbitrary", @@ -384,26 +384,27 @@ dependencies = [ "derive_more", "foldhash 0.2.0", "hashbrown 0.16.1", - "indexmap 2.13.0", + "indexmap 2.11.4", "itoa", "k256", "keccak-asm", "paste", "proptest", - "proptest-derive 0.7.0", + "proptest-derive 0.6.0", "rand 0.9.2", "rapidhash", "ruint", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "sha3", + "tiny-keccak 2.0.2", ] [[package]] name = "alloy-provider" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6b18b929ef1d078b834c3631e9c925177f3b23ddc6fa08a722d13047205876" +checksum = "d181c8cc7cf4805d7e589bf4074d56d55064fa1a979f005a45a62b047616d870" dependencies = [ "alloy-chains", "alloy-consensus", @@ -435,7 +436,7 @@ dependencies = [ "lru", "parking_lot", "pin-project", - "reqwest 0.13.2", + "reqwest", "serde", "serde_json", "thiserror 2.0.18", @@ -447,9 +448,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad54073131e7292d4e03e1aa2287730f737280eb160d8b579fb31939f558c11" +checksum = "e8bd82953194dec221aa4cbbbb0b1e2df46066fe9d0333ac25b43a311e122d13" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -462,7 +463,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tower 0.5.2", "tracing", "wasmtimer", ] @@ -491,9 +492,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fcc9604042ca80bd37aa5e232ea1cd851f337e31e2babbbb345bc0b1c30de3" +checksum = "f2792758a93ae32a32e9047c843d536e1448044f78422d71bf7d7c05149e103f" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -504,12 +505,12 @@ dependencies = [ "alloy-transport-ws", "futures 0.3.31", "pin-project", - "reqwest 0.13.2", + "reqwest", "serde", "serde_json", "tokio", "tokio-stream", - "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tower 0.5.2", "tracing", "url", "wasmtimer", @@ -517,9 +518,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4faad925d3a669ffc15f43b3deec7fbdf2adeb28a4d6f9cf4bc661698c0f8f4b" +checksum = "7bdcbf9dfd5eea8bfeb078b1d906da8cd3a39c4d4dbe7a628025648e323611f6" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -534,9 +535,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47df51bedb3e6062cb9981187a51e86d0d64a4de66eb0855e9efe6574b044ddf" +checksum = "e0a3100b76987c1b1dc81f3abe592b7edc29e92b1242067a69d65e0030b35cf9" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -546,9 +547,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3823026d1ed239a40f12364fac50726c8daf1b6ab8077a97212c5123910429ed" +checksum = "dd720b63f82b457610f2eaaf1f32edf44efffe03ae25d537632e7d23e7929e1a" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -557,9 +558,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2145138f3214928f08cd13da3cb51ef7482b5920d8ac5a02ecd4e38d1a8f6d1e" +checksum = "e1b21e1ad18ff1b31ff1030e046462ab8168cf8894e6778cd805c8bdfe2bd649" dependencies = [ "alloy-primitives", "derive_more", @@ -569,9 +570,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb9b97b6e7965679ad22df297dda809b11cebc13405c1b537e5cffecc95834fa" +checksum = "e4ac61f03f1edabccde1c687b5b25fff28f183afee64eaa2e767def3929e4457" dependencies = [ "alloy-consensus", "alloy-eips", @@ -587,9 +588,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c095f92c4e1ff4981d89e9aa02d5f98c762a1980ab66bec49c44be11349da2" +checksum = "9b2dc411f13092f237d2bf6918caf80977fc2f51485f9b90cb2a2f956912c8c9" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -609,9 +610,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5a4d010f86cd4e01e5205ec273911e538e1738e76d8bafe9ecd245910ea5a3" +checksum = "1ad79f1e27e161943b5a4f99fe5534ef0849876214be411e0032c12f38e94daa" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -623,9 +624,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942d26a2ca8891b26de4a8529d21091e21c1093e27eb99698f1a86405c76b1ff" +checksum = "d459f902a2313737bc66d18ed094c25d2aeb268b74d98c26bbbda2aa44182ab0" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -635,9 +636,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ece63b89294b8614ab3f483560c08d016930f842bf36da56bf0b764a15c11e" +checksum = "e2ce1e0dbf7720eee747700e300c99aac01b1a95bb93f493a01e78ee28bb1a37" dependencies = [ "alloy-primitives", "arbitrary", @@ -647,9 +648,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f447aefab0f1c0649f71edc33f590992d4e122bc35fb9cdbbf67d4421ace85" +checksum = "2425c6f314522c78e8198979c8cbf6769362be4da381d4152ea8eefce383535d" dependencies = [ "alloy-primitives", "async-trait", @@ -662,9 +663,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f721f4bf2e4812e5505aaf5de16ef3065a8e26b9139ac885862d00b5a55a659a" +checksum = "c3ecb71ee53d8d9c3fa7bac17542c8116ebc7a9726c91b1bf333ec3d04f5a789" dependencies = [ "alloy-consensus", "alloy-network", @@ -678,9 +679,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "1.5.7" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab81bab693da9bb79f7a95b64b394718259fdd7e41dceeced4cad57cb71c4f6a" +checksum = "a3b96d5f5890605ba9907ce1e2158e2701587631dc005bfa582cf92dd6f21147" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -692,28 +693,28 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "1.5.7" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489f1620bb7e2483fb5819ed01ab6edc1d2f93939dce35a5695085a1afd1d699" +checksum = "b8247b7cca5cde556e93f8b3882b01dbd272f527836049083d240c57bf7b4c15" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck 0.5.0", - "indexmap 2.13.0", + "indexmap 2.11.4", "proc-macro-error2", "proc-macro2", "quote", - "sha3", "syn 2.0.117", "syn-solidity", + "tiny-keccak 2.0.2", ] [[package]] name = "alloy-sol-macro-input" -version = "1.5.7" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56cef806ad22d4392c5fc83cf8f2089f988eb99c7067b4e0c6f1971fc1cca318" +checksum = "3cd54f38512ac7bae10bbc38480eefb1b9b398ca2ce25db9cc0c048c6411c4f1" dependencies = [ "alloy-json-abi", "const-hex", @@ -729,9 +730,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "1.5.7" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" +checksum = "af67a0b0dcebe14244fc92002cd8d96ecbf65db4639d479f5fcd5805755a4c27" dependencies = [ "serde", "winnow 0.7.13", @@ -739,9 +740,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.5.7" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64612d29379782a5dde6f4b6570d9c756d734d760c0c94c254d361e678a6591f" +checksum = "dc1038284171df8bfd48befc0c7b78f667a7e2be162f45f07bd1c378078ebe58" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -751,9 +752,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8098f965442a9feb620965ba4b4be5e2b320f4ec5a3fff6bfa9e1ff7ef42bed1" +checksum = "fa186e560d523d196580c48bf00f1bf62e63041f28ecf276acc22f8b27bb9f53" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -766,7 +767,7 @@ dependencies = [ "serde_json", "thiserror 2.0.18", "tokio", - "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tower 0.5.2", "tracing", "url", "wasmtimer", @@ -774,25 +775,25 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8597d36d546e1dab822345ad563243ec3920e199322cb554ce56c8ef1a1e2e7" +checksum = "aa501ad58dd20acddbfebc65b52e60f05ebf97c52fa40d1b35e91f5e2da0ad0e" dependencies = [ "alloy-json-rpc", "alloy-transport", "itertools 0.14.0", - "reqwest 0.13.2", + "reqwest", "serde_json", - "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tower 0.5.2", "tracing", "url", ] [[package]] name = "alloy-transport-ipc" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1bd98c3870b8a44b79091dde5216a81d58ffbc1fd8ed61b776f9fee0f3bdf20" +checksum = "c2ef85688e5ac2da72afc804e0a1f153a1f309f05a864b1998bbbed7804dbaab" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -810,20 +811,18 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3ab7a72b180992881acc112628b7668337a19ce15293ee974600ea7b693691" +checksum = "b9f00445db69d63298e2b00a0ea1d859f00e6424a3144ffc5eba9c31da995e16" dependencies = [ "alloy-pubsub", "alloy-transport", "futures 0.3.31", "http 1.4.0", - "rustls", "serde_json", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.26.2", "tracing", - "url", "ws_stream_wasm", ] @@ -849,11 +848,11 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.8.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d69722eddcdf1ce096c3ab66cf8116999363f734eb36fe94a148f4f71c85da84" +checksum = "6fa0c53e8c1e1ef4d01066b01c737fb62fc9397ab52c6e7bb5669f97d281b9bc" dependencies = [ - "darling 0.23.0", + "darling 0.21.3", "proc-macro2", "quote", "syn 2.0.117", @@ -939,7 +938,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.1", ] [[package]] @@ -950,18 +949,18 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" dependencies = [ "derive_arbitrary", ] [[package]] name = "arc-swap" -version = "1.9.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07d1f37ff60921c83bdfc7407723bdefe89b44b98a9b772f225c8f9d67141a6" +checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5" dependencies = [ "rustversion", ] @@ -1351,7 +1350,7 @@ dependencies = [ "arrow-schema", "chrono", "half", - "indexmap 2.13.0", + "indexmap 2.11.4", "itoa", "lexical-core", "memchr", @@ -1443,12 +1442,14 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.41" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" +checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" dependencies = [ - "compression-codecs", - "compression-core", + "brotli", + "flate2", + "futures-core", + "memchr", "pin-project-lite", "tokio", ] @@ -1473,7 +1474,7 @@ dependencies = [ "futures-util", "handlebars", "http 1.4.0", - "indexmap 2.13.0", + "indexmap 2.11.4", "mime", "multer", "num-traits", @@ -1540,7 +1541,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e3ef112905abea9dea592fc868a6873b10ebd3f983e83308f995d6284e9ba41" dependencies = [ "bytes", - "indexmap 2.13.0", + "indexmap 2.11.4", "serde", "serde_json", ] @@ -1677,28 +1678,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" -[[package]] -name = "aws-lc-rs" -version = "1.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.39.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", -] - [[package]] name = "axum" version = "0.8.8" @@ -1728,8 +1707,8 @@ dependencies = [ "sha1 0.10.6", "sync_wrapper", "tokio", - "tokio-tungstenite", - "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tungstenite 0.28.0", + "tower 0.5.2", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", @@ -1861,9 +1840,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bitvec" @@ -1894,15 +1873,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.2" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" dependencies = [ "arrayref", "arrayvec 0.7.4", "cc", "cfg-if 1.0.0", - "constant_time_eq 0.3.1", + "constant_time_eq 0.4.2", + "cpufeatures 0.3.0", ] [[package]] @@ -1937,20 +1917,19 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" dependencies = [ "borsh-derive", - "bytes", "cfg_aliases", ] [[package]] name = "borsh-derive" -version = "1.6.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" dependencies = [ "once_cell", "proc-macro-crate", @@ -2053,9 +2032,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.58" +version = "1.2.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" +checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2" dependencies = [ "find-msvc-tools", "jobserver", @@ -2063,12 +2042,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-if" version = "0.1.10" @@ -2174,15 +2147,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" -[[package]] -name = "cmake" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" -dependencies = [ - "cc", -] - [[package]] name = "cobs" version = "0.2.3" @@ -2209,24 +2173,6 @@ dependencies = [ "tokio-util", ] -[[package]] -name = "compression-codecs" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" -dependencies = [ - "brotli", - "compression-core", - "flate2", - "memchr", -] - -[[package]] -name = "compression-core" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -2313,9 +2259,9 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "constant_time_eq" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" [[package]] name = "convert_case" @@ -2445,7 +2391,7 @@ dependencies = [ "log", "pulley-interpreter", "regalloc2", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "smallvec", "target-lexicon", @@ -2612,7 +2558,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "crossterm_winapi", "derive_more", "document-features", @@ -2755,6 +2701,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", + "serde", "strsim", "syn 2.0.117", ] @@ -2768,7 +2715,6 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "serde", "strsim", "syn 2.0.117", ] @@ -2911,12 +2857,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -3002,7 +2948,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8496eeb328dce26ee9d9b73275d396d9bddb433fa30106cf6056dd8c3c2764c" dependencies = [ "bigdecimal 0.3.1", - "bitflags 2.11.0", + "bitflags 2.9.0", "byteorder", "chrono", "diesel_derives", @@ -3195,9 +3141,9 @@ dependencies = [ [[package]] name = "doctest-file" -version = "1.1.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2db04e74f0a9a93103b50e90b96024c9b2bdca8bce6a632ec71b88736d3d359" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" [[package]] name = "document-features" @@ -3345,9 +3291,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" +checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" dependencies = [ "log", "regex", @@ -3493,9 +3439,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" [[package]] name = "firestorm" @@ -3533,7 +3479,7 @@ version = "25.12.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "rustc_version 0.4.0", ] @@ -3590,12 +3536,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "fsevent-sys" version = "4.1.0" @@ -3738,9 +3678,9 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25234f20a3ec0a962a61770cfe39ecf03cb529a6e474ad8cff025ed497eda557" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "debugid", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "serde_derive", "serde_json", @@ -3805,7 +3745,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" dependencies = [ "fallible-iterator 0.3.0", - "indexmap 2.13.0", + "indexmap 2.11.4", "stable_deref_trait", ] @@ -3879,7 +3819,7 @@ dependencies = [ "pgtemp", "pq-sys", "regex", - "reqwest 0.12.23", + "reqwest", "semver 1.0.27", "serde", "serde_json", @@ -3890,7 +3830,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-util", - "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tower 0.5.2", "url", "walkdir", "wasmparser 0.118.2", @@ -3954,7 +3894,7 @@ dependencies = [ "rand 0.9.2", "redis", "regex", - "reqwest 0.12.23", + "reqwest", "semver 1.0.27", "serde", "serde_derive", @@ -3979,7 +3919,7 @@ dependencies = [ "tokio-retry", "tokio-stream", "tokio-util", - "toml 1.1.1+spec-1.1.0", + "toml 1.1.0+spec-1.1.0", "tonic", "tonic-prost", "tonic-prost-build", @@ -4022,7 +3962,7 @@ dependencies = [ "tokio", "tokio-stream", "tonic-prost-build", - "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tower 0.5.2", ] [[package]] @@ -4068,7 +4008,7 @@ dependencies = [ "tokio", "tokio-retry", "tokio-util", - "tower 0.5.2 (git+https://github.com/tower-rs/tower.git)", + "tower 0.5.3", "tower-test", "wiremock", ] @@ -4181,7 +4121,7 @@ name = "graph-server-index-node" version = "0.42.1" dependencies = [ "async-trait", - "blake3 1.8.2", + "blake3 1.8.4", "git-testament", "graph", "graph-chain-ethereum", @@ -4217,7 +4157,7 @@ dependencies = [ "anyhow", "arrow", "async-trait", - "blake3 1.8.2", + "blake3 1.8.4", "chrono", "clap", "deadpool 0.13.0", @@ -4319,7 +4259,7 @@ dependencies = [ "graphman", "graphman-store", "lazy_static", - "reqwest 0.12.23", + "reqwest", "serde", "serde_json", "slog", @@ -4342,9 +4282,9 @@ dependencies = [ [[package]] name = "graphql-tools" -version = "0.5.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90d90a4b9177dee43c94150e40e134e989ec725604f305f07d55db0d9fd2fe29" +checksum = "9b1ae57fa544e67a661c97805be3e36b97e7d15d65d67bb34beac24d940f987e" dependencies = [ "combine", "itoa", @@ -4354,7 +4294,6 @@ dependencies = [ "serde_json", "serde_with", "thiserror 2.0.18", - "xxhash-rust", ] [[package]] @@ -4380,7 +4319,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.4.0", - "indexmap 2.13.0", + "indexmap 2.11.4", "slab", "tokio", "tokio-util", @@ -4587,9 +4526,9 @@ checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "hybrid-array" -version = "0.4.10" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" +checksum = "1a79f2aff40c18ab8615ddc5caa9eb5b96314aef18fe5823090f204ad988e813" dependencies = [ "typenum", ] @@ -4633,6 +4572,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki-roots 0.26.11", ] [[package]] @@ -4666,14 +4606,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", "http 1.4.0", "http-body", @@ -4682,7 +4621,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.0", "system-configuration", "tokio", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4910,9 +4849,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "arbitrary", "equivalent", @@ -4949,7 +4888,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "inotify-sys", "libc", ] @@ -4969,7 +4908,7 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6654738b8024300cf062d04a1c13c10c8e2cea598ec1c47dc9b6641159429756" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "crossterm", "dyn-clone", "fuzzy-matcher", @@ -4985,9 +4924,9 @@ checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" [[package]] name = "interprocess" -version = "2.4.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6be5e5c847dbdb44564bd85294740d031f4f8aeb3464e5375ef7141f7538db69" +checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d" dependencies = [ "doctest-file", "futures-core", @@ -5079,9 +5018,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "ittapi" @@ -5127,50 +5066,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if 1.0.0", - "combine", - "jni-sys 0.3.1", - "log", - "thiserror 1.0.61", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" -dependencies = [ - "jni-sys 0.4.1", -] - -[[package]] -name = "jni-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" -dependencies = [ - "jni-sys-macros", -] - -[[package]] -name = "jni-sys-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" -dependencies = [ - "quote", - "syn 2.0.117", -] - [[package]] name = "jobserver" version = "0.1.31" @@ -5233,18 +5128,18 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ "cpufeatures 0.2.12", ] [[package]] name = "keccak-asm" -version = "0.1.6" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa468878266ad91431012b3e5ef1bf9b170eab22883503a318d46857afa4579a" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -5386,7 +5281,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "libc", ] @@ -5523,7 +5418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36c791ecdf977c99f45f23280405d7723727470f6689a5e6dbf513ac547ae10d" dependencies = [ "serde", - "toml 0.9.11+spec-1.1.0", + "toml 0.9.12+spec-1.1.0", ] [[package]] @@ -5648,7 +5543,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "fsevent-sys", "inotify", "kqueue", @@ -5699,9 +5594,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-integer" @@ -5749,9 +5644,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.6" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", "rustversion", @@ -5759,9 +5654,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.6" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro2", "quote", @@ -5791,7 +5686,7 @@ checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "crc32fast", "hashbrown 0.15.2", - "indexmap 2.13.0", + "indexmap 2.11.4", "memchr", ] @@ -5818,7 +5713,7 @@ dependencies = [ "percent-encoding", "quick-xml", "rand 0.10.0", - "reqwest 0.12.23", + "reqwest", "ring", "rustls-pki-types", "serde", @@ -5862,7 +5757,7 @@ version = "0.10.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "cfg-if 1.0.0", "foreign-types", "libc", @@ -6090,7 +5985,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", "hashbrown 0.15.2", - "indexmap 2.13.0", + "indexmap 2.11.4", "serde", ] @@ -6350,7 +6245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93980406f12d9f8140ed5abe7155acb10bb1e69ea55c88960b9c2f117445ef96" dependencies = [ "equivalent", - "indexmap 2.13.0", + "indexmap 2.11.4", "serde", ] @@ -6418,7 +6313,7 @@ dependencies = [ "memchr", "parking_lot", "protobuf", - "reqwest 0.12.23", + "reqwest", "thiserror 2.0.18", ] @@ -6430,7 +6325,7 @@ checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.11.0", + "bitflags 2.9.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -6454,9 +6349,9 @@ dependencies = [ [[package]] name = "proptest-derive" -version = "0.7.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" +checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" dependencies = [ "proc-macro2", "quote", @@ -6534,7 +6429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap 2.11.4", "log", "protobuf", "protobuf-support", @@ -6563,11 +6458,11 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.13.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a14896dfa883796f1cb410461aef38810ea05f2b2c33c5aded3649095fdad" +checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "memchr", "unicase", ] @@ -6622,22 +6517,19 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" dependencies = [ "bytes", - "cfg_aliases", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 1.1.0", "rustls", - "socket2", - "thiserror 2.0.18", + "thiserror 1.0.61", "tokio", "tracing", - "web-time", ] [[package]] @@ -6646,13 +6538,12 @@ version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ - "aws-lc-rs", "bytes", "getrandom 0.3.1", "lru-slab", "rand 0.9.2", "ring", - "rustc-hash", + "rustc-hash 2.1.1", "rustls", "rustls-pki-types", "slab", @@ -6664,23 +6555,22 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" dependencies = [ - "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.5.7", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -6798,9 +6688,9 @@ dependencies = [ [[package]] name = "rapidhash" -version = "4.4.1" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" +checksum = "5d8b5b858a440a0bc02625b62dd95131b9201aa9f69f411195dd4a7cfb1de3d7" dependencies = [ "rustversion", ] @@ -6872,7 +6762,7 @@ dependencies = [ "pin-project-lite", "ryu", "sha1_smol", - "socket2", + "socket2 0.6.0", "tokio", "tokio-util", "url", @@ -6894,7 +6784,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", ] [[package]] @@ -6918,15 +6808,15 @@ dependencies = [ "bumpalo", "hashbrown 0.15.2", "log", - "rustc-hash", + "rustc-hash 2.1.1", "smallvec", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -6991,7 +6881,7 @@ dependencies = [ "tokio-native-tls", "tokio-rustls", "tokio-util", - "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tower 0.5.2", "tower-http", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "url", @@ -6999,43 +6889,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", -] - -[[package]] -name = "reqwest" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-core", - "http 1.4.0", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier", - "serde", - "serde_json", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tower-http", - "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", + "webpki-roots 1.0.5", ] [[package]] @@ -7115,9 +6969,15 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "2.1.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc-hex" @@ -7149,7 +7009,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys 0.4.14", @@ -7162,7 +7022,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys 0.9.4", @@ -7175,7 +7035,6 @@ version = "0.23.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" dependencies = [ - "aws-lc-rs", "log", "once_cell", "ring", @@ -7207,7 +7066,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.7.0", + "security-framework 3.2.0", ] [[package]] @@ -7229,40 +7088,12 @@ dependencies = [ "web-time", ] -[[package]] -name = "rustls-platform-verifier" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" -dependencies = [ - "core-foundation 0.10.0", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs 0.8.1", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework 3.7.0", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.60.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - [[package]] name = "rustls-webpki" version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ - "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -7288,9 +7119,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.23" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "same-file" @@ -7376,7 +7207,7 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -7385,11 +7216,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.7.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "core-foundation 0.10.0", "core-foundation-sys", "libc", @@ -7398,9 +7229,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.17.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -7448,9 +7279,9 @@ checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" dependencies = [ "serde_core", "serde_derive", @@ -7458,18 +7289,18 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" dependencies = [ "proc-macro2", "quote", @@ -7527,9 +7358,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" dependencies = [ "serde_core", ] @@ -7556,7 +7387,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.0", + "indexmap 2.11.4", "serde", "serde_derive", "serde_json", @@ -7582,7 +7413,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.11.4", "itoa", "ryu", "serde", @@ -7661,9 +7492,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.6" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cbb88c189d6352cc8ae96a39d19c7ecad8f7330b29461187f2587fdc2988d5" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" dependencies = [ "cc", "cfg-if 1.0.0", @@ -7726,9 +7557,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "simdutf8" @@ -7847,6 +7678,16 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.0" @@ -8065,9 +7906,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.5.7" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53f425ae0b12e2f5ae65542e00898d500d4d318b4baf09f40fd0d410454e9947" +checksum = "5f92d01b5de07eaf324f7fca61cc6bd3d82bbc1de5b6c963e6fe79e86f36580d" dependencies = [ "paste", "proc-macro2", @@ -8097,11 +7938,11 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -8136,14 +7977,15 @@ checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if 1.0.0", "fastrand", + "once_cell", "rustix 0.38.34", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -8269,30 +8111,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -8353,16 +8195,16 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.0", "tokio-macros", "windows-sys 0.61.1", ] [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -8411,7 +8253,7 @@ dependencies = [ "postgres-protocol", "postgres-types", "rand 0.9.2", - "socket2", + "socket2 0.6.0", "tokio", "tokio-util", "whoami", @@ -8465,9 +8307,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.28.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" dependencies = [ "futures-util", "log", @@ -8475,10 +8317,22 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite", + "tungstenite 0.26.2", "webpki-roots 0.26.11", ] +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.28.0", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -8507,12 +8361,12 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.11+spec-1.1.0" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "serde_core", - "serde_spanned 1.1.1", + "serde_spanned 1.1.0", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "winnow 0.7.13", @@ -8520,17 +8374,17 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.1+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "994b95d9e7bae62b34bab0e2a4510b801fa466066a6a8b2b57361fa1eba068ee" +checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.11.4", "serde_core", - "serde_spanned 1.1.1", - "toml_datetime 1.1.1+spec-1.1.0", + "serde_spanned 1.1.0", + "toml_datetime 1.1.0+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.1", + "winnow 1.0.0", ] [[package]] @@ -8553,9 +8407,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.1+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" dependencies = [ "serde_core", ] @@ -8566,7 +8420,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.11.4", "toml_datetime 0.6.6", "winnow 0.5.40", ] @@ -8577,7 +8431,7 @@ version = "0.22.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.11.4", "serde", "serde_spanned 0.6.6", "toml_datetime 0.6.6", @@ -8586,18 +8440,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.1+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ca317ebc49f06bd748bfba29533eac9485569dc9bf80b849024b025e814fb9" +checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" dependencies = [ - "winnow 1.0.1", + "winnow 1.0.0", ] [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" [[package]] name = "tonic" @@ -8620,12 +8474,12 @@ dependencies = [ "percent-encoding", "pin-project", "rustls-native-certs 0.8.1", - "socket2", + "socket2 0.6.0", "sync_wrapper", "tokio", "tokio-rustls", "tokio-stream", - "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tower 0.5.2", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", @@ -8633,9 +8487,9 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.14.5" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1882ac3bf5ef12877d7ed57aad87e75154c11931c2ba7e6cde5e22d63522c734" +checksum = "27aac809edf60b741e2d7db6367214d078856b8a5bff0087e94ff330fb97b6fc" dependencies = [ "prettyplease", "proc-macro2", @@ -8679,7 +8533,7 @@ dependencies = [ "futures-core", "futures-util", "hdrhistogram", - "indexmap 2.13.0", + "indexmap 2.11.4", "pin-project-lite", "slab", "sync_wrapper", @@ -8692,13 +8546,13 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.2" -source = "git+https://github.com/tower-rs/tower.git#1992ebd196467deffe193d5a073db655492ce168" +version = "0.5.3" +source = "git+https://github.com/tower-rs/tower.git#251296dc54a044383dffd16d2179b443e2615672" dependencies = [ "futures-core", "futures-util", "hdrhistogram", - "indexmap 2.13.0", + "indexmap 2.11.4", "pin-project-lite", "slab", "sync_wrapper", @@ -8715,14 +8569,14 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "bytes", "futures-util", "http 1.4.0", "http-body", "iri-string", "pin-project-lite", - "tower 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tower 0.5.2", "tower-layer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -8736,7 +8590,7 @@ checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-layer" version = "0.3.3" -source = "git+https://github.com/tower-rs/tower.git#1992ebd196467deffe193d5a073db655492ce168" +source = "git+https://github.com/tower-rs/tower.git#251296dc54a044383dffd16d2179b443e2615672" [[package]] name = "tower-service" @@ -8747,12 +8601,12 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tower-service" version = "0.3.3" -source = "git+https://github.com/tower-rs/tower.git#1992ebd196467deffe193d5a073db655492ce168" +source = "git+https://github.com/tower-rs/tower.git#251296dc54a044383dffd16d2179b443e2615672" [[package]] name = "tower-test" version = "0.4.1" -source = "git+https://github.com/tower-rs/tower.git#1992ebd196467deffe193d5a073db655492ce168" +source = "git+https://github.com/tower-rs/tower.git#251296dc54a044383dffd16d2179b443e2615672" dependencies = [ "pin-project-lite", "tokio", @@ -8801,9 +8655,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.28.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ "bytes", "data-encoding", @@ -8818,6 +8672,23 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http 1.4.0", + "httparse", + "log", + "rand 0.9.2", + "sha1 0.10.6", + "thiserror 2.0.18", + "utf-8", +] + [[package]] name = "twox-hash" version = "2.1.2" @@ -9184,7 +9055,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap 2.11.4", "wasm-encoder 0.244.0", "wasmparser 0.244.0", ] @@ -9208,7 +9079,7 @@ version = "0.118.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77f1154f1ab868e2a01d9834a805faca7bf8b50d041b4ca714d005d0dab1c50c" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.11.4", "semver 1.0.27", ] @@ -9218,9 +9089,9 @@ version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "hashbrown 0.15.2", - "indexmap 2.13.0", + "indexmap 2.11.4", "semver 1.0.27", "serde", ] @@ -9231,9 +9102,9 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", "hashbrown 0.15.2", - "indexmap 2.13.0", + "indexmap 2.11.4", "semver 1.0.27", ] @@ -9257,7 +9128,7 @@ dependencies = [ "addr2line", "anyhow", "async-trait", - "bitflags 2.11.0", + "bitflags 2.9.0", "bumpalo", "cc", "cfg-if 1.0.0", @@ -9265,7 +9136,7 @@ dependencies = [ "fxprof-processed-profile", "gimli", "hashbrown 0.15.2", - "indexmap 2.13.0", + "indexmap 2.11.4", "ittapi", "libc", "log", @@ -9313,7 +9184,7 @@ dependencies = [ "cranelift-bitset", "cranelift-entity", "gimli", - "indexmap 2.13.0", + "indexmap 2.11.4", "log", "object", "postcard", @@ -9501,9 +9372,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f758625553fe33fdce0713f63bb7784c4f5fecb7f7cd4813414519ec24b6a4c" dependencies = [ "anyhow", - "bitflags 2.11.0", + "bitflags 2.9.0", "heck 0.5.0", - "indexmap 2.13.0", + "indexmap 2.11.4", "wit-parser 0.239.0", ] @@ -9563,29 +9434,20 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-root-certs" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "webpki-roots" version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.6", + "webpki-roots 1.0.5", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" dependencies = [ "rustls-pki-types", ] @@ -9720,15 +9582,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -9774,21 +9627,6 @@ dependencies = [ "windows-link 0.2.0", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.5" @@ -9837,12 +9675,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -9861,12 +9693,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -9885,12 +9711,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -9921,12 +9741,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -9945,12 +9759,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -9969,12 +9777,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -9993,12 +9795,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -10046,9 +9842,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" [[package]] name = "wiremock" @@ -10099,7 +9895,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.9.0", ] [[package]] @@ -10110,7 +9906,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck 0.5.0", - "indexmap 2.13.0", + "indexmap 2.11.4", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -10140,8 +9936,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.0", - "indexmap 2.13.0", + "bitflags 2.9.0", + "indexmap 2.11.4", "log", "serde", "serde_derive", @@ -10160,7 +9956,7 @@ checksum = "55c92c939d667b7bf0c6bf2d1f67196529758f99a2a45a3355cc56964fd5315d" dependencies = [ "anyhow", "id-arena", - "indexmap 2.13.0", + "indexmap 2.11.4", "log", "semver 1.0.27", "serde", @@ -10178,7 +9974,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.13.0", + "indexmap 2.11.4", "log", "semver 1.0.27", "serde", @@ -10230,9 +10026,9 @@ dependencies = [ [[package]] name = "xxhash-rust" -version = "0.8.15" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" +checksum = "63658493314859b4dfdf3fb8c1defd61587839def09582db50b8a4e93afca6bb" [[package]] name = "yansi" @@ -10349,9 +10145,9 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.6.3" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" +checksum = "c745c48e1007337ed136dc99df34128b9faa6ed542d80a1c673cf55a6d7236c8" [[package]] name = "zstd" diff --git a/gnd/src/commands/test/runner.rs b/gnd/src/commands/test/runner.rs index 3d0955b63b7..0fd0e7c7bda 100644 --- a/gnd/src/commands/test/runner.rs +++ b/gnd/src/commands/test/runner.rs @@ -771,6 +771,7 @@ async fn setup_context( stores.network_store.clone(), Arc::new(load_manager), mock_registry.clone(), + Arc::new(graph::components::log_store::NoOpLogStore), )); // Uses PanicSubscriptionManager — tests don't need GraphQL subscriptions. diff --git a/graph/src/schema/api.rs b/graph/src/schema/api.rs index 4f31edd318f..000f24d286a 100644 --- a/graph/src/schema/api.rs +++ b/graph/src/schema/api.rs @@ -1313,7 +1313,7 @@ fn meta_field() -> s::Field { fn logs_field() -> s::Field { lazy_static! { static ref LOGS_FIELD: s::Field = s::Field { - position: Pos::default(), + position: q::Pos::default(), description: Some( "Query execution logs emitted by the subgraph during indexing. \ Results are sorted by timestamp in descending order (newest first)." @@ -1323,7 +1323,7 @@ fn logs_field() -> s::Field { arguments: vec![ // level: LogLevel s::InputValue { - position: Pos::default(), + position: q::Pos::default(), description: Some( "Filter logs by severity level. Only logs at this level will be returned." .to_string() @@ -1335,7 +1335,7 @@ fn logs_field() -> s::Field { }, // from: String (RFC3339 timestamp) s::InputValue { - position: Pos::default(), + position: q::Pos::default(), description: Some( "Filter logs from this timestamp onwards (inclusive). \ Must be in RFC3339 format (e.g., '2024-01-15T10:30:00Z')." @@ -1348,7 +1348,7 @@ fn logs_field() -> s::Field { }, // to: String (RFC3339 timestamp) s::InputValue { - position: Pos::default(), + position: q::Pos::default(), description: Some( "Filter logs until this timestamp (inclusive). \ Must be in RFC3339 format (e.g., '2024-01-15T23:59:59Z')." @@ -1361,7 +1361,7 @@ fn logs_field() -> s::Field { }, // search: String (full-text search) s::InputValue { - position: Pos::default(), + position: q::Pos::default(), description: Some( "Search for logs containing this text in the message. \ Case-insensitive substring match. Maximum length: 1000 characters." @@ -1374,7 +1374,7 @@ fn logs_field() -> s::Field { }, // first: Int (default 100, max 1000) s::InputValue { - position: Pos::default(), + position: q::Pos::default(), description: Some( "Maximum number of logs to return. Default: 100, Maximum: 1000." .to_string() @@ -1386,7 +1386,7 @@ fn logs_field() -> s::Field { }, // skip: Int (default 0, max 10000) s::InputValue { - position: Pos::default(), + position: q::Pos::default(), description: Some( "Number of logs to skip (for pagination). Default: 0, Maximum: 10000." .to_string() @@ -1398,7 +1398,7 @@ fn logs_field() -> s::Field { }, // orderDirection: OrderDirection (default desc) s::InputValue { - position: Pos::default(), + position: q::Pos::default(), description: Some( "Sort direction for results. Default: desc (newest first)." .to_string() diff --git a/node/src/config.rs b/node/src/config.rs index 9d0baa8ed1b..5b38b0c47e2 100644 --- a/node/src/config.rs +++ b/node/src/config.rs @@ -2415,6 +2415,7 @@ fdw_pool_size = [ chains: section, deployment: toml::from_str("[[rule]]\nshards = [\"primary\"]\nindexers = [\"test\"]") .unwrap(), + log_store: None, }; let amp = config.amp_chain_names(); @@ -2457,6 +2458,7 @@ fdw_pool_size = [ "[[rule]]\nshards = [\"primary\"]\nindexers = [\"test\"]", ) .unwrap(), + log_store: None, } }; From fee13b25dd7ed751eb35ab13954ef3e453db0b4e Mon Sep 17 00:00:00 2001 From: Ford Date: Wed, 1 Apr 2026 11:54:21 -0700 Subject: [PATCH 26/28] all: Regenerate pnpm-lock.yaml after rebase --- pnpm-lock.yaml | 945 ++++++++++++++++--------------------------------- 1 file changed, 308 insertions(+), 637 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4470fab39b0..3373b3961da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,76 +6,56 @@ settings: importers: - .: {} + .: + devDependencies: + assemblyscript: + specifier: 0.19.23 + version: 0.19.23 tests/integration-tests/base: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/block-handlers: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/declared-calls-basic: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.97.1 - version: 0.97.1(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@graphprotocol/graph-ts': specifier: 0.33.0 version: 0.33.0 tests/integration-tests/declared-calls-struct-fields: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.97.1 - version: 0.97.1(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@graphprotocol/graph-ts': specifier: 0.33.0 version: 0.33.0 tests/integration-tests/ethereum-api-tests: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.36.0-alpha-20240422133139-8761ea3 version: 0.36.0-alpha-20240422133139-8761ea3 tests/integration-tests/grafted: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/host-exports: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/int8: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 @@ -91,121 +71,91 @@ importers: tests/integration-tests/multiple-subgraph-datasources: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.93.4-alpha-20250105163501-f401d0c57c4ba1f1af95a928d447efd63a56ecdc - version: 0.93.4-alpha-20250105163501-f401d0c57c4ba1f1af95a928d447efd63a56ecdc(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@graphprotocol/graph-ts': specifier: 0.36.0-alpha-20241129215038-b75cda9 version: 0.36.0-alpha-20241129215038-b75cda9 tests/integration-tests/non-fatal-errors: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/overloaded-functions: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/poi-for-failed-subgraph: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/remove-then-update: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/reverted-calls: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/source-subgraph: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.91.0-alpha-20241129215038-b75cda9 - version: 0.91.0-alpha-20241129215038-b75cda9(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.36.0-alpha-20241129215038-b75cda9 version: 0.36.0-alpha-20241129215038-b75cda9 tests/integration-tests/source-subgraph-a: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/source-subgraph-b: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/subgraph-data-sources: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.93.4-alpha-20250105163501-f401d0c57c4ba1f1af95a928d447efd63a56ecdc - version: 0.93.4-alpha-20250105163501-f401d0c57c4ba1f1af95a928d447efd63a56ecdc(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@graphprotocol/graph-ts': specifier: 0.36.0-alpha-20241129215038-b75cda9 version: 0.36.0-alpha-20241129215038-b75cda9 tests/integration-tests/timestamp: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 tests/integration-tests/topic-filter: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.71.0-alpha-20240419180731-51ea29d - version: 0.71.0-alpha-20240419180731-51ea29d(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.35.0 version: 0.35.0 tests/integration-tests/value-roundtrip: devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': specifier: 0.34.0 version: 0.34.0 + tests/runner-tests/aggregation-current-bucket: + devDependencies: + '@graphprotocol/graph-cli': + specifier: 0.98.1 + version: 0.98.1(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@graphprotocol/graph-ts': + specifier: 0.35.0 + version: 0.35.0 + tests/runner-tests/api-version: devDependencies: '@graphprotocol/graph-cli': @@ -317,21 +267,6 @@ importers: specifier: 0.31.0 version: 0.31.0 - tests/runner-tests/logs-query: - devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.69.0 - version: 0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) - '@graphprotocol/graph-ts': - specifier: 0.34.0 - version: 0.34.0 - - tests/runner-tests/substreams: - devDependencies: - '@graphprotocol/graph-cli': - specifier: 0.61.0 - version: 0.61.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10) - tests/runner-tests/typename: devDependencies: '@graphprotocol/graph-cli': @@ -437,33 +372,13 @@ packages: engines: {node: '>=14'} hasBin: true - '@graphprotocol/graph-cli@0.61.0': - resolution: {integrity: sha512-gc3+DioZ/K40sQCt6DsNvbqfPTc9ZysuSz3I9MJ++bD6SftaSSweWwfpPysDMzDuxvUAhLAsJ6QjBACPngT2Kw==} - engines: {node: '>=14'} - hasBin: true - '@graphprotocol/graph-cli@0.69.0': resolution: {integrity: sha512-DoneR0TRkZYumsygdi/RST+OB55TgwmhziredI21lYzfj0QNXGEHZOagTOKeFKDFEpP3KR6BAq6rQIrkprJ1IQ==} engines: {node: '>=18'} hasBin: true - '@graphprotocol/graph-cli@0.71.0-alpha-20240419180731-51ea29d': - resolution: {integrity: sha512-S8TRg4aHzsRQ0I7aJl91d4R2qoPzK0svrRpFcqzZ4AoYr52yBdmPo4yTsSDlB8sQl2zz2e5avJ5r1avU1J7m+g==} - engines: {node: '>=18'} - hasBin: true - - '@graphprotocol/graph-cli@0.91.0-alpha-20241129215038-b75cda9': - resolution: {integrity: sha512-LpfQPjOkCOquTeWqeeC9MJr4eTyKspl2g8u/K8S8qe3SKzMmuHcwQfq/dgBxCbs3m+4vrDYJgDUcQNJ6W5afyw==} - engines: {node: '>=18'} - hasBin: true - - '@graphprotocol/graph-cli@0.93.4-alpha-20250105163501-f401d0c57c4ba1f1af95a928d447efd63a56ecdc': - resolution: {integrity: sha512-+pleAuy1422Q26KCNjMd+DJvjazEb3rSRTM+Y0cRwdMJtl2qcDAXUcg9E/9z+tpCFxx61ujf7T3z04x8Tlq+Lg==} - engines: {node: '>=20.18.1'} - hasBin: true - - '@graphprotocol/graph-cli@0.97.1': - resolution: {integrity: sha512-j5dc2Tl694jMZmVQu8SSl5Yt3VURiBPgglQEpx30aW6UJ89eLR/x46Nn7S6eflV69fmB5IHAuAACnuTzo8MD0Q==} + '@graphprotocol/graph-cli@0.98.1': + resolution: {integrity: sha512-GrWFcRCBlLcRT+gIGundQl7yyrX3YWUPj66bxThKf5CJvvWXdZoNxrj27dMMqulsSwYmpCkb3YmpCiVJFGdpHw==} engines: {node: '>=20.18.1'} hasBin: true @@ -724,16 +639,8 @@ packages: resolution: {integrity: sha512-1QlPaHMhOORySCXkQyzjsIsy2GYTilOw3LkjeHkCgsPJQjAT4IclVytJusWktPbYNys9O+O4V23J44yomQvnBQ==} engines: {node: '>=14.0.0'} - '@oclif/core@4.0.34': - resolution: {integrity: sha512-jHww7lIqyifamynDSjDNNjNOwFTQdKYeOSYaxUaoWhqXnRwacZ+pfUN4Y0L9lqSN4MQtlWM9mwnBD7FvlT9kPw==} - engines: {node: '>=18.0.0'} - - '@oclif/core@4.3.0': - resolution: {integrity: sha512-lIzHY+JMP6evrS5E/sGijNnwrCoNtGy8703jWXcMuPOYKiFhWoAqnIm1BGgoRgmxczkbSfRsHUL/lwsSgh74Lw==} - engines: {node: '>=18.0.0'} - - '@oclif/core@4.5.2': - resolution: {integrity: sha512-eQcKyrEcDYeZJKu4vUWiu0ii/1Gfev6GF4FsLSgNez5/+aQyAUCjg3ZWlurf491WiYZTXCWyKAxyPWk8DKv2MA==} + '@oclif/core@4.5.5': + resolution: {integrity: sha512-iQzlaJQgPeUXrtrX71OzDwxPikQ7c2FhNd8U8rBB7BCtj2XYfmzBT/Hmbc+g9OKDIG/JkbJT0fXaWMMBrhi+1A==} engines: {node: '>=18.0.0'} '@oclif/plugin-autocomplete@2.3.10': @@ -767,8 +674,8 @@ packages: resolution: {integrity: sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg==} engines: {node: '>=10.12.0'} - '@pinax/graph-networks-registry@0.6.7': - resolution: {integrity: sha512-xogeCEZ50XRMxpBwE3TZjJ8RCO8Guv39gDRrrKtlpDEDEMLm0MzD3A0SQObgj7aF7qTZNRTWzsuvQdxgzw25wQ==} + '@pinax/graph-networks-registry@0.7.1': + resolution: {integrity: sha512-Gn2kXRiEd5COAaMY/aDCRO0V+zfb1uQKCu5HFPoWka+EsZW27AlTINA7JctYYYEMuCbjMia5FBOzskjgEvj6LA==} '@pnpm/config.env-replace@1.1.0': resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} @@ -1069,9 +976,6 @@ packages: axios@0.21.4: resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} - axios@0.26.1: - resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1092,11 +996,6 @@ packages: resolution: {integrity: sha512-v7ms6N/H7iciuk6QInon3/n2mu7oRX+6knJ9xFPsJ3rQePgAqcR3CRTwUheFd8SLbiq4LL7Z4G/44L9zscdt9A==} engines: {node: '>=10'} - 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. - binaryen@101.0.0-nightly.20210723: resolution: {integrity: sha512-eioJNqhHlkguVSbblHOtLqlhtC882SOEPKmNFZaDuz1hzQjolxZ+eu3/kaS10n3sGPONsIZsO7R9fR00UyhEUA==} hasBin: true @@ -1160,6 +1059,9 @@ packages: buffer-alloc@1.2.0: resolution: {integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==} + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-fill@1.0.0: resolution: {integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==} @@ -1169,6 +1071,9 @@ packages: buffer-xor@1.0.3: resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -1234,10 +1139,6 @@ packages: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} - chokidar@4.0.1: - resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==} - engines: {node: '>= 14.16.0'} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -1373,8 +1274,8 @@ packages: supports-color: optional: true - debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1382,8 +1283,8 @@ packages: supports-color: optional: true - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1391,6 +1292,26 @@ packages: supports-color: optional: true + decompress-tar@4.1.1: + resolution: {integrity: sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==} + engines: {node: '>=4'} + + decompress-tarbz2@4.1.1: + resolution: {integrity: sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==} + engines: {node: '>=4'} + + decompress-targz@4.1.1: + resolution: {integrity: sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==} + engines: {node: '>=4'} + + decompress-unzip@4.0.1: + resolution: {integrity: sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==} + engines: {node: '>=4'} + + decompress@4.2.1: + resolution: {integrity: sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==} + engines: {node: '>=4'} + default-browser-id@5.0.0: resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} engines: {node: '>=18'} @@ -1406,10 +1327,6 @@ packages: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} - define-lazy-prop@2.0.0: - resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} - engines: {node: '>=8'} - define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} @@ -1441,12 +1358,8 @@ packages: resolution: {integrity: sha512-v5vNLIdUqwj4my80wxFDkNH+4S85zsRuH29SO7dCWVWPCMt/ohZBsGN6g6KXWifT0pzQ7uOxqEKCYCDPJ8Vz4g==} engines: {node: '>= 6.0.0'} - docker-compose@1.1.0: - resolution: {integrity: sha512-VrkQJNafPQ5d6bGULW0P6KqcxSkv3ZU5Wn2wQA19oB71o7+55vQ9ogFe2MMeNbK+jc9rrKVy280DnHO5JLMWOQ==} - engines: {node: '>= 6.0.0'} - - docker-compose@1.2.0: - resolution: {integrity: sha512-wIU1eHk3Op7dFgELRdmOYlPYS4gP8HhH1ZmZa13QZF59y0fblzFDFmKPhyc05phCy2hze9OEvNZAsoljrs+72w==} + docker-compose@1.3.0: + resolution: {integrity: sha512-7Gevk/5eGD50+eMD+XDnFnOrruFkL0kSd7jEG4cjmqweDSUhB7i0g8is/nBdVpl+Bx338SqIB2GLKm32M+Vs6g==} engines: {node: '>= 6.0.0'} docker-modem@1.0.9: @@ -1620,6 +1533,9 @@ packages: fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1629,6 +1545,18 @@ packages: picomatch: optional: true + file-type@3.9.0: + resolution: {integrity: sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==} + engines: {node: '>=0.10.0'} + + file-type@5.2.0: + resolution: {integrity: sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==} + engines: {node: '>=4'} + + file-type@6.2.0: + resolution: {integrity: sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==} + engines: {node: '>=4'} + filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} @@ -1667,12 +1595,8 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} - engines: {node: '>=14.14'} - - fs-extra@11.3.0: - resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} + fs-extra@11.3.2: + resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} engines: {node: '>=14.14'} fs-extra@9.1.0: @@ -1716,6 +1640,10 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-stream@2.3.1: + resolution: {integrity: sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==} + engines: {node: '>=0.10.0'} + get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -1727,13 +1655,8 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - glob@11.0.0: - resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} - engines: {node: 20 || >=22} - hasBin: true - - glob@11.0.2: - resolution: {integrity: sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==} + glob@11.0.3: + resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} engines: {node: 20 || >=22} hasBin: true @@ -1784,10 +1707,6 @@ packages: resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - graphql@16.9.0: - resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==} - engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - har-schema@2.0.0: resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} engines: {node: '>=4'} @@ -1873,11 +1792,8 @@ packages: immutable@4.2.1: resolution: {integrity: sha512-7WYV7Q5BTs0nlQm7tl92rDYYoyELLKHoDMBKhrxEoiV4mrfVdRz8hzPiYOzH7yWjzoVEamxRuAqhxL2PLRwZYQ==} - immutable@5.0.3: - resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==} - - immutable@5.1.2: - resolution: {integrity: sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==} + immutable@5.1.4: + resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -1998,6 +1914,9 @@ packages: resolution: {integrity: sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==} engines: {node: '>=8'} + is-natural-number@4.0.1: + resolution: {integrity: sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -2014,6 +1933,10 @@ packages: resolution: {integrity: sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==} engines: {node: '>=0.10.0'} + is-stream@1.1.0: + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} + engines: {node: '>=0.10.0'} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -2116,11 +2039,6 @@ packages: engines: {node: '>=8'} hasBin: true - jayson@4.1.3: - resolution: {integrity: sha512-LtXh5aYZodBZ9Fc3j6f2w+MTNcnxteMOrb+QgIouguGOulWi0lieEkOUg+HkjjFs0DGoWDds6bi4E9hpNFLulQ==} - engines: {node: '>=8'} - hasBin: true - jayson@4.2.0: resolution: {integrity: sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg==} engines: {node: '>=8'} @@ -2255,6 +2173,10 @@ packages: main-event@1.0.1: resolution: {integrity: sha512-NWtdGrAca/69fm6DIVd8T9rtfDII4Q8NQbIbsKQq2VzS9eqOGYs8uaNQjcuaCq/d9H/o625aOTJX2Qoxzqw0Pw==} + make-dir@1.3.0: + resolution: {integrity: sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==} + engines: {node: '>=4'} + make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -2465,18 +2387,10 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - open@10.1.0: - resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} - engines: {node: '>=18'} - - open@10.1.2: - resolution: {integrity: sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==} + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} engines: {node: '>=18'} - open@8.4.2: - resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} - engines: {node: '>=12'} - ora@4.0.2: resolution: {integrity: sha512-YUOZbamht5mfLxPmk4M35CD/5DuOkAacxlEUbStVXpBAt4fyhBf+vZHI/HRkI++QUp3sNoeA2Gw4C+hi4eGSig==} engines: {node: '>=8'} @@ -2551,6 +2465,9 @@ packages: resolution: {integrity: sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==} engines: {node: '>=0.12'} + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} @@ -2565,6 +2482,22 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + + pinkie-promise@2.0.1: + resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} + engines: {node: '>=0.10.0'} + + pinkie@2.0.4: + resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} + engines: {node: '>=0.10.0'} + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -2583,13 +2516,8 @@ packages: engines: {node: '>=14'} hasBin: true - prettier@3.4.2: - resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} - engines: {node: '>=14'} - hasBin: true - - prettier@3.5.3: - resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true @@ -2599,6 +2527,10 @@ packages: progress-events@1.0.1: resolution: {integrity: sha512-MOzLIwhpt64KIVN64h1MwdKWiyKFNc/S6BoYKPIVUHFg0/eIEyBulhWCgn678v/4c0ri3FdGuzXymNCv02MUIw==} + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + promise@8.3.0: resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==} @@ -2744,6 +2676,10 @@ packages: resolution: {integrity: sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==} engines: {node: '>=18.0.0'} + seek-bzip@1.0.6: + resolution: {integrity: sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==} + hasBin: true + semver@7.3.5: resolution: {integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==} engines: {node: '>=10'} @@ -2754,13 +2690,13 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} hasBin: true - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} hasBin: true @@ -2878,6 +2814,9 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} + strip-dirs@2.1.0: + resolution: {integrity: sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==} + strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -3011,15 +2950,14 @@ packages: uint8arrays@5.1.0: resolution: {integrity: sha512-vA6nFepEmlSKkMBnLBaUMVvAC4G3CTmO58C12y4sq6WPDOR7mOFYOi7GlrQ4djeSbP6JG9Pv9tJDM97PedRSww==} + unbzip2-stream@1.4.3: + resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + undici-types@7.10.0: resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} - undici@7.1.1: - resolution: {integrity: sha512-WZkQ6eH9f5ZT93gaIffsbUaDpBwjbpvmMbfaEhOnbdUneurTESeRxwPGwjI28mRFESH3W3e8Togijh37ptOQqA==} - engines: {node: '>=20.18.1'} - - undici@7.9.0: - resolution: {integrity: sha512-e696y354tf5cFZPXsF26Yg+5M63+5H3oE6Vtkh2oqbvsE2Oe7s2nIbcQh5lmG7Lp/eS29vJtTpw9+p6PX0qNSg==} + undici@7.16.0: + resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} engines: {node: '>=20.18.1'} universalify@2.0.1: @@ -3161,6 +3099,10 @@ packages: utf-8-validate: optional: true + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -3172,13 +3114,8 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} - yaml@2.6.1: - resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} - engines: {node: '>= 14'} - hasBin: true - - yaml@2.8.0: - resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} engines: {node: '>= 14.6'} hasBin: true @@ -3186,6 +3123,9 @@ packages: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -3468,47 +3408,6 @@ snapshots: - typescript - utf-8-validate - '@graphprotocol/graph-cli@0.61.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10)': - dependencies: - '@float-capital/float-subgraph-uncrashable': 0.0.0-internal-testing.5 - '@oclif/core': 2.8.6(@types/node@24.3.0)(typescript@5.9.2) - '@oclif/plugin-autocomplete': 2.3.10(@types/node@24.3.0)(typescript@5.9.2) - '@oclif/plugin-not-found': 2.4.3(@types/node@24.3.0)(typescript@5.9.2) - '@whatwg-node/fetch': 0.8.8 - assemblyscript: 0.19.23 - binary-install-raw: 0.0.13(debug@4.3.4) - chalk: 3.0.0 - chokidar: 3.5.3 - debug: 4.3.4 - docker-compose: 0.23.19 - dockerode: 2.5.8 - fs-extra: 9.1.0 - glob: 9.3.5 - gluegun: 5.1.2(debug@4.3.4) - graphql: 15.5.0 - immutable: 4.2.1 - ipfs-http-client: 55.0.0(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13)) - jayson: 4.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - js-yaml: 3.14.1 - prettier: 1.19.1 - request: 2.88.2 - semver: 7.4.0 - sync-request: 6.1.0 - tmp-promise: 3.0.3 - web3-eth-abi: 1.7.0 - which: 2.0.2 - yaml: 1.10.2 - transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - - '@types/node' - - bufferutil - - encoding - - node-fetch - - supports-color - - typescript - - utf-8-validate - '@graphprotocol/graph-cli@0.69.0(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10)': dependencies: '@float-capital/float-subgraph-uncrashable': 0.0.0-internal-testing.5 @@ -3549,162 +3448,45 @@ snapshots: - typescript - utf-8-validate - '@graphprotocol/graph-cli@0.71.0-alpha-20240419180731-51ea29d(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10)': + '@graphprotocol/graph-cli@0.98.1(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@float-capital/float-subgraph-uncrashable': 0.0.0-internal-testing.5 - '@oclif/core': 2.8.6(@types/node@24.3.0)(typescript@5.9.2) - '@oclif/plugin-autocomplete': 2.3.10(@types/node@24.3.0)(typescript@5.9.2) - '@oclif/plugin-not-found': 2.4.3(@types/node@24.3.0)(typescript@5.9.2) - '@whatwg-node/fetch': 0.8.8 + '@oclif/core': 4.5.5 + '@oclif/plugin-autocomplete': 3.2.34 + '@oclif/plugin-not-found': 3.2.65(@types/node@24.3.0) + '@oclif/plugin-warn-if-update-available': 3.1.46 + '@pinax/graph-networks-registry': 0.7.1 + '@whatwg-node/fetch': 0.10.10 assemblyscript: 0.19.23 - binary-install-raw: 0.0.13(debug@4.3.4) - chalk: 3.0.0 - chokidar: 3.5.3 - debug: 4.3.4 - docker-compose: 0.23.19 - dockerode: 2.5.8 - fs-extra: 9.1.0 - glob: 9.3.5 - gluegun: 5.1.6(debug@4.3.4) - graphql: 15.5.0 - immutable: 4.2.1 - ipfs-http-client: 55.0.0(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13)) - jayson: 4.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - js-yaml: 3.14.1 - prettier: 3.0.3 - semver: 7.4.0 - sync-request: 6.1.0 + chokidar: 4.0.3 + debug: 4.4.3(supports-color@8.1.1) + decompress: 4.2.1 + docker-compose: 1.3.0 + fs-extra: 11.3.2 + glob: 11.0.3 + gluegun: 5.2.0(debug@4.4.3) + graphql: 16.11.0 + immutable: 5.1.4 + jayson: 4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + js-yaml: 4.1.0 + kubo-rpc-client: 5.2.0(undici@7.16.0) + open: 10.2.0 + prettier: 3.6.2 + progress: 2.0.3 + semver: 7.7.3 tmp-promise: 3.0.3 - web3-eth-abi: 1.7.0 - which: 2.0.2 - yaml: 1.10.2 + undici: 7.16.0 + web3-eth-abi: 4.4.1(typescript@5.9.2)(zod@3.25.76) + yaml: 2.8.1 transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - '@types/node' - bufferutil - - encoding - - node-fetch - supports-color - typescript - utf-8-validate + - zod - '@graphprotocol/graph-cli@0.91.0-alpha-20241129215038-b75cda9(@types/node@24.3.0)(bufferutil@4.0.9)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.2)(utf-8-validate@5.0.10)': - dependencies: - '@float-capital/float-subgraph-uncrashable': 0.0.0-internal-testing.5 - '@oclif/core': 2.8.6(@types/node@24.3.0)(typescript@5.9.2) - '@oclif/plugin-autocomplete': 2.3.10(@types/node@24.3.0)(typescript@5.9.2) - '@oclif/plugin-not-found': 2.4.3(@types/node@24.3.0)(typescript@5.9.2) - '@oclif/plugin-warn-if-update-available': 3.1.46 - '@whatwg-node/fetch': 0.8.8 - assemblyscript: 0.19.23 - binary-install-raw: 0.0.13(debug@4.3.4) - chalk: 3.0.0 - chokidar: 3.5.3 - debug: 4.3.4 - docker-compose: 0.23.19 - dockerode: 2.5.8 - fs-extra: 9.1.0 - glob: 9.3.5 - gluegun: 5.1.6(debug@4.3.4) - graphql: 15.5.0 - immutable: 4.2.1 - ipfs-http-client: 55.0.0(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13)) - jayson: 4.0.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - js-yaml: 3.14.1 - open: 8.4.2 - prettier: 3.0.3 - semver: 7.4.0 - sync-request: 6.1.0 - tmp-promise: 3.0.3 - web3-eth-abi: 1.7.0 - which: 2.0.2 - yaml: 1.10.2 - transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - - '@types/node' - - bufferutil - - encoding - - node-fetch - - supports-color - - typescript - - utf-8-validate - - '@graphprotocol/graph-cli@0.93.4-alpha-20250105163501-f401d0c57c4ba1f1af95a928d447efd63a56ecdc(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@float-capital/float-subgraph-uncrashable': 0.0.0-internal-testing.5 - '@oclif/core': 4.0.34 - '@oclif/plugin-autocomplete': 3.2.34 - '@oclif/plugin-not-found': 3.2.65(@types/node@24.3.0) - '@oclif/plugin-warn-if-update-available': 3.1.46 - '@pinax/graph-networks-registry': 0.6.7 - '@whatwg-node/fetch': 0.10.10 - assemblyscript: 0.19.23 - binary-install: 1.1.2(debug@4.3.7) - chokidar: 4.0.1 - debug: 4.3.7(supports-color@8.1.1) - docker-compose: 1.1.0 - fs-extra: 11.2.0 - glob: 11.0.0 - gluegun: 5.2.0(debug@4.3.7) - graphql: 16.9.0 - immutable: 5.0.3 - jayson: 4.1.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - js-yaml: 4.1.0 - kubo-rpc-client: 5.2.0(undici@7.1.1) - open: 10.1.0 - prettier: 3.4.2 - semver: 7.6.3 - tmp-promise: 3.0.3 - undici: 7.1.1 - web3-eth-abi: 4.4.1(typescript@5.9.2)(zod@3.25.76) - yaml: 2.6.1 - transitivePeerDependencies: - - '@types/node' - - bufferutil - - supports-color - - typescript - - utf-8-validate - - zod - - '@graphprotocol/graph-cli@0.97.1(@types/node@24.3.0)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@float-capital/float-subgraph-uncrashable': 0.0.0-internal-testing.5 - '@oclif/core': 4.3.0 - '@oclif/plugin-autocomplete': 3.2.34 - '@oclif/plugin-not-found': 3.2.65(@types/node@24.3.0) - '@oclif/plugin-warn-if-update-available': 3.1.46 - '@pinax/graph-networks-registry': 0.6.7 - '@whatwg-node/fetch': 0.10.10 - assemblyscript: 0.19.23 - chokidar: 4.0.3 - debug: 4.4.1(supports-color@8.1.1) - docker-compose: 1.2.0 - fs-extra: 11.3.0 - glob: 11.0.2 - gluegun: 5.2.0(debug@4.4.1) - graphql: 16.11.0 - immutable: 5.1.2 - jayson: 4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - js-yaml: 4.1.0 - kubo-rpc-client: 5.2.0(undici@7.9.0) - open: 10.1.2 - prettier: 3.5.3 - semver: 7.7.2 - tmp-promise: 3.0.3 - undici: 7.9.0 - web3-eth-abi: 4.4.1(typescript@5.9.2)(zod@3.25.76) - yaml: 2.8.0 - transitivePeerDependencies: - - '@types/node' - - bufferutil - - supports-color - - typescript - - utf-8-validate - - zod - - '@graphprotocol/graph-ts@0.30.0': + '@graphprotocol/graph-ts@0.30.0': dependencies: assemblyscript: 0.19.10 @@ -4103,62 +3885,20 @@ snapshots: - '@types/node' - typescript - '@oclif/core@4.0.34': + '@oclif/core@4.5.5': dependencies: ansi-escapes: 4.3.2 ansis: 3.17.0 clean-stack: 3.0.1 cli-spinners: 2.9.2 - debug: 4.3.7(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) ejs: 3.1.10 get-package-type: 0.1.0 - globby: 11.1.0 indent-string: 4.0.0 is-wsl: 2.2.0 lilconfig: 3.1.3 minimatch: 9.0.5 - semver: 7.6.3 - string-width: 4.2.3 - supports-color: 8.1.1 - widest-line: 3.1.0 - wordwrap: 1.0.0 - wrap-ansi: 7.0.0 - - '@oclif/core@4.3.0': - dependencies: - ansi-escapes: 4.3.2 - ansis: 3.17.0 - clean-stack: 3.0.1 - cli-spinners: 2.9.2 - debug: 4.4.1(supports-color@8.1.1) - ejs: 3.1.10 - get-package-type: 0.1.0 - globby: 11.1.0 - indent-string: 4.0.0 - is-wsl: 2.2.0 - lilconfig: 3.1.3 - minimatch: 9.0.5 - semver: 7.7.2 - string-width: 4.2.3 - supports-color: 8.1.1 - widest-line: 3.1.0 - wordwrap: 1.0.0 - wrap-ansi: 7.0.0 - - '@oclif/core@4.5.2': - dependencies: - ansi-escapes: 4.3.2 - ansis: 3.17.0 - clean-stack: 3.0.1 - cli-spinners: 2.9.2 - debug: 4.4.1(supports-color@8.1.1) - ejs: 3.1.10 - get-package-type: 0.1.0 - indent-string: 4.0.0 - is-wsl: 2.2.0 - lilconfig: 3.1.3 - minimatch: 9.0.5 - semver: 7.7.2 + semver: 7.7.3 string-width: 4.2.3 supports-color: 8.1.1 tinyglobby: 0.2.14 @@ -4180,9 +3920,9 @@ snapshots: '@oclif/plugin-autocomplete@3.2.34': dependencies: - '@oclif/core': 4.0.34 + '@oclif/core': 4.5.5 ansis: 3.17.0 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) ejs: 3.1.10 transitivePeerDependencies: - supports-color @@ -4201,7 +3941,7 @@ snapshots: '@oclif/plugin-not-found@3.2.65(@types/node@24.3.0)': dependencies: '@inquirer/prompts': 7.8.3(@types/node@24.3.0) - '@oclif/core': 4.5.2 + '@oclif/core': 4.5.5 ansis: 3.17.0 fast-levenshtein: 3.0.0 transitivePeerDependencies: @@ -4209,9 +3949,9 @@ snapshots: '@oclif/plugin-warn-if-update-available@3.1.46': dependencies: - '@oclif/core': 4.0.34 + '@oclif/core': 4.5.5 ansis: 3.17.0 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) http-call: 5.3.0 lodash: 4.17.21 registry-auth-token: 5.1.0 @@ -4236,7 +3976,7 @@ snapshots: tslib: 2.8.1 webcrypto-core: 1.8.1 - '@pinax/graph-networks-registry@0.6.7': {} + '@pinax/graph-networks-registry@0.7.1': {} '@pnpm/config.env-replace@1.1.0': {} @@ -4470,15 +4210,9 @@ snapshots: transitivePeerDependencies: - debug - apisauce@2.1.6(debug@4.3.7): - dependencies: - axios: 0.21.4(debug@4.3.7) - transitivePeerDependencies: - - debug - - apisauce@2.1.6(debug@4.4.1): + apisauce@2.1.6(debug@4.4.3): dependencies: - axios: 0.21.4(debug@4.4.1) + axios: 0.21.4(debug@4.4.3) transitivePeerDependencies: - debug @@ -4541,21 +4275,9 @@ snapshots: transitivePeerDependencies: - debug - axios@0.21.4(debug@4.3.7): - dependencies: - follow-redirects: 1.15.11(debug@4.3.7) - transitivePeerDependencies: - - debug - - axios@0.21.4(debug@4.4.1): + axios@0.21.4(debug@4.4.3): dependencies: - follow-redirects: 1.15.11(debug@4.4.1) - transitivePeerDependencies: - - debug - - axios@0.26.1(debug@4.3.7): - dependencies: - follow-redirects: 1.15.11(debug@4.3.7) + follow-redirects: 1.15.11(debug@4.4.3) transitivePeerDependencies: - debug @@ -4581,14 +4303,6 @@ snapshots: transitivePeerDependencies: - debug - binary-install@1.1.2(debug@4.3.7): - dependencies: - axios: 0.26.1(debug@4.3.7) - rimraf: 3.0.2 - tar: 6.2.1 - transitivePeerDependencies: - - debug - binaryen@101.0.0-nightly.20210723: {} binaryen@102.0.0-nightly.20211028: {} @@ -4659,12 +4373,19 @@ snapshots: buffer-alloc-unsafe: 1.1.0 buffer-fill: 1.0.0 + buffer-crc32@0.2.13: {} + buffer-fill@1.0.0: {} buffer-from@1.1.2: {} buffer-xor@1.0.3: {} + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + buffer@6.0.3: dependencies: base64-js: 1.5.1 @@ -4743,10 +4464,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chokidar@4.0.1: - dependencies: - readdirp: 4.1.2 - chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -4888,18 +4605,56 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.3.7(supports-color@8.1.1): + debug@4.4.1(supports-color@8.1.1): dependencies: ms: 2.1.3 optionalDependencies: supports-color: 8.1.1 - debug@4.4.1(supports-color@8.1.1): + debug@4.4.3(supports-color@8.1.1): dependencies: ms: 2.1.3 optionalDependencies: supports-color: 8.1.1 + decompress-tar@4.1.1: + dependencies: + file-type: 5.2.0 + is-stream: 1.1.0 + tar-stream: 1.6.2 + + decompress-tarbz2@4.1.1: + dependencies: + decompress-tar: 4.1.1 + file-type: 6.2.0 + is-stream: 1.1.0 + seek-bzip: 1.0.6 + unbzip2-stream: 1.4.3 + + decompress-targz@4.1.1: + dependencies: + decompress-tar: 4.1.1 + file-type: 5.2.0 + is-stream: 1.1.0 + + decompress-unzip@4.0.1: + dependencies: + file-type: 3.9.0 + get-stream: 2.3.1 + pify: 2.3.0 + yauzl: 2.10.0 + + decompress@4.2.1: + dependencies: + decompress-tar: 4.1.1 + decompress-tarbz2: 4.1.1 + decompress-targz: 4.1.1 + decompress-unzip: 4.0.1 + graceful-fs: 4.2.11 + make-dir: 1.3.0 + pify: 2.3.0 + strip-dirs: 2.1.0 + default-browser-id@5.0.0: {} default-browser@5.2.1: @@ -4917,8 +4672,6 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - define-lazy-prop@2.0.0: {} - define-lazy-prop@3.0.0: {} delay@5.0.0: {} @@ -4948,13 +4701,9 @@ snapshots: dependencies: yaml: 1.10.2 - docker-compose@1.1.0: + docker-compose@1.3.0: dependencies: - yaml: 2.6.1 - - docker-compose@1.2.0: - dependencies: - yaml: 2.8.0 + yaml: 2.8.1 docker-modem@1.0.9: dependencies: @@ -5164,10 +4913,20 @@ snapshots: dependencies: reusify: 1.1.0 + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 + file-type@3.9.0: {} + + file-type@5.2.0: {} + + file-type@6.2.0: {} + filelist@1.0.4: dependencies: minimatch: 5.1.6 @@ -5180,13 +4939,9 @@ snapshots: optionalDependencies: debug: 4.3.4 - follow-redirects@1.15.11(debug@4.3.7): + follow-redirects@1.15.11(debug@4.4.3): optionalDependencies: - debug: 4.3.7(supports-color@8.1.1) - - follow-redirects@1.15.11(debug@4.4.1): - optionalDependencies: - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) for-each@0.3.5: dependencies: @@ -5216,13 +4971,7 @@ snapshots: fs-constants@1.0.0: {} - fs-extra@11.2.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 - - fs-extra@11.3.0: + fs-extra@11.3.2: dependencies: graceful-fs: 4.2.11 jsonfile: 6.2.0 @@ -5275,6 +5024,11 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-stream@2.3.1: + dependencies: + object-assign: 4.1.1 + pinkie-promise: 2.0.1 + get-stream@6.0.1: {} getpass@0.1.7: @@ -5285,16 +5039,7 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@11.0.0: - dependencies: - foreground-child: 3.3.1 - jackspeak: 4.1.1 - minimatch: 10.0.3 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 2.0.0 - - glob@11.0.2: + glob@11.0.3: dependencies: foreground-child: 3.3.1 jackspeak: 4.1.1 @@ -5398,44 +5143,9 @@ snapshots: transitivePeerDependencies: - debug - gluegun@5.2.0(debug@4.3.7): - dependencies: - apisauce: 2.1.6(debug@4.3.7) - app-module-path: 2.2.0 - cli-table3: 0.6.0 - colors: 1.4.0 - cosmiconfig: 7.0.1 - cross-spawn: 7.0.3 - ejs: 3.1.8 - enquirer: 2.3.6 - execa: 5.1.1 - fs-jetpack: 4.3.1 - lodash.camelcase: 4.3.0 - lodash.kebabcase: 4.1.1 - lodash.lowercase: 4.3.0 - lodash.lowerfirst: 4.3.1 - lodash.pad: 4.5.1 - lodash.padend: 4.6.1 - lodash.padstart: 4.6.1 - lodash.repeat: 4.1.0 - lodash.snakecase: 4.1.1 - lodash.startcase: 4.4.0 - lodash.trim: 4.5.1 - lodash.trimend: 4.5.1 - lodash.trimstart: 4.5.1 - lodash.uppercase: 4.3.0 - lodash.upperfirst: 4.3.1 - ora: 4.0.2 - pluralize: 8.0.0 - semver: 7.3.5 - which: 2.0.2 - yargs-parser: 21.1.1 - transitivePeerDependencies: - - debug - - gluegun@5.2.0(debug@4.4.1): + gluegun@5.2.0(debug@4.4.3): dependencies: - apisauce: 2.1.6(debug@4.4.1) + apisauce: 2.1.6(debug@4.4.3) app-module-path: 2.2.0 cli-table3: 0.6.0 colors: 1.4.0 @@ -5482,8 +5192,6 @@ snapshots: graphql@16.11.0: {} - graphql@16.9.0: {} - har-schema@2.0.0: {} har-validator@5.1.5: @@ -5542,7 +5250,7 @@ snapshots: http-call@5.3.0: dependencies: content-type: 1.0.5 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) is-retry-allowed: 1.2.0 is-stream: 2.0.1 parse-json: 4.0.0 @@ -5574,9 +5282,7 @@ snapshots: immutable@4.2.1: {} - immutable@5.0.3: {} - - immutable@5.1.2: {} + immutable@5.1.4: {} import-fresh@3.3.1: dependencies: @@ -5750,6 +5456,8 @@ snapshots: dependencies: ip-regex: 4.3.0 + is-natural-number@4.0.1: {} + is-number@7.0.0: {} is-plain-obj@2.1.0: {} @@ -5763,6 +5471,8 @@ snapshots: is-retry-allowed@1.2.0: {} + is-stream@1.1.0: {} + is-stream@2.0.1: {} is-typed-array@1.1.15: @@ -5869,24 +5579,6 @@ snapshots: - bufferutil - utf-8-validate - jayson@4.1.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): - dependencies: - '@types/connect': 3.4.38 - '@types/node': 12.20.55 - '@types/ws': 7.4.7 - JSONStream: 1.3.5 - commander: 2.20.3 - delay: 5.0.0 - es6-promisify: 5.0.0 - eyes: 0.1.8 - isomorphic-ws: 4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - json-stringify-safe: 5.0.1 - uuid: 8.3.2 - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - jayson@4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@types/connect': 3.4.38 @@ -5951,7 +5643,7 @@ snapshots: node-gyp-build: 4.8.4 readable-stream: 3.6.2 - kubo-rpc-client@5.2.0(undici@7.1.1): + kubo-rpc-client@5.2.0(undici@7.16.0): dependencies: '@ipld/dag-cbor': 9.2.4 '@ipld/dag-json': 10.2.5 @@ -5980,45 +5672,7 @@ snapshots: merge-options: 3.0.4 multiformats: 13.4.0 nanoid: 5.1.5 - native-fetch: 4.0.2(undici@7.1.1) - parse-duration: 2.1.4 - react-native-fetch-api: 3.0.0 - stream-to-it: 1.0.1 - uint8arrays: 5.1.0 - wherearewe: 2.0.1 - transitivePeerDependencies: - - undici - - kubo-rpc-client@5.2.0(undici@7.9.0): - dependencies: - '@ipld/dag-cbor': 9.2.4 - '@ipld/dag-json': 10.2.5 - '@ipld/dag-pb': 4.1.5 - '@libp2p/crypto': 5.1.7 - '@libp2p/interface': 2.10.5 - '@libp2p/logger': 5.1.21 - '@libp2p/peer-id': 5.1.8 - '@multiformats/multiaddr': 12.5.1 - '@multiformats/multiaddr-to-uri': 11.0.2 - any-signal: 4.1.1 - blob-to-it: 2.0.10 - browser-readablestream-to-it: 2.0.10 - dag-jose: 5.1.1 - electron-fetch: 1.9.1 - err-code: 3.0.1 - ipfs-unixfs: 11.2.5 - iso-url: 1.2.1 - it-all: 3.0.9 - it-first: 3.0.9 - it-glob: 3.0.4 - it-last: 3.0.9 - it-map: 3.1.4 - it-peekable: 3.0.8 - it-to-stream: 1.0.0 - merge-options: 3.0.4 - multiformats: 13.4.0 - nanoid: 5.1.5 - native-fetch: 4.0.2(undici@7.9.0) + native-fetch: 4.0.2(undici@7.16.0) parse-duration: 2.1.4 react-native-fetch-api: 3.0.0 stream-to-it: 1.0.1 @@ -6081,6 +5735,10 @@ snapshots: main-event@1.0.1: {} + make-dir@1.3.0: + dependencies: + pify: 3.0.0 + make-error@1.3.6: {} math-intrinsics@1.1.0: {} @@ -6206,13 +5864,9 @@ snapshots: dependencies: node-fetch: 2.7.0(encoding@0.1.13) - native-fetch@4.0.2(undici@7.1.1): - dependencies: - undici: 7.1.1 - - native-fetch@4.0.2(undici@7.9.0): + native-fetch@4.0.2(undici@7.16.0): dependencies: - undici: 7.9.0 + undici: 7.16.0 natural-orderby@2.0.3: {} @@ -6255,25 +5909,12 @@ snapshots: dependencies: mimic-fn: 2.1.0 - open@10.1.0: - dependencies: - default-browser: 5.2.1 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - is-wsl: 3.1.0 - - open@10.1.2: + open@10.2.0: dependencies: default-browser: 5.2.1 define-lazy-prop: 3.0.0 is-inside-container: 1.0.0 - is-wsl: 3.1.0 - - open@8.4.2: - dependencies: - define-lazy-prop: 2.0.0 - is-docker: 2.2.1 - is-wsl: 2.2.0 + wsl-utils: 0.1.0 ora@4.0.2: dependencies: @@ -6355,6 +5996,8 @@ snapshots: sha.js: 2.4.12 to-buffer: 1.2.1 + pend@1.2.0: {} + performance-now@2.1.0: {} picocolors@1.1.1: {} @@ -6363,6 +6006,16 @@ snapshots: picomatch@4.0.3: {} + pify@2.3.0: {} + + pify@3.0.0: {} + + pinkie-promise@2.0.1: + dependencies: + pinkie: 2.0.4 + + pinkie@2.0.4: {} + pluralize@8.0.0: {} possible-typed-array-names@1.1.0: {} @@ -6371,14 +6024,14 @@ snapshots: prettier@3.0.3: {} - prettier@3.4.2: {} - - prettier@3.5.3: {} + prettier@3.6.2: {} process-nextick-args@2.0.1: {} progress-events@1.0.1: {} + progress@2.0.3: {} + promise@8.3.0: dependencies: asap: 2.0.6 @@ -6565,6 +6218,10 @@ snapshots: node-addon-api: 5.1.0 node-gyp-build: 4.8.4 + seek-bzip@1.0.6: + dependencies: + commander: 2.20.3 + semver@7.3.5: dependencies: lru-cache: 6.0.0 @@ -6573,10 +6230,10 @@ snapshots: dependencies: lru-cache: 6.0.0 - semver@7.6.3: {} - semver@7.7.2: {} + semver@7.7.3: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -6713,6 +6370,10 @@ snapshots: dependencies: ansi-regex: 6.2.0 + strip-dirs@2.1.0: + dependencies: + is-natural-number: 4.0.1 + strip-final-newline@2.0.0: {} strip-hex-prefix@1.0.0: @@ -6879,11 +6540,14 @@ snapshots: dependencies: multiformats: 13.4.0 - undici-types@7.10.0: {} + unbzip2-stream@1.4.3: + dependencies: + buffer: 5.7.1 + through: 2.3.8 - undici@7.1.1: {} + undici-types@7.10.0: {} - undici@7.9.0: {} + undici@7.16.0: {} universalify@2.0.1: {} @@ -7049,18 +6713,25 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 5.0.10 + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.0 + xtend@4.0.2: {} yallist@4.0.0: {} yaml@1.10.2: {} - yaml@2.6.1: {} - - yaml@2.8.0: {} + yaml@2.8.1: {} yargs-parser@21.1.1: {} + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + yn@3.1.1: {} yoctocolors-cjs@2.1.2: {} From 260b8f3191f4bec830ab93247a47e794f079bd69 Mon Sep 17 00:00:00 2001 From: Ford Date: Wed, 1 Apr 2026 12:08:18 -0700 Subject: [PATCH 27/28] tests: Use TOML config file for integration tests Switch from --postgres-url CLI args to --config with a generated TOML file, so the [log_store] section is available for the logs-query test. --- tests/src/config.rs | 56 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/tests/src/config.rs b/tests/src/config.rs index 2e1fa1a1fde..b5c032bcab5 100644 --- a/tests/src/config.rs +++ b/tests/src/config.rs @@ -198,21 +198,61 @@ impl Config { ) -> anyhow::Result { let ports = &self.graph_node.ports; + // Generate a TOML config file so we can include [log_store] + let log_dir = "/tmp/integration-test-logs"; + std::fs::create_dir_all(log_dir).ok(); + let config_content = format!( + r#" +[store] +[store.primary] +connection = "{db_url}" +pool_size = 10 + +[deployment] +[[deployment.rule]] +store = "primary" +indexers = ["default"] + +[chains] +ingestor = "default" + +[chains.test] +shard = "primary" +provider = [ + {{ label = "test", url = "{eth_url}", features = [] }} +] + +[log_store] +backend = "file" +directory = "{log_dir}" +retention_hours = 0 +"#, + db_url = self.db.url(), + eth_url = self.eth.url(), + log_dir = log_dir, + ); + let config_path = std::env::temp_dir().join("graph-node-integration-test.toml"); + std::fs::write(&config_path, &config_content)?; + + let config_path_str = config_path.to_string_lossy().to_string(); + let http_port = ports.http.to_string(); + let index_port = ports.index.to_string(); + let admin_port = ports.admin.to_string(); + let metrics_port = ports.metrics.to_string(); + let args = [ - "--postgres-url", - &self.db.url(), - "--ethereum-rpc", - &self.eth.network_url(), + "--config", + &config_path_str, "--ipfs", &self.graph_node.ipfs_uri, "--http-port", - &ports.http.to_string(), + &http_port, "--index-node-port", - &ports.index.to_string(), + &index_port, "--admin-port", - &ports.admin.to_string(), + &admin_port, "--metrics-port", - &ports.metrics.to_string(), + &metrics_port, ]; let args = args From e1aa1c4e986c7f979c70d44c9893aa2f5e5b122b Mon Sep 17 00:00:00 2001 From: Ford Date: Wed, 1 Apr 2026 12:50:55 -0700 Subject: [PATCH 28/28] tests: Add archive and traces features to integration test config The --ethereum-rpc CLI mode defaults to archive,traces features, but the generated TOML config had features = []. This caused subgraphs that depend on those capabilities to fail during indexing. --- tests/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/config.rs b/tests/src/config.rs index b5c032bcab5..e35fbb6bf0d 100644 --- a/tests/src/config.rs +++ b/tests/src/config.rs @@ -219,7 +219,7 @@ ingestor = "default" [chains.test] shard = "primary" provider = [ - {{ label = "test", url = "{eth_url}", features = [] }} + {{ label = "test", url = "{eth_url}", features = ["archive", "traces"] }} ] [log_store]