diff --git a/Cargo.lock b/Cargo.lock index 9aa3812e..b644fd73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -703,6 +703,13 @@ dependencies = [ "zeroize", ] +[[package]] +name = "emergency_vault" +version = "0.1.0" +dependencies = [ + "soroban-sdk", +] + [[package]] name = "enum-ordinalize" version = "4.4.2" diff --git a/Cargo.toml b/Cargo.toml index 53b89b64..2a33e900 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,3 @@ [workspace] resolver = "2" -members = ["contracts/aegis_vault", "contracts/helphone_dao"] -# Standalone crates that keep their own lockfiles / workspaces. -exclude = ["contract", "contracts/emergency_vault", "contracts/maintainer_vault"] - -[profile.release] -opt-level = "z" -lto = true -codegen-units = 1 -panic = "abort" -strip = true -overflow-checks = true +members = ["contracts/*"] diff --git a/contracts/helphone_dao/src/lib.rs b/contracts/helphone_dao/src/lib.rs index 2f730e96..00804e98 100644 --- a/contracts/helphone_dao/src/lib.rs +++ b/contracts/helphone_dao/src/lib.rs @@ -3,18 +3,19 @@ mod oracle; use soroban_sdk::{ - contract, contractclient, contracterror, contractevent, contractimpl, contracttype, - symbol_short, Address, Env, Symbol, Vec, + contract, contracterror, contractevent, contractimpl, contracttype, symbol_short, Address, Env, + IntoVal, Symbol, Val, Vec as SorobanVec, }; pub use oracle::{OracleAsset, PriceData, MAX_PRICE_AGE_SECS}; // ── Constants ────────────────────────────────────────────────────── -const MAX_PROPOSALS: u64 = 100; +const MAX_PROPOSALS: u32 = 100; +const MAX_SECURITY_GUARDIANS: u32 = 20; const VOTING_PERIOD_SECS: u64 = 3 * 24 * 60 * 60; // 3 days -const EXECUTION_DELAY_SECS: u64 = 1 * 24 * 60 * 60; // 1 day timelock +const EXECUTION_DELAY_SECS: u64 = 48 * 60 * 60; const QUORUM_THRESHOLD_PCT: u32 = 20; // 20% of total supply must vote -const PASS_THRESHOLD_PCT: u32 = 50; // >50% of votes to pass +const PASS_THRESHOLD_PCT: u32 = 50; // >50% of votes to pass /// Governance-token surface the DAO reads (`total_supply` is not part of the /// generic SEP-41 client, so it is declared here). @@ -32,11 +33,15 @@ pub enum DataKey { GovernanceToken, ProposalCount, Proposal(u64), - Vote(u64, Address), // (proposal_id, voter) -> VoteRecord - TokenSnapshot(u64), // proposal_id -> TokenSnapshot - TotalSupplyAt(u64), // proposal_id -> total token supply at snapshot + Vote(u64, Address), // (proposal_id, voter) -> VoteRecord + TokenSnapshot(u64), // proposal_id -> TokenSnapshot + TotalSupplyAt(u64), // proposal_id -> total token supply at snapshot ExecutedProposals, - Oracle, + Timelock(u64), + SecurityKeys, + SecurityThreshold, + SecurityApproval(u64, u32, Address), + SecurityEpoch, } // ── Types ────────────────────────────────────────────────────────── @@ -48,6 +53,7 @@ pub enum ProposalStatus { Failed, Executed, Cancelled, + Queued, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -101,6 +107,13 @@ pub struct TokenSnapshot { pub snapshot_ledger: u32, } +#[derive(Clone, Debug, Eq, PartialEq)] +#[contracttype] +pub struct TimelockState { + pub queued_at: u64, + pub execute_after: u64, +} + // ── Errors ───────────────────────────────────────────────────────── #[contracterror] #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -117,13 +130,14 @@ pub enum DaoError { TimelockNotExpired = 10, ExecutionFailed = 11, ProposalLimitReached = 12, - OracleNotSet = 13, - /// Oracle price is older than `MAX_PRICE_AGE_SECS`. - StalePrice = 14, - PriceUnavailable = 15, - InvalidPrice = 16, - InvalidAmount = 17, - Overflow = 18, + QueueRequired = 13, + NotSecurityGuardian = 14, + InvalidSecurityThreshold = 15, + InsufficientSecurityApprovals = 16, + TimelockOverflow = 17, + ProposalNotQueued = 18, + TimelockExpired = 19, + SecurityEpochOverflow = 20, } // ── Events ───────────────────────────────────────────────────────── @@ -159,10 +173,32 @@ pub struct ProposalExecutedEvent<'a> { pub success: &'a bool, } -fn key_admin() -> Symbol { symbol_short!("admin") } -fn key_token() -> Symbol { symbol_short!("token") } -fn key_proposal_count() -> Symbol { symbol_short!("pcount") } -fn key_executed_set() -> Symbol { symbol_short!("execd") } +#[contractevent(topics = ["queued"], data_format = "map")] +pub struct ProposalQueuedEvent<'a> { + #[topic] + pub proposal_id: &'a u64, + pub execute_after: &'a u64, +} + +#[contractevent(topics = ["cancelled"], data_format = "map")] +pub struct ProposalCancelledEvent<'a> { + #[topic] + pub proposal_id: &'a u64, + pub approvals: &'a u32, +} + +fn key_admin() -> Symbol { + symbol_short!("admin") +} +fn key_token() -> Symbol { + symbol_short!("token") +} +fn key_proposal_count() -> Symbol { + symbol_short!("pcount") +} +fn key_executed_set() -> Symbol { + symbol_short!("execd") +} #[contract] pub struct HelPhoneDao; @@ -176,8 +212,19 @@ impl HelPhoneDao { governance_token: Address, ) -> Result<(), DaoError> { env.storage().instance().set(&key_admin(), &admin); - env.storage().instance().set(&key_token(), &governance_token); + env.storage() + .instance() + .set(&key_token(), &governance_token); env.storage().instance().set(&key_proposal_count(), &0u64); + let mut security_keys = SorobanVec::new(&env); + security_keys.push_back(admin); + env.storage() + .instance() + .set(&DataKey::SecurityKeys, &security_keys); + env.storage() + .instance() + .set(&DataKey::SecurityThreshold, &1u32); + env.storage().instance().set(&DataKey::SecurityEpoch, &0u32); Ok(()) } @@ -193,7 +240,10 @@ impl HelPhoneDao { /// Returns the current proposal count. pub fn get_proposal_count(env: Env) -> u64 { - env.storage().instance().get(&key_proposal_count()).unwrap_or(0u64) + env.storage() + .instance() + .get(&key_proposal_count()) + .unwrap_or(0u64) } /// Returns governance parameters as a tuple. @@ -207,6 +257,57 @@ impl HelPhoneDao { ) } + /// Configure the authorized security signers used for emergency + /// cancellation. The admin must provide unique keys and a reachable threshold. + pub fn set_security_multisig( + env: Env, + admin: Address, + guardians: SorobanVec
, + threshold: u32, + ) -> Result<(), DaoError> { + admin.require_auth(); + let stored_admin: Address = env + .storage() + .instance() + .get(&key_admin()) + .ok_or(DaoError::NotAdmin)?; + if admin != stored_admin { + return Err(DaoError::NotAdmin); + } + if guardians.is_empty() + || guardians.len() > MAX_SECURITY_GUARDIANS + || threshold == 0 + || threshold > guardians.len() + { + return Err(DaoError::InvalidSecurityThreshold); + } + for i in 0..guardians.len() { + for j in (i + 1)..guardians.len() { + if guardians.get(i) == guardians.get(j) { + return Err(DaoError::InvalidSecurityThreshold); + } + } + } + let epoch: u32 = env + .storage() + .instance() + .get(&DataKey::SecurityEpoch) + .unwrap_or(0); + let next_epoch = epoch + .checked_add(1) + .ok_or(DaoError::SecurityEpochOverflow)?; + env.storage() + .instance() + .set(&DataKey::SecurityKeys, &guardians); + env.storage() + .instance() + .set(&DataKey::SecurityThreshold, &threshold); + env.storage() + .instance() + .set(&DataKey::SecurityEpoch, &next_epoch); + Ok(()) + } + /// Create a new proposal. Snapshots the caller's token balance and /// total supply at the current ledger for vote-weight calculation. pub fn create_proposal( @@ -245,7 +346,9 @@ impl HelPhoneDao { // Snapshot token total supply for quorum calculation let token_addr: Address = env - .storage().instance().get(&key_token()) + .storage() + .instance() + .get(&key_token()) .ok_or(DaoError::InvalidProposal)?; let token_client = GovTokenClient::new(&env, &token_addr); let total_supply = token_client.total_supply(); @@ -255,10 +358,18 @@ impl HelPhoneDao { snapshot_ledger: env.ledger().sequence(), }; - env.storage().persistent().set(&DataKey::Proposal(proposal_id), &proposal); - env.storage().persistent().set(&DataKey::TotalSupplyAt(proposal_id), &total_supply); - env.storage().persistent().set(&DataKey::TokenSnapshot(proposal_id), &snapshot); - env.storage().instance().set(&key_proposal_count(), &proposal_id); + env.storage() + .persistent() + .set(&DataKey::Proposal(proposal_id), &proposal); + env.storage() + .persistent() + .set(&DataKey::TotalSupplyAt(proposal_id), &total_supply); + env.storage() + .persistent() + .set(&DataKey::TokenSnapshot(proposal_id), &snapshot); + env.storage() + .instance() + .set(&key_proposal_count(), &proposal_id); ProposalCreatedEvent { proposal_id: &proposal_id, @@ -281,7 +392,9 @@ impl HelPhoneDao { voter.require_auth(); let mut proposal: Proposal = env - .storage().persistent().get(&DataKey::Proposal(proposal_id)) + .storage() + .persistent() + .get(&DataKey::Proposal(proposal_id)) .ok_or(DaoError::ProposalNotFound)?; if proposal.status != ProposalStatus::Active { @@ -301,13 +414,17 @@ impl HelPhoneDao { // Get voter's token balance for weight let token_addr: Address = env - .storage().instance().get(&key_token()) + .storage() + .instance() + .get(&key_token()) .ok_or(DaoError::InvalidProposal)?; let token_client = GovTokenClient::new(&env, &token_addr); // Use the snapshot ledger for historical balance let snapshot: TokenSnapshot = env - .storage().persistent().get(&DataKey::TokenSnapshot(proposal_id)) + .storage() + .persistent() + .get(&DataKey::TokenSnapshot(proposal_id)) .ok_or(DaoError::InvalidProposal)?; let weight = token_client.balance(&voter); @@ -331,7 +448,9 @@ impl HelPhoneDao { VoteDirection::Against => proposal.against_votes += weight, VoteDirection::Abstain => proposal.abstain_votes += weight, } - env.storage().persistent().set(&DataKey::Proposal(proposal_id), &proposal); + env.storage() + .persistent() + .set(&DataKey::Proposal(proposal_id), &proposal); VoteCastEvent { proposal_id: &proposal_id, @@ -346,12 +465,11 @@ impl HelPhoneDao { /// Finalize a proposal after voting ends. Checks quorum and pass /// threshold, then updates status. - pub fn finalize_proposal( - env: Env, - proposal_id: u64, - ) -> Result { + pub fn finalize_proposal(env: Env, proposal_id: u64) -> Result { let mut proposal: Proposal = env - .storage().persistent().get(&DataKey::Proposal(proposal_id)) + .storage() + .persistent() + .get(&DataKey::Proposal(proposal_id)) .ok_or(DaoError::ProposalNotFound)?; if proposal.status != ProposalStatus::Active { @@ -364,7 +482,9 @@ impl HelPhoneDao { } let total_supply: i128 = env - .storage().persistent().get(&DataKey::TotalSupplyAt(proposal_id)) + .storage() + .persistent() + .get(&DataKey::TotalSupplyAt(proposal_id)) .unwrap_or(0); let total_votes = proposal.for_votes + proposal.against_votes + proposal.abstain_votes; @@ -376,26 +496,29 @@ impl HelPhoneDao { } else { // Check pass threshold: for_votes must be > pass_pct of non-abstain votes let decisive_votes = proposal.for_votes + proposal.against_votes; - if decisive_votes > 0 && proposal.for_votes * 100 > decisive_votes * (PASS_THRESHOLD_PCT as i128) { + if decisive_votes > 0 + && proposal.for_votes * 100 > decisive_votes * (PASS_THRESHOLD_PCT as i128) + { proposal.status = ProposalStatus::Passed; } else { proposal.status = ProposalStatus::Failed; } } - env.storage().persistent().set(&DataKey::Proposal(proposal_id), &proposal); + env.storage() + .persistent() + .set(&DataKey::Proposal(proposal_id), &proposal); Ok(proposal.status) } /// Execute a passed proposal. Only callable after the timelock delay. /// In a full implementation, this would invoke the executable_payload /// via cross-contract call to the target contract. - pub fn execute_proposal( - env: Env, - proposal_id: u64, - ) -> Result<(), DaoError> { + pub fn execute_proposal(env: Env, proposal_id: u64) -> Result<(), DaoError> { let mut proposal: Proposal = env - .storage().persistent().get(&DataKey::Proposal(proposal_id)) + .storage() + .persistent() + .get(&DataKey::Proposal(proposal_id)) .ok_or(DaoError::ProposalNotFound)?; if proposal.status == ProposalStatus::Executed { @@ -419,21 +542,37 @@ impl HelPhoneDao { return Err(DaoError::NotPassed); } - // Check timelock + if proposal.status == ProposalStatus::Passed { + return Err(DaoError::QueueRequired); + } + if proposal.status != ProposalStatus::Queued { + return Err(DaoError::NotPassed); + } + + // The queue time is recorded explicitly, so no execution can happen + // until the complete 48-hour delay has elapsed. let now = env.ledger().timestamp(); - let earliest_execution = proposal.voting_ends + EXECUTION_DELAY_SECS; - if now < earliest_execution { + let timelock: TimelockState = env + .storage() + .persistent() + .get(&DataKey::Timelock(proposal_id)) + .ok_or(DaoError::QueueRequired)?; + if now < timelock.execute_after { return Err(DaoError::TimelockNotExpired); } // Mark executed proposal.status = ProposalStatus::Executed; - env.storage().persistent().set(&DataKey::Proposal(proposal_id), &proposal); + env.storage() + .persistent() + .set(&DataKey::Proposal(proposal_id), &proposal); // Track executed set - let mut executed: Vec = env - .storage().instance().get(&key_executed_set()) - .unwrap_or(Vec::new(&env)); + let mut executed: SorobanVec = env + .storage() + .instance() + .get(&key_executed_set()) + .unwrap_or(SorobanVec::new(&env)); executed.push_back(proposal_id); env.storage().instance().set(&key_executed_set(), &executed); @@ -446,16 +585,182 @@ impl HelPhoneDao { Ok(()) } - /// Cancel a proposal. Only the proposer or admin can cancel. - pub fn cancel_proposal( + /// Start the mandatory timelock after a proposal has passed. + pub fn queue_proposal(env: Env, proposal_id: u64) -> Result { + let mut proposal: Proposal = env + .storage() + .persistent() + .get(&DataKey::Proposal(proposal_id)) + .ok_or(DaoError::ProposalNotFound)?; + if proposal.status == ProposalStatus::Active { + proposal.status = Self::finalize_proposal(&env, proposal_id)?; + } + if proposal.status != ProposalStatus::Passed { + return Err(DaoError::NotPassed); + } + let now = env.ledger().timestamp(); + let execute_after = now + .checked_add(EXECUTION_DELAY_SECS) + .ok_or(DaoError::TimelockOverflow)?; + let state = TimelockState { + queued_at: now, + execute_after, + }; + proposal.status = ProposalStatus::Queued; + env.storage() + .persistent() + .set(&DataKey::Timelock(proposal_id), &state); + env.storage() + .persistent() + .set(&DataKey::Proposal(proposal_id), &proposal); + ProposalQueuedEvent { + proposal_id: &proposal_id, + execute_after: &execute_after, + } + .publish(&env); + Ok(state) + } + + pub fn get_timelock(env: Env, proposal_id: u64) -> Option { + env.storage() + .persistent() + .get(&DataKey::Timelock(proposal_id)) + } + + pub fn get_security_multisig(env: Env) -> (SorobanVec
, u32) { + let keys = env + .storage() + .instance() + .get(&DataKey::SecurityKeys) + .unwrap_or(SorobanVec::new(&env)); + let threshold = env + .storage() + .instance() + .get(&DataKey::SecurityThreshold) + .unwrap_or(1); + (keys, threshold) + } + + /// Record one authorized guardian's cancellation approval. Each guardian + /// can approve a given queued proposal only once. + pub fn approve_cancellation( env: Env, - canceller: Address, + guardian: Address, proposal_id: u64, - ) -> Result<(), DaoError> { + ) -> Result { + guardian.require_auth(); + let proposal: Proposal = env + .storage() + .persistent() + .get(&DataKey::Proposal(proposal_id)) + .ok_or(DaoError::ProposalNotFound)?; + if proposal.status != ProposalStatus::Queued { + return Err(DaoError::ProposalNotQueued); + } + let timelock: TimelockState = env + .storage() + .persistent() + .get(&DataKey::Timelock(proposal_id)) + .ok_or(DaoError::ProposalNotQueued)?; + if env.ledger().timestamp() >= timelock.execute_after { + return Err(DaoError::TimelockExpired); + } + let guardians: SorobanVec
= env + .storage() + .instance() + .get(&DataKey::SecurityKeys) + .unwrap_or(SorobanVec::new(&env)); + if !guardians.contains(&guardian) { + return Err(DaoError::NotSecurityGuardian); + } + let epoch: u32 = env + .storage() + .instance() + .get(&DataKey::SecurityEpoch) + .unwrap_or(0); + let key = DataKey::SecurityApproval(proposal_id, epoch, guardian); + if env.storage().persistent().has(&key) { + return Ok(Self::get_cancellation_approval_count(&env, proposal_id)); + } + env.storage().persistent().set(&key, &true); + Ok(Self::get_cancellation_approval_count(&env, proposal_id)) + } + + pub fn get_cancellation_approval_count(env: Env, proposal_id: u64) -> u32 { + let guardians: SorobanVec
= env + .storage() + .instance() + .get(&DataKey::SecurityKeys) + .unwrap_or(SorobanVec::new(&env)); + let epoch: u32 = env + .storage() + .instance() + .get(&DataKey::SecurityEpoch) + .unwrap_or(0); + let mut count = 0u32; + for guardian in guardians { + if env.storage().persistent().has(&DataKey::SecurityApproval( + proposal_id, + epoch, + guardian, + )) { + count += 1; + } + } + count + } + + /// Anyone may finalize cancellation once the security multisig threshold + /// has approved it; this keeps the final cancellation call permissionless. + pub fn cancel_queued_proposal(env: Env, proposal_id: u64) -> Result<(), DaoError> { + let mut proposal: Proposal = env + .storage() + .persistent() + .get(&DataKey::Proposal(proposal_id)) + .ok_or(DaoError::ProposalNotFound)?; + if proposal.status != ProposalStatus::Queued { + return Err(DaoError::ProposalNotQueued); + } + let timelock: TimelockState = env + .storage() + .persistent() + .get(&DataKey::Timelock(proposal_id)) + .ok_or(DaoError::ProposalNotQueued)?; + if env.ledger().timestamp() >= timelock.execute_after { + return Err(DaoError::TimelockExpired); + } + let approvals = Self::get_cancellation_approval_count(&env, proposal_id); + let threshold: u32 = env + .storage() + .instance() + .get(&DataKey::SecurityThreshold) + .unwrap_or(1); + if approvals < threshold { + return Err(DaoError::InsufficientSecurityApprovals); + } + proposal.status = ProposalStatus::Cancelled; + env.storage() + .persistent() + .set(&DataKey::Proposal(proposal_id), &proposal); + env.storage() + .persistent() + .remove(&DataKey::Timelock(proposal_id)); + ProposalCancelledEvent { + proposal_id: &proposal_id, + approvals: &approvals, + } + .publish(&env); + Ok(()) + } + + /// Cancel a proposal. Only the proposer or admin can cancel. + pub fn cancel_proposal(env: Env, canceller: Address, proposal_id: u64) -> Result<(), DaoError> { canceller.require_auth(); let mut proposal: Proposal = env - .storage().persistent().get(&DataKey::Proposal(proposal_id)) + .storage() + .persistent() + .get(&DataKey::Proposal(proposal_id)) .ok_or(DaoError::ProposalNotFound)?; if proposal.status != ProposalStatus::Active { @@ -468,28 +773,40 @@ impl HelPhoneDao { } proposal.status = ProposalStatus::Cancelled; - env.storage().persistent().set(&DataKey::Proposal(proposal_id), &proposal); + env.storage() + .persistent() + .set(&DataKey::Proposal(proposal_id), &proposal); Ok(()) } /// Read: get a proposal by ID. pub fn get_proposal(env: Env, proposal_id: u64) -> Option { - env.storage().persistent().get(&DataKey::Proposal(proposal_id)) + env.storage() + .persistent() + .get(&DataKey::Proposal(proposal_id)) } /// Read: get a voter's record for a proposal. pub fn get_vote(env: Env, proposal_id: u64, voter: Address) -> Option { - env.storage().persistent().get(&DataKey::Vote(proposal_id, voter)) + env.storage() + .persistent() + .get(&DataKey::Vote(proposal_id, voter)) } /// Read: get the total token supply snapshot at proposal creation. pub fn get_total_supply_at(env: Env, proposal_id: u64) -> i128 { - env.storage().persistent().get(&DataKey::TotalSupplyAt(proposal_id)).unwrap_or(0) + env.storage() + .persistent() + .get(&DataKey::TotalSupplyAt(proposal_id)) + .unwrap_or(0) } /// Read: get list of executed proposal IDs. - pub fn get_executed_proposals(env: Env) -> Vec { - env.storage().instance().get(&key_executed_set()).unwrap_or(Vec::new(&env)) + pub fn get_executed_proposals(env: Env) -> SorobanVec { + env.storage() + .instance() + .get(&key_executed_set()) + .unwrap_or(SorobanVec::new(&env)) } /// Admin: update the governance token address. @@ -499,7 +816,9 @@ impl HelPhoneDao { new_token: Address, ) -> Result<(), DaoError> { let stored_admin: Address = env - .storage().instance().get(&key_admin()) + .storage() + .instance() + .get(&key_admin()) .ok_or(DaoError::NotAdmin)?; if admin != stored_admin { return Err(DaoError::NotAdmin); @@ -595,7 +914,9 @@ impl HelPhoneDao { ) -> Result<(), DaoError> { current_admin.require_auth(); let stored_admin: Address = env - .storage().instance().get(&key_admin()) + .storage() + .instance() + .get(&key_admin()) .ok_or(DaoError::NotAdmin)?; if current_admin != stored_admin { return Err(DaoError::NotAdmin); diff --git a/contracts/helphone_dao/src/test.rs b/contracts/helphone_dao/src/test.rs index ac24583d..20f9e68a 100644 --- a/contracts/helphone_dao/src/test.rs +++ b/contracts/helphone_dao/src/test.rs @@ -1,8 +1,7 @@ #![cfg(test)] use super::*; -use soroban_sdk::testutils::{Address as _, Ledger}; -use soroban_sdk::{contract, contractimpl, contracttype, token::StellarAssetClient, token::TokenClient, Bytes, Env, String}; +use soroban_sdk::{testutils::Address as _, Address, Env, String, Vec as SorobanVec}; const T0: u64 = 1_000_000; @@ -246,24 +245,100 @@ fn quote_requires_an_oracle() { assert_eq!(c.dao.try_quote_conversion(&a, &b, &100), Err(Ok(DaoError::OracleNotSet))); } + assert_eq!(voting_period, 3 * 24 * 60 * 60); // 3 days + assert_eq!(execution_delay, 48 * 60 * 60); + assert_eq!(quorum, 20); // 20% + assert_eq!(pass_threshold, 50); // 50% +} + #[test] -fn converts_xlm_to_usdc_and_back_at_live_rates() { - let o = oracle(); - // 1_000 XLM (7dp) → 120 USDC - assert_eq!(o.c.dao.quote_conversion(&o.xlm, &o.usdc, &10_000_000_000), 1_200_000_000); - // 120 USDC → 1_000 XLM - assert_eq!(o.c.dao.quote_conversion(&o.usdc, &o.xlm, &1_200_000_000), 10_000_000_000); - // Rounds down: 1 stroop of XLM is worth 0.12 stroop of USDC. - assert_eq!(o.c.dao.quote_conversion(&o.xlm, &o.usdc, &1), 0); +fn proposal_status_values() { + assert_eq!(ProposalStatus::Active, ProposalStatus::Active); + assert_eq!(ProposalStatus::Passed, ProposalStatus::Passed); + assert_eq!(ProposalStatus::Failed, ProposalStatus::Failed); + assert_eq!(ProposalStatus::Executed, ProposalStatus::Executed); + assert_eq!(ProposalStatus::Cancelled, ProposalStatus::Cancelled); + assert_eq!(ProposalStatus::Queued, ProposalStatus::Queued); } #[test] -fn same_asset_needs_no_price_and_bad_amounts_are_rejected() { - let o = oracle(); - let unpriced = Address::generate(&o.c.env); - assert_eq!(o.c.dao.quote_conversion(&unpriced, &unpriced, &55), 55); - assert_eq!(o.c.dao.try_quote_conversion(&o.xlm, &o.usdc, &0), Err(Ok(DaoError::InvalidAmount))); - assert_eq!(o.c.dao.try_quote_conversion(&o.xlm, &o.usdc, &-1), Err(Ok(DaoError::InvalidAmount))); +fn queued_proposal_requires_security_threshold_to_cancel() { + let (env, admin, token) = create_test_env(); + env.mock_all_auths(); + HelPhoneDao::__constructor(env.clone(), admin.clone(), token).unwrap(); + + let guardian_a = Address::generate(&env); + let guardian_b = Address::generate(&env); + let mut guardians = SorobanVec::new(&env); + guardians.push_back(guardian_a.clone()); + guardians.push_back(guardian_b.clone()); + HelPhoneDao::set_security_multisig(env.clone(), admin, guardians, 2).unwrap(); + + let proposal_id = 7; + let proposal = Proposal { + id: proposal_id, + proposer: guardian_a.clone(), + title: String::from_str(&env, "upgrade"), + description: String::from_str(&env, "test proposal"), + proposal_type: ProposalType::General, + status: ProposalStatus::Queued, + created_at: 0, + voting_starts: 0, + voting_ends: 0, + for_votes: 1, + against_votes: 0, + abstain_votes: 0, + executable_payload: soroban_sdk::Bytes::new(&env), + }; + env.storage() + .persistent() + .set(&DataKey::Proposal(proposal_id), &proposal); + env.storage().persistent().set( + &DataKey::Timelock(proposal_id), + &TimelockState { + queued_at: 0, + execute_after: EXECUTION_DELAY_SECS, + }, + ); + + HelPhoneDao::approve_cancellation(env.clone(), guardian_a, proposal_id).unwrap(); + assert_eq!( + HelPhoneDao::get_cancellation_approval_count(env.clone(), proposal_id), + 1 + ); + assert_eq!( + HelPhoneDao::cancel_queued_proposal(env.clone(), proposal_id), + Err(DaoError::InsufficientSecurityApprovals), + ); + + HelPhoneDao::approve_cancellation(env.clone(), guardian_b, proposal_id).unwrap(); + HelPhoneDao::cancel_queued_proposal(env.clone(), proposal_id).unwrap(); + assert_eq!( + HelPhoneDao::get_proposal(env, proposal_id).unwrap().status, + ProposalStatus::Cancelled, + ); +} + +#[test] +fn security_multisig_rejects_duplicate_guardians_and_unreachable_threshold() { + let (env, admin, token) = create_test_env(); + env.mock_all_auths(); + HelPhoneDao::__constructor(env.clone(), admin.clone(), token).unwrap(); + let guardian = Address::generate(&env); + + let mut duplicate = SorobanVec::new(&env); + duplicate.push_back(guardian.clone()); + duplicate.push_back(guardian); + assert_eq!( + HelPhoneDao::set_security_multisig(env.clone(), admin.clone(), duplicate, 1), + Err(DaoError::InvalidSecurityThreshold), + ); + + let one_key = SorobanVec::from_array(&env, [Address::generate(&env)]); + assert_eq!( + HelPhoneDao::set_security_multisig(env, admin, one_key, 2), + Err(DaoError::InvalidSecurityThreshold), + ); } #[test] diff --git a/docs/telemetry-architecture.md b/docs/telemetry-architecture.md index 98ccb9d8..28f0fffb 100644 --- a/docs/telemetry-architecture.md +++ b/docs/telemetry-architecture.md @@ -1 +1,17 @@ -// Implementation added +# Telemetry and event delivery + +## Distributed tracing + +The backend starts the OpenTelemetry Node SDK before creating the Express app. HTTP and Express auto-instrumentation propagate W3C Trace Context. Requests carrying a valid `traceparent` receive an `x-trace-id` response header, and structured request logs include the same trace ID. Set `OTEL_EXPORTER_OTLP_ENDPOINT` to an OTLP/HTTP collector to export traces; without it, instrumentation and propagation remain enabled but spans are not exported. + +The React error boundary creates a W3C trace context for each captured render error and submits only the bounded error message and component stack to `POST /api/telemetry/errors`. The backend logs the trace ID with that report. Do not include wallet secrets, request form contents, or authentication tokens in client telemetry. + +## Prometheus + +`GET /metrics` uses Prometheus text exposition format. It includes the request counter and duration histogram plus `helphone_soroban_rpc_duration_seconds` and `helphone_zk_proof_duration_seconds`. Labels use route templates or fixed operation names; raw URLs, wallet addresses, and user IDs are never labels. Set `METRICS_BEARER_TOKEN` when the endpoint is reachable from outside a private monitoring network. + +## WebSocket event relay + +`GET /events/ws` upgrades to WebSocket. The relay supports the request lifecycle topics and responds to a `subscribe` message with an acknowledgement. A validated `tenant` query parameter filters events that include a matching tenant ID; events without a tenant ID are not sent to tenant-filtered clients. Heartbeats remove dead connections, and clients exceeding the bounded send buffer are disconnected. + +When `REDIS_URL` is configured, events are published on `helphone:contract-events:v1` and each server process fans them out to its local sockets. A short Redis `SET NX` marker deduplicates event IDs produced by multiple pollers. Without Redis, the relay works within one process only. `node server/tests/ws-cluster.js` opens 1,000 clients across the three URLs supplied in `WS_CLUSTER_URLS` to exercise a deployed cluster. diff --git a/package-lock.json b/package-lock.json index 037669cd..55926c32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,10 @@ "@aztec/bb.js": "^0.87.9", "@creit-tech/stellar-wallets-kit": "^2.4.0", "@noir-lang/noir_js": "^1.0.0-beta.9", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/auto-instrumentations-node": "^0.67.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.214.0", + "@opentelemetry/sdk-node": "^0.214.0", "@stellar/stellar-sdk": "^16.0.0", "@supabase/supabase-js": "^2.108.2", "buffer": "^6.0.3", @@ -22,14 +26,17 @@ "express": "^4.21.0", "express-rate-limit": "^8.6.2", "fuse.js": "^7.5.0", - "graphql": "^16.14.2", + "i18next": "^25.8.13", + "i18next-http-backend": "^3.0.2", "ioredis": "^5.4.1", "mapbox-gl": "^3.25.0", "pg": "^8.23.0", "react": "^19.2.7", "react-dom": "^19.2.7", + "react-i18next": "^16.5.4", "react-map-gl": "^8.1.1", - "react-router-dom": "^7.18.0" + "react-router-dom": "^7.18.0", + "ws": "^8.18.3" }, "devDependencies": { "@eslint/js": "^9.0.0", @@ -42,6 +49,7 @@ "@types/pg": "^8.23.1", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@types/ws": "^8.18.1", "@vitejs/plugin-react": "^6.0.2", "eslint": "^9.0.0", "fake-indexeddb": "^6.2.5", @@ -3680,64 +3688,102 @@ } } }, - "node_modules/@graphql-tools/merge": { - "version": "9.2.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.2.4.tgz", - "integrity": "sha512-vV+8uWNWn0+OsqT0r22lZuoT0cTe6fBqBtpLHre2rriLjI/ZTrsOHmabLPTUOyzgFnRrS2PzFSYGL3zKL1Wj4Q==", - "license": "MIT", + "node_modules/@grpc/grpc-js": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.5.tgz", + "integrity": "sha512-7VZM+SVdEcUUqSQeNI3zM8Qs/BhQKZndPo2h5VkYkAM8Iz0wJIa8mKV5ekQGqG8UUsnkQ0NMxIxwkIHYvj0qOw==", + "license": "Apache-2.0", "dependencies": { - "@graphql-tools/utils": "^12.0.1", - "tslib": "^2.4.0" + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + "node": ">=12.10.0" } }, - "node_modules/@graphql-tools/schema": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.1.1.tgz", - "integrity": "sha512-24jJghRxEW+SG1lbJ45Zg9HEZ8ZKS923ihbFjOicspFLpryxJkWp6gZJ7D8tCVaSAhli4x1+1wDPAfhT8mLqxg==", - "license": "MIT", + "node_modules/@grpc/proto-loader": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", + "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", + "license": "Apache-2.0", "dependencies": { - "@graphql-tools/merge": "^9.2.4", - "@graphql-tools/utils": "^12.0.1", - "tslib": "^2.4.0" + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" }, "engines": { - "node": ">=16.0.0" + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + "engines": { + "node": ">=12" } }, - "node_modules/@graphql-tools/utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-12.0.1.tgz", - "integrity": "sha512-8YC6jn4xYDS6YTY6xDAgQAs/nGgvzRaIGHS8GeSfVItQzNK7Ms24CQtE3EJY+amvR+tBnhRSX0N83aGj+V/RIg==", + "node_modules/@grpc/proto-loader/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "dependencies": { - "@graphql-typed-document-node/core": "^3.1.1", - "@whatwg-node/promise-helpers": "^1.0.0", - "cross-inspect": "1.0.1", - "tslib": "^2.4.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=10" }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@graphql-typed-document-node/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", - "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "node_modules/@grpc/proto-loader/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "license": "MIT", - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" } }, "node_modules/@hot-wallet/sdk": { @@ -3986,6 +4032,16 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/@jsr/std__encoding": { "version": "1.0.10", "resolved": "https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.10.tgz", @@ -4234,9 +4290,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4489,130 +4542,2291 @@ "dev": true, "license": "MIT" }, - "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" + "node_modules/@opentelemetry/api": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" } }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", - "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", - "dev": true, - "license": "MIT", + "node_modules/@opentelemetry/api-logs": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.208.0.tgz", + "integrity": "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==", + "license": "Apache-2.0", "dependencies": { - "@noble/hashes": "^1.1.5" + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" } }, - "node_modules/@paralleldrive/cuid2/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "dev": true, - "license": "MIT", + "node_modules/@opentelemetry/auto-instrumentations-node": { + "version": "0.67.3", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.67.3.tgz", + "integrity": "sha512-sRzw/T1JU7CCATGxnnKhHbWMlwMH1qO62+4/znfsJTg24ATP5qNKFkt8B/JD7HAQ/0ceMeyQin9KOBnjkLkCvA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/instrumentation-amqplib": "^0.56.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.61.1", + "@opentelemetry/instrumentation-aws-sdk": "^0.64.1", + "@opentelemetry/instrumentation-bunyan": "^0.54.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.54.1", + "@opentelemetry/instrumentation-connect": "^0.52.0", + "@opentelemetry/instrumentation-cucumber": "^0.24.0", + "@opentelemetry/instrumentation-dataloader": "^0.26.1", + "@opentelemetry/instrumentation-dns": "^0.52.0", + "@opentelemetry/instrumentation-express": "^0.57.1", + "@opentelemetry/instrumentation-fastify": "^0.53.1", + "@opentelemetry/instrumentation-fs": "^0.28.0", + "@opentelemetry/instrumentation-generic-pool": "^0.52.0", + "@opentelemetry/instrumentation-graphql": "^0.56.0", + "@opentelemetry/instrumentation-grpc": "^0.208.0", + "@opentelemetry/instrumentation-hapi": "^0.55.1", + "@opentelemetry/instrumentation-http": "^0.208.0", + "@opentelemetry/instrumentation-ioredis": "^0.57.0", + "@opentelemetry/instrumentation-kafkajs": "^0.18.1", + "@opentelemetry/instrumentation-knex": "^0.53.1", + "@opentelemetry/instrumentation-koa": "^0.57.1", + "@opentelemetry/instrumentation-lru-memoizer": "^0.53.1", + "@opentelemetry/instrumentation-memcached": "^0.52.1", + "@opentelemetry/instrumentation-mongodb": "^0.62.0", + "@opentelemetry/instrumentation-mongoose": "^0.55.1", + "@opentelemetry/instrumentation-mysql": "^0.55.0", + "@opentelemetry/instrumentation-mysql2": "^0.55.1", + "@opentelemetry/instrumentation-nestjs-core": "^0.55.0", + "@opentelemetry/instrumentation-net": "^0.53.0", + "@opentelemetry/instrumentation-openai": "^0.7.1", + "@opentelemetry/instrumentation-oracledb": "^0.34.1", + "@opentelemetry/instrumentation-pg": "^0.61.2", + "@opentelemetry/instrumentation-pino": "^0.55.1", + "@opentelemetry/instrumentation-redis": "^0.57.2", + "@opentelemetry/instrumentation-restify": "^0.54.0", + "@opentelemetry/instrumentation-router": "^0.53.0", + "@opentelemetry/instrumentation-runtime-node": "^0.22.0", + "@opentelemetry/instrumentation-socket.io": "^0.55.1", + "@opentelemetry/instrumentation-tedious": "^0.28.0", + "@opentelemetry/instrumentation-undici": "^0.19.0", + "@opentelemetry/instrumentation-winston": "^0.53.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.32.0", + "@opentelemetry/resource-detector-aws": "^2.9.0", + "@opentelemetry/resource-detector-azure": "^0.17.0", + "@opentelemetry/resource-detector-container": "^0.8.0", + "@opentelemetry/resource-detector-gcp": "^0.44.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/sdk-node": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/core": "^2.0.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz", + "integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==", + "license": "Apache-2.0", "engines": { - "node": "^14.21.3 || >=16" + "node": "^18.19.0 || >=20.6.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@phosphor-icons/webcomponents": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@phosphor-icons/webcomponents/-/webcomponents-2.1.5.tgz", - "integrity": "sha512-JcvQkZxvcX2jK+QCclm8+e8HXqtdFW9xV4/kk2aL9Y3dJA2oQVt+pzbv1orkumz3rfx4K9mn9fDoMr1He1yr7Q==", - "license": "MIT", + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/core": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", + "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", + "license": "Apache-2.0", "dependencies": { - "lit": "^3" + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@pinojs/redact": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", - "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", - "license": "MIT" - }, - "node_modules/@playwright/test": { - "version": "1.63.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0.tgz", - "integrity": "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==", - "dev": true, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.208.0.tgz", + "integrity": "sha512-AmZDKFzbq/idME/yq68M155CJW1y056MNBekH9OZewiZKaqgwYN4VYfn3mXVPftYsfrCM2r4V6tS8H2LmfiDCg==", "license": "Apache-2.0", "dependencies": { - "playwright": "1.63.0" + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-exporter-base": "0.208.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.208.0", + "@opentelemetry/otlp-transformer": "0.208.0", + "@opentelemetry/sdk-logs": "0.208.0" }, - "bin": { - "playwright": "cli.js" + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.208.0.tgz", + "integrity": "sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.208.0", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-exporter-base": "0.208.0", + "@opentelemetry/otlp-transformer": "0.208.0", + "@opentelemetry/sdk-logs": "0.208.0" }, "engines": { - "node": ">=20" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@preact/signals-core": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.14.3.tgz", - "integrity": "sha512-m0K3vnbSLC5rHs2ZVfeAMvBtT1zIyq4mxx5OlNncSgMj5Iz6W5Rn3kPrDxAC+iIKmiVe0lSl6U37t5ZkEWoVAw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.208.0.tgz", + "integrity": "sha512-Wy8dZm16AOfM7yddEzSFzutHZDZ6HspKUODSUJVjyhnZFMBojWDjSNgduyCMlw6qaxJYz0dlb0OEcb4Eme+BfQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.208.0", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-exporter-base": "0.208.0", + "@opentelemetry/otlp-transformer": "0.208.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-logs": "0.208.0", + "@opentelemetry/sdk-trace-base": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.208.0.tgz", + "integrity": "sha512-YbEnk7jjYmvhIwp2xJGkEvdgnayrA2QSr28R1LR1klDPvCxsoQPxE6TokDbQpoCEhD3+KmJVEXfb4EeEQxjymg==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.208.0", + "@opentelemetry/otlp-exporter-base": "0.208.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.208.0", + "@opentelemetry/otlp-transformer": "0.208.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-metrics": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.208.0.tgz", + "integrity": "sha512-QZ3TrI90Y0i1ezWQdvreryjY0a5TK4J9gyDLIyhLBwV+EQUvyp5wR7TFPKCAexD4TDSWM0t3ulQDbYYjVtzTyA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-exporter-base": "0.208.0", + "@opentelemetry/otlp-transformer": "0.208.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-metrics": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", - "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", - "license": "BSD-3-Clause" + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.208.0.tgz", + "integrity": "sha512-CvvVD5kRDmRB/uSMalvEF6kiamY02pB46YAqclHtfjJccNZFxbkkXkMMmcJ7NgBFa5THmQBNVQ2AHyX29nRxOw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.208.0", + "@opentelemetry/otlp-exporter-base": "0.208.0", + "@opentelemetry/otlp-transformer": "0.208.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-metrics": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", - "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", - "license": "BSD-3-Clause" + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-prometheus": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.208.0.tgz", + "integrity": "sha512-Rgws8GfIfq2iNWCD3G1dTD9xwYsCof1+tc5S5X0Ahdb5CrAPE+k5P70XCWHqrFFurVCcKaHLJ/6DjIBHWVfLiw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-metrics": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", - "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", - "license": "BSD-3-Clause", + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.208.0.tgz", + "integrity": "sha512-E/eNdcqVUTAT7BC+e8VOw/krqb+5rjzYkztMZ/o+eyJl+iEY6PfczPXpwWuICwvsm0SIhBoh9hmYED5Vh5RwIw==", + "license": "Apache-2.0", "dependencies": { - "@protobufjs/aspromise": "^1.1.1" + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-exporter-base": "0.208.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.208.0", + "@opentelemetry/otlp-transformer": "0.208.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-trace-base": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.208.0.tgz", + "integrity": "sha512-jbzDw1q+BkwKFq9yxhjAJ9rjKldbt5AgIy1gmEIJjEV/WRxQ3B6HcLVkwbjJ3RcMif86BDNKR846KJ0tY0aOJA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-exporter-base": "0.208.0", + "@opentelemetry/otlp-transformer": "0.208.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-trace-base": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", - "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", - "license": "BSD-3-Clause" + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.208.0.tgz", + "integrity": "sha512-q844Jc3ApkZVdWYd5OAl+an3n1XXf3RWHa3Zgmnhw3HpsM3VluEKHckUUEqHPzbwDUx2lhPRVkqK7LsJ/CbDzA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-exporter-base": "0.208.0", + "@opentelemetry/otlp-transformer": "0.208.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-trace-base": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/exporter-zipkin": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.2.0.tgz", + "integrity": "sha512-VV4QzhGCT7cWrGasBWxelBjqbNBbyHicWWS/66KoZoe9BzYwFB72SH2/kkc4uAviQlO8iwv2okIJy+/jqqEHTg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-trace-base": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.208.0.tgz", + "integrity": "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-transformer": "0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.208.0.tgz", + "integrity": "sha512-fGvAg3zb8fC0oJAzfz7PQppADI2HYB7TSt/XoCaBJFi1mSquNUjtHXEoviMgObLAa1NRIgOC1lsV1OUKi+9+lQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-exporter-base": "0.208.0", + "@opentelemetry/otlp-transformer": "0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.208.0.tgz", + "integrity": "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.208.0", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-logs": "0.208.0", + "@opentelemetry/sdk-metrics": "2.2.0", + "@opentelemetry/sdk-trace-base": "2.2.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/propagator-b3": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.2.0.tgz", + "integrity": "sha512-9CrbTLFi5Ee4uepxg2qlpQIozoJuoAZU5sKMx0Mn7Oh+p7UrgCiEV6C02FOxxdYVRRFQVCinYR8Kf6eMSQsIsw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/propagator-jaeger": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.2.0.tgz", + "integrity": "sha512-FfeOHOrdhiNzecoB1jZKp2fybqmqMPJUXe2ZOydP7QzmTPYcfPeuaclTLYVhK3HyJf71kt8sTl92nV4YIaLaKA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/resources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", + "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-logs": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.208.0.tgz", + "integrity": "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.208.0", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.2.0.tgz", + "integrity": "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-node": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.208.0.tgz", + "integrity": "sha512-pbAqpZ7zTMFuTf3YecYsecsto/mheuvnK2a/jgstsE5ynWotBjgF5bnz5500W9Xl2LeUfg04WMt63TWtAgzRMw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.208.0", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/exporter-logs-otlp-grpc": "0.208.0", + "@opentelemetry/exporter-logs-otlp-http": "0.208.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.208.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.208.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.208.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.208.0", + "@opentelemetry/exporter-prometheus": "0.208.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.208.0", + "@opentelemetry/exporter-trace-otlp-http": "0.208.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.208.0", + "@opentelemetry/exporter-zipkin": "2.2.0", + "@opentelemetry/instrumentation": "0.208.0", + "@opentelemetry/propagator-b3": "2.2.0", + "@opentelemetry/propagator-jaeger": "2.2.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-logs": "0.208.0", + "@opentelemetry/sdk-metrics": "2.2.0", + "@opentelemetry/sdk-trace-base": "2.2.0", + "@opentelemetry/sdk-trace-node": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", + "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node/node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.2.0.tgz", + "integrity": "sha512-+OaRja3f0IqGG2kptVeYsrZQK9nKRSpfFrKtRBq4uh6nIB8bTBgaGvYQrQoRrQWQMA5dK5yLhDMDc0dvYvCOIQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "2.2.0", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/sdk-trace-base": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/configuration": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/configuration/-/configuration-0.214.0.tgz", + "integrity": "sha512-Q+awuEwxhETwIAXuxHvIY5ZMEP0ZqvxLTi9kclrkyVJppEUXYL3Bhiw3jYrxdHYMh0Y0tVInQH9FEZ1aMinvLA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "yaml": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.6.1.tgz", + "integrity": "sha512-XHzhwRNkBpeP8Fs/qjGrAf9r9PRv67wkJQ/7ZPaBQQ68DYlTBBx5MF9LvPx7mhuXcDessKK2b+DcxqwpgkcivQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.1.tgz", + "integrity": "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.214.0.tgz", + "integrity": "sha512-SwmFRwO8mi6nndzbsjPgSFg7qy1WeNHRFD+s6uCsdiUDUt3+yzI2qiHE3/ub2f37+/CbeGcG+Ugc8Gwr6nu2Aw==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.6.1", + "@opentelemetry/otlp-exporter-base": "0.214.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.214.0", + "@opentelemetry/otlp-transformer": "0.214.0", + "@opentelemetry/sdk-logs": "0.214.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.214.0.tgz", + "integrity": "sha512-9qv2Tl/Hq6qc5pJCbzFJnzA0uvlb9DgM70yGJPYf3bA5LlLkRCpcn81i4JbcIH4grlQIWY6A+W7YG0LLvS1BAw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.214.0", + "@opentelemetry/core": "2.6.1", + "@opentelemetry/otlp-exporter-base": "0.214.0", + "@opentelemetry/otlp-transformer": "0.214.0", + "@opentelemetry/sdk-logs": "0.214.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/api-logs": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.214.0.tgz", + "integrity": "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.214.0.tgz", + "integrity": "sha512-IWAVvCO1TlpotRjFmhQFz9RSfQy5BsLtDRBtptSrXZRwfyRPpuql/RMe5zdmu0Gxl3ERDFwOzOqkf3bwy7Jzcw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.214.0", + "@opentelemetry/core": "2.6.1", + "@opentelemetry/otlp-exporter-base": "0.214.0", + "@opentelemetry/otlp-transformer": "0.214.0", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/sdk-logs": "0.214.0", + "@opentelemetry/sdk-trace-base": "2.6.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/api-logs": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.214.0.tgz", + "integrity": "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.214.0.tgz", + "integrity": "sha512-0NGxWHVYHgbp51SEzmsP+Hdups81eRs229STcSWHo3WO0aqY6RpJ9csxfyEtFgaNrBDv6UfOh0je4ss/ROS6XA==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.6.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.214.0", + "@opentelemetry/otlp-exporter-base": "0.214.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.214.0", + "@opentelemetry/otlp-transformer": "0.214.0", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/sdk-metrics": "2.6.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.214.0.tgz", + "integrity": "sha512-Tx/59RmjBgkXJ3qnsD04rpDrVWL53LU/czpgLJh+Ab98nAroe91I7vZ3uGN9mxwPS0jsZEnmqmHygVwB2vRMlA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/otlp-exporter-base": "0.214.0", + "@opentelemetry/otlp-transformer": "0.214.0", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/sdk-metrics": "2.6.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.214.0.tgz", + "integrity": "sha512-pJIcghFGhx3VSCgP5U+yZx+OMNj0t+ttnhC8IjL5Wza7vWIczctF6t3AGcVQffi2dEqX+ZHANoBwoPR8y6RMKA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.214.0", + "@opentelemetry/otlp-exporter-base": "0.214.0", + "@opentelemetry/otlp-transformer": "0.214.0", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/sdk-metrics": "2.6.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.214.0.tgz", + "integrity": "sha512-4TGYoZKebUWVuYkV6r5wS2dUF4zH7EbWFw/Uqz1ZM1tGHQeFT9wzHGXq3iSIXMUrwu5jRdxjfMaXrYejPu2kpQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/sdk-metrics": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.214.0.tgz", + "integrity": "sha512-FWRZ7AWoTryYhthralHkfXUuyO3l7cRsnr49WcDio1orl2a7KxT8aDZdwQtV1adzoUvZ9Gfo+IstElghCS4zfw==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.6.1", + "@opentelemetry/otlp-exporter-base": "0.214.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.214.0", + "@opentelemetry/otlp-transformer": "0.214.0", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/sdk-trace-base": "2.6.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.214.0.tgz", + "integrity": "sha512-kIN8nTBMgV2hXzV/a20BCFilPZdAIMYYJGSgfMMRm/Xa+07y5hRDS2Vm12A/z8Cdu3Sq++ZvJfElokX2rkgGgw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/otlp-exporter-base": "0.214.0", + "@opentelemetry/otlp-transformer": "0.214.0", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/sdk-trace-base": "2.6.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.214.0.tgz", + "integrity": "sha512-ON0spYWb2yAdQ9b+ItNyK0c6qdtcs+0eVR4YFJkhJL7agfT8sHFg0e5EesauSRiTHPZHiDobI92k77q0lwAmqg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/otlp-exporter-base": "0.214.0", + "@opentelemetry/otlp-transformer": "0.214.0", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/sdk-trace-base": "2.6.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.6.1.tgz", + "integrity": "sha512-km2/hD3inLTqtLnUAHDGz7ZP/VOyZNslrC/iN66x4jkmpckwlONW54LRPNI6fm09/musDtZga9EWsxgwnjGUlw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/sdk-trace-base": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.208.0.tgz", + "integrity": "sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.208.0", + "import-in-the-middle": "^2.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-amqplib": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.56.0.tgz", + "integrity": "sha512-/orV2zO2K7iGa1TR6lbs170LNNDbeTC6E3JF1EeB+okJ3rB5tl1gHFSjoqEDkQYFprNs5CPitqU8Y4l4S2Pkmg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-lambda": { + "version": "0.61.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.61.1.tgz", + "integrity": "sha512-leISmqN7/KSCYAKEVOAnQ0NUCa3rigB7ShCVLnYrHr6+7CXPef7C+nvowElMcYTid8egiHKgApR/FaNdlBda3A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/aws-lambda": "^8.10.155" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-sdk": { + "version": "0.64.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.64.1.tgz", + "integrity": "sha512-A8joPAuHwvwrkG5UpH7OYhzkeYznNBiG3o1TKoZ7yvyXU/q4CNxnZ7vzZBEpt9OocptCe6X/YyBENFSa0axqiw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.34.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-bunyan": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.54.0.tgz", + "integrity": "sha512-DnPoHSLcKwQmueW+7OOaXFD/cj1M6hqwTm6P88QdMbln/dqEatLxzt/ACPk4Yb5x4aU3ZLyeLyKxtzfhp76+aw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.208.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@types/bunyan": "1.8.11" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cassandra-driver": { + "version": "0.54.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.54.1.tgz", + "integrity": "sha512-wVGI4YrWmaNNtNjg84KTl8sHebG7jm3PHvmZxPl2V/aSskAyQMSxgJZpnv1dmBmJuISc+a8H8daporljbscCcQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-connect": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.52.0.tgz", + "integrity": "sha512-GXPxfNB5szMbV3I9b7kNWSmQBoBzw7MT0ui6iU/p+NIzVx3a06Ri2cdQO7tG9EKb4aKSLmfX9Cw5cKxXqX6Ohg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/connect": "3.4.38" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cucumber": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.24.0.tgz", + "integrity": "sha512-ICHrmax9PwU/Z+fehD0uIjM8W0cEvdToglV1+o76Mgw51HZBVp2Y3mkga1qMPIN5tPMoWUYoYtI4U85rea5HYg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dataloader": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.26.1.tgz", + "integrity": "sha512-S2JAM6lV16tMravuPLd3tJCC6ySb5a//5KgJeXutbTVb/UbSTXcnHSdEtMaAvE2KbazVWyWzcoytLRy6AUOwsw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dns": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.52.0.tgz", + "integrity": "sha512-XJvS8PkZec+X6HhOi1xldJydTpmIUAW14+1vyqwAK97LWKXlxmiWst8/fjZ709+CHgshz8i5V37yCHlr6o3pxw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-express": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.57.1.tgz", + "integrity": "sha512-r+ulPbvgG8rGgFFWbJWJpTh7nMzsEYH7rBFNWdFs7ZfVAtgpFijMkRtU7DecIo6ItF8Op+RxogSuk/083W8HKw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fastify": { + "version": "0.53.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.53.1.tgz", + "integrity": "sha512-tTa84J9rcrl4iTHdJDwirrNbM4prgJH+MF0iMlVLu++6gZg8TTfmYYqDiKPWBgdXB4M+bnlCkvgag36uV34uwA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fs": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.28.0.tgz", + "integrity": "sha512-FFvg8fq53RRXVBRHZViP+EMxMR03tqzEGpuq55lHNbVPyFklSVfQBN50syPhK5UYYwaStx0eyCtHtbRreusc5g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-generic-pool": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.52.0.tgz", + "integrity": "sha512-ISkNcv5CM2IwvsMVL31Tl61/p2Zm2I2NAsYq5SSBgOsOndT0TjnptjufYVScCnD5ZLD1tpl4T3GEYULLYOdIdQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-graphql": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.56.0.tgz", + "integrity": "sha512-IPvNk8AFoVzTAM0Z399t34VDmGDgwT6rIqCUug8P9oAGerl2/PEIYMPOl/rerPGu+q8gSWdmbFSjgg7PDVRd3Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.208.0.tgz", + "integrity": "sha512-8hFEQRAiOyIWO6LYj7tUfdAgNCuQUdYjLYMItRYlOLGJhshGdGYD7aeNzt2H+HPMDEWnKWqldIHfLTqM7ep7gg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "0.208.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-hapi": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.55.1.tgz", + "integrity": "sha512-Pm1HCHnnijUOGXd+nyJp96CfU8Lb6XdT6H6YvvmXO/NHMb6tV+EjzDRBr9sZ/XQjka9zLCz7jR0js7ut0IJAyg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.208.0.tgz", + "integrity": "sha512-rhmK46DRWEbQQB77RxmVXGyjs6783crXCnFjYQj+4tDH/Kpv9Rbg3h2kaNyp5Vz2emF1f9HOQQvZoHzwMWOFZQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/instrumentation": "0.208.0", + "@opentelemetry/semantic-conventions": "^1.29.0", + "forwarded-parse": "2.1.2" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", + "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation-ioredis": { + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.57.0.tgz", + "integrity": "sha512-o/PYGPbfFbS0Sq8EEQC8YUgDMiTGvwoMejPjV2d466yJoii+BUpffGejVQN0hC5V5/GT29m1B1jL+3yruNxwDw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/redis-common": "^0.38.2", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-kafkajs": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.18.1.tgz", + "integrity": "sha512-qM9hk7BIsVWqWJsrCa1fAEcEfutVvwhHO9kk4vpwaTGYR+lPWRk2r5+nEPcM+sIiYBmQNJCef5tEjQpKxTpP0A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.30.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-knex": { + "version": "0.53.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.53.1.tgz", + "integrity": "sha512-tIW3gqVC8d9CCE+oxPO63WNvC+5PKC/LrPrYWFobii5afUpHJV+0pfyt08okAFBHztzT0voMOEPGkLKoacZRXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.33.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-koa": { + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.57.1.tgz", + "integrity": "sha512-XPjdzgXvMG3YSZvsSgOj0Je0fsmlaBYIFFGJqUn1HRpbrVjdpP45eXI+6yUp48J8N5Qss32WDD5f+2tmV7Xvsg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.36.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0" + } + }, + "node_modules/@opentelemetry/instrumentation-lru-memoizer": { + "version": "0.53.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.53.1.tgz", + "integrity": "sha512-L93bPJKFzrObD4FvKpsavYEFTzXFKMmAeRHz7J4lUFc7TPZLouxX3PYW1+YGr/bT1y24H9NLNX66l7BW1s75QA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-memcached": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.52.1.tgz", + "integrity": "sha512-qg92SyWAypSZmX3Lhm2wz4BsovKarkWg9OHm4DPW6fGzmk40eB5voQIuctrBAfsml6gr+vbg4VEBcC1AKRvzzQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/memcached": "^2.2.6" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.62.0.tgz", + "integrity": "sha512-hcEEW26ToGVpQGblXk9m3p2cXkBu9j2bcyeevS/ahujr1WodfrItmMldWCEJkmN4+4uMo9pb6jAMhm6bZIMnig==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongoose": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.55.1.tgz", + "integrity": "sha512-M2MusLn/31YOt176Y6qXJQcpDuZPmq/fqQ9vIaKb4x/qIJ3oYO2lT45SUMFmZpODEhrpYXgGaEKwG6TGXhlosA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.55.0.tgz", + "integrity": "sha512-tEGaVMzqAlwhoDomaUWOP2H4KkK16m18qq+TZoyvcSe9O21UxnYFWQa87a4kmc7N4Q6Q70L/YhwDt+fC+NDRBA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/mysql": "2.15.27" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql2": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.55.1.tgz", + "integrity": "sha512-/cw7TzEmeaCQ5xi+FwrCWQUlY8v9RXjN5tqtb0D1sgBedfiV6DvW+dlMl1jo6Nkx9eSHmGcDy9IjyR0frHpKLg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@opentelemetry/sql-common": "^0.41.2" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-nestjs-core": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.55.0.tgz", + "integrity": "sha512-JFLNhbbEGnnQrMKOYoXx0nNk5N9cPeghu4xP/oup40a7VaSeYruyOiFbg9nkbS4ZQiI8aMuRqUT3Mo4lQjKEKg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.30.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-net": { + "version": "0.53.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.53.0.tgz", + "integrity": "sha512-d8tU5z0fx28z622RwVwU4zfNt40EKxzEcQcaPzch/CqpkKNAlvBOW/1K9OkjNdydpsKqxpMkbjvo3tY6PD1EMA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-openai": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-openai/-/instrumentation-openai-0.7.1.tgz", + "integrity": "sha512-QDnnAYxByJoJ3jMly/EwRbXhnfZpGigfBcHyPcgWEMR4bfawJZhdOdFi1GVcC4ImdS7fGaYQOTX1WW24mftISg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.208.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.36.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-oracledb": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-oracledb/-/instrumentation-oracledb-0.34.1.tgz", + "integrity": "sha512-RI5EV3ZIkHA748dPm4hwLkUnqYU/rrBcq+qA5cNks0dZaAgsu46XMA/MEPcubrBSsgaG1NAIG78P9NLZs2gN/A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@types/oracledb": "6.5.2" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.61.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.61.2.tgz", + "integrity": "sha512-l1tN4dX8Ig1bKzMu81Q1EBXWFRy9wqchXbeHDRniJsXYND5dC8u1Uhah7wz1zZta3fbBWflP2mJZcDPWNsAMRg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/sql-common": "^0.41.2", + "@types/pg": "8.15.6", + "@types/pg-pool": "2.0.6" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg/node_modules/@types/pg": { + "version": "8.15.6", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.6.tgz", + "integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pino": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.55.1.tgz", + "integrity": "sha512-rt35H5vvP9KA1xrMrJGsnqwcVxyt8dher04pR64gvX4rxLwsmijUF1cEMbPZ2O8jXpeV8nAIzGHBnWEYp5ILNA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.208.0", + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis": { + "version": "0.57.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.57.2.tgz", + "integrity": "sha512-vD1nzOUDOPjnvDCny7fmRSX/hMTFzPUCZKADF5tQ5DvBqlOEV/de/tOkwvIwo9YX956EBMT+8qSjhd7qPXFkRw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/redis-common": "^0.38.2", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-restify": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.54.0.tgz", + "integrity": "sha512-V6kCoAtU8jLuUi9hr3IEWVTHr8d8s4wObV1DlI/A+VzYToK1W4Zv1SI8x3hiF0yR1poRjOY6rl91Q427HHTMww==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-router": { + "version": "0.53.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.53.0.tgz", + "integrity": "sha512-3gF9jJ7C3lwlCOer1KzKKdpLr6/c7yOZBP44KI+Xi/TqiZjhsfUlHjetzC6BLDjkSk1DnIGyf+YzJR4aF5dJBQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-runtime-node": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.22.0.tgz", + "integrity": "sha512-27aodhzdWqPuPVWM2UsLYz2gl6yLRqLP7Z6Kn6ukUx/I+9oruaztJkLtYg4SqCrm/7Nsv9FIly7gO3/ZyDIMPg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-socket.io": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.55.1.tgz", + "integrity": "sha512-KQaOvZlw7NpA/VDRJdm45FIdzt4hXrDhvtmLU5a2AttcTI9e/VpVg4Y/LPOXM+o29VkKxETZzJfRlOJEIHl+uQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-tedious": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.28.0.tgz", + "integrity": "sha512-nQ9k1Bdk2yG4SPRuHZ+QVcc3YMm2sfsBV1MQIc/Y/OcN83Q+jA7gXgYgYIblQ1wI+/RtKlJpdl6hobAXuj+pSA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/tedious": "^4.0.14" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.19.0.tgz", + "integrity": "sha512-Pst/RhR61A2OoZQZkn6OLpdVpXp6qn3Y92wXa6umfJe9rV640r4bc6SWvw4pPN6DiQqPu2c8gnSSZPDtC6JlpQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.24.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.7.0" + } + }, + "node_modules/@opentelemetry/instrumentation-winston": { + "version": "0.53.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.53.0.tgz", + "integrity": "sha512-yF9v0DphyG715er1HG1pbweNUSygvc22xw2s2Y8E8oaEMJo2/nH3Ww/8c4K6gdI/6xvi2unla1KQBCYN4uCo8w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.208.0", + "@opentelemetry/instrumentation": "^0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.214.0.tgz", + "integrity": "sha512-u1Gdv0/E9wP+apqWf7Wv2npXmgJtxsW2XL0TEv9FZloTZRuMBKmu8cYVXwS4Hm3q/f/3FuCnPTgiwYvIqRSpRg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/otlp-transformer": "0.214.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.214.0.tgz", + "integrity": "sha512-IDP6zcyA24RhNZ289MP6eToIZcinlmirHjX8v3zKCQ2ZhPpt5cGwkN91tCth337lqHIgWcTy90uKRiX/SzALDw==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.6.1", + "@opentelemetry/otlp-exporter-base": "0.214.0", + "@opentelemetry/otlp-transformer": "0.214.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.214.0.tgz", + "integrity": "sha512-DSaYcuBRh6uozfsWN3R8HsN0yDhCuWP7tOFdkUOVaWD1KVJg8m4qiLUsg/tNhTLS9HUYUcwNpwL2eroLtsZZ/w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.214.0", + "@opentelemetry/core": "2.6.1", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/sdk-logs": "0.214.0", + "@opentelemetry/sdk-metrics": "2.6.1", + "@opentelemetry/sdk-trace-base": "2.6.1", + "protobufjs": "^7.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/api-logs": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.214.0.tgz", + "integrity": "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.6.1.tgz", + "integrity": "sha512-Dvz9TA6cPqIbxolSzQ5x9br6iQlqdGhVYrm+oYc7pfJ7LaVXz8F0XIqhWbnKB5YvfZ6SUmabBUUxnvHs/9uhxA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.6.1.tgz", + "integrity": "sha512-kKFMxBcjBZAC1vBch5mtZ/dJQvcAEKWga+c+q5iGgRLPIE6Mc649zEwMaCIQCzalziMJQiyUadFYMHmELB7AFw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/redis-common": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.3.tgz", + "integrity": "sha512-VCghU1JYs/4gP6Gqf/xro9MEsZ7LrMv2uONVsaESKL38ZOB9BqnI98FfS23wjMnHlpuE+TTaWSoAVNpTwYXzjw==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + } + }, + "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.32.0.tgz", + "integrity": "sha512-W+n4ZIbNndOaW6xlGeW7afKQeCMNlOHsSflGRLkzjnSfAl2tWJU5Mhr6hf/t6m8uhic71psHx/CoFQMsRQKnvQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-aws": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.22.0.tgz", + "integrity": "sha512-3kM+xfsSHs0hShUAx9IFhX5jniaXxJL3CTjbCMNCFS+EY7bswWbukV3au0jfqEgzeNxGxHEy8RLA1TOhAVLpTQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-azure": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.17.0.tgz", + "integrity": "sha512-JGNPW+Om8MNiVOK1Jl5jg3znGJQP7YeGsgRQiegftqEZj0th8e1Uf6U5s6H672KBT442WDGOG0a4du5xJgJB5w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.37.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-container": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.8.13.tgz", + "integrity": "sha512-44AKD/FOpC4amfsZkKvtymUnThWLBPTrZavv5tpRPVE3REEtFCbQV0rMT5lTRXmDkg8rk8M/2RX49NZdTSnpjw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-gcp": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.44.0.tgz", + "integrity": "sha512-sj9WSSjMyZJDP7DSmfQpsfivM2sQECwhjAmK6V97uVAeJiXSMiPhfo3fZi0Hpu+GQQ1Wb09qQIkwkMjwr0MH/g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "gcp-metadata": "^6.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.11.0.tgz", + "integrity": "sha512-Ie7+8q8MDF4FAEQCKVMTx3ReUvxiIAgIiiW3c9JdmP8+HMcDy20puT+AHjexnExgnbvBxjQ9fjkFDWrikJ2jQA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.11.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.11.0.tgz", + "integrity": "sha512-7YP44XH0tV6+Mb54x2YGf84i7yi+31MBZlE8JwvozkxyTvXbSp10X7cI7YE49ChJ3shMJoBmCJF3+1QFBJctGA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.214.0.tgz", + "integrity": "sha512-zf6acnScjhsaBUU22zXZ/sLWim1dfhUAbGXdMmHmNG3LfBnQ3DKsOCITb2IZwoUsNNMTogqFKBnlIPPftUgGwA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.214.0", + "@opentelemetry/core": "2.6.1", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/api-logs": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.214.0.tgz", + "integrity": "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.6.1.tgz", + "integrity": "sha512-9t9hJHX15meBy2NmTJxL+NJfXmnausR2xUDvE19XQce0Qi/GBtDGamU8nS1RMbdgDmhgpm3VaOu2+fiS/SfTpQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/resources": "2.6.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.214.0.tgz", + "integrity": "sha512-gl2XvQBJuPjhGcw9SsnQO5qxChAPMuGRPFaD8lqtF+Cey91NgGUQ0sio2vlDFOSm3JOLzc44vL+OAfx1dXuZjg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.214.0", + "@opentelemetry/configuration": "0.214.0", + "@opentelemetry/context-async-hooks": "2.6.1", + "@opentelemetry/core": "2.6.1", + "@opentelemetry/exporter-logs-otlp-grpc": "0.214.0", + "@opentelemetry/exporter-logs-otlp-http": "0.214.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.214.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.214.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.214.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.214.0", + "@opentelemetry/exporter-prometheus": "0.214.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.214.0", + "@opentelemetry/exporter-trace-otlp-http": "0.214.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.214.0", + "@opentelemetry/exporter-zipkin": "2.6.1", + "@opentelemetry/instrumentation": "0.214.0", + "@opentelemetry/otlp-exporter-base": "0.214.0", + "@opentelemetry/propagator-b3": "2.6.1", + "@opentelemetry/propagator-jaeger": "2.6.1", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/sdk-logs": "0.214.0", + "@opentelemetry/sdk-metrics": "2.6.1", + "@opentelemetry/sdk-trace-base": "2.6.1", + "@opentelemetry/sdk-trace-node": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/api-logs": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.214.0.tgz", + "integrity": "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/instrumentation": { + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.214.0.tgz", + "integrity": "sha512-MHqEX5Dk59cqVah5LiARMACku7jXSVk9iVDWOea4x3cr7VfdByeDCURK6o1lntT1JS/Tsovw01UJrBhN3/uC5w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.214.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/es-module-lexer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-3.0.2.tgz", + "integrity": "sha512-BuIB67FngDSyQ/dpQNOZybwdEBDUGJQvOqwWr4ha/ufYiqzuEwPkKO2zLhRAgay28tStRIHUeWmszZAJo3GCOg==", + "license": "MIT" + }, + "node_modules/@opentelemetry/sdk-node/node_modules/import-in-the-middle": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.5.1.tgz", + "integrity": "sha512-mPKuL8bPQzecui2KK6Gb+M8JvJoHnhS1FeYGa22QopBmlevF5F0FE6ued/B5EgHDeIoMTONIpDWWFKUPOG0DBQ==", + "license": "Apache-2.0", + "dependencies": { + "cjs-module-lexer": "^2.2.0", + "es-module-lexer": "^3.0.2", + "module-details-from-path": "^1.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.6.1.tgz", + "integrity": "sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/resources": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/resources": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", + "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.6.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.6.1.tgz", + "integrity": "sha512-Hh2i4FwHWRFhnO2Q/p6svMxy8MPsNCG0uuzUY3glqm0rwM0nQvbTO1dXSp9OqQoTKXcQzaz9q1f65fsurmOhNw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "2.6.1", + "@opentelemetry/core": "2.6.1", + "@opentelemetry/sdk-trace-base": "2.6.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", + "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.41.2.tgz", + "integrity": "sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@paralleldrive/cuid2/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@phosphor-icons/webcomponents": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@phosphor-icons/webcomponents/-/webcomponents-2.1.5.tgz", + "integrity": "sha512-JcvQkZxvcX2jK+QCclm8+e8HXqtdFW9xV4/kk2aL9Y3dJA2oQVt+pzbv1orkumz3rfx4K9mn9fDoMr1He1yr7Q==", + "license": "MIT", + "dependencies": { + "lit": "^3" + } + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, + "node_modules/@playwright/test": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0.tgz", + "integrity": "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@preact/signals-core": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.14.3.tgz", + "integrity": "sha512-m0K3vnbSLC5rHs2ZVfeAMvBtT1zIyq4mxx5OlNncSgMj5Iz6W5Rn3kPrDxAC+iIKmiVe0lSl6U37t5ZkEWoVAw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", @@ -5143,9 +7357,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5160,9 +7371,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5177,9 +7385,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5194,9 +7399,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5211,9 +7413,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5228,9 +7427,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5245,9 +7441,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5262,9 +7455,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5279,9 +7469,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5296,9 +7483,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -5313,9 +7497,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5330,9 +7511,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -5347,9 +7525,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -7070,6 +9245,12 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/aws-lambda": { + "version": "8.10.163", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.163.tgz", + "integrity": "sha512-+4zuoEB3S8RIhimtOFT7zAEk2SbpwrKjjGl9CyYnQR6k08uynxfauIIB0pDU1ssr05F8oyGiETwpn+8eXZMqPw==", + "license": "MIT" + }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", @@ -7081,6 +9262,15 @@ "@types/node": "*" } }, + "node_modules/@types/bunyan": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", + "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -7165,11 +9355,14 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "license": "MIT" + "node_modules/@types/memcached": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.10.tgz", + "integrity": "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/mime": { "version": "1.3.5", @@ -7178,6 +9371,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mysql": { + "version": "2.15.27", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", + "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "22.20.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.4.tgz", @@ -7193,11 +9395,19 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, + "node_modules/@types/oracledb": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@types/oracledb/-/oracledb-6.5.2.tgz", + "integrity": "sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/pg": { "version": "8.23.1", "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.23.1.tgz", "integrity": "sha512-fKVHpikPdg4GKks3JuLEhvwSyvwzF23hnabPy6DD8ljVbC7+6J5dQzdv4arV6jqq57djnMgs1HKBxX4P8aBI3A==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -7205,6 +9415,15 @@ "pg-types": "^2.2.0" } }, + "node_modules/@types/pg-pool": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.6.tgz", + "integrity": "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==", + "license": "MIT", + "dependencies": { + "@types/pg": "*" + } + }, "node_modules/@types/qs": { "version": "6.15.1", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", @@ -7296,6 +9515,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/tedious": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", + "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -7309,9 +9537,9 @@ "license": "MIT" }, "node_modules/@types/ws": { - "version": "7.4.7", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", - "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "license": "MIT", "dependencies": { "@types/node": "*" @@ -8143,7 +10371,6 @@ "version": "8.18.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -8152,6 +10379,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -8963,6 +11199,12 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/cjs-module-lexer": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.1.tgz", + "integrity": "sha512-Ca8swihM+/4yKecYHY52kgJd300hi2lADU/a1RxNTRe+RJ9jvqQlESpbz9DnG9mowez8qwXHB8qYdIUw9e+F5Q==", + "license": "MIT" + }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -9636,6 +11878,15 @@ "node": ">=0.10" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -10047,7 +12298,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -10500,6 +12750,27 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/express/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/express/node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", @@ -10856,6 +13127,12 @@ "node": ">= 0.6" } }, + "node_modules/forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", + "license": "MIT" + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -10952,6 +13229,84 @@ "url": "https://github.com/sponsors/krisk" } }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/generator-function": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", @@ -11185,6 +13540,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -11378,24 +13742,13 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "node_modules/html-parse-stringify": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.1.0.tgz", + "integrity": "sha512-E0oAXcELOtsXe+BmpJ2EZyedbldPpriV5vICzEuo6xjC/D1lDukOI7KrpfQGF2Qc4wWEy0nk3bFORS2K5ZAhFQ==", "license": "MIT", "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "void-elements": "3.1.0" } }, "node_modules/https-proxy-agent": { @@ -11446,6 +13799,55 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/i18next": { + "version": "25.10.10", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.10.10.tgz", + "integrity": "sha512-cqUW2Z3EkRx7NqSyywjkgCLK7KLCL6IFVFcONG7nVYIJ3ekZ1/N5jUsihHV6Bq37NfhgtczxJcxduELtjTwkuQ==", + "funding": [ + { + "type": "individual", + "url": "https://www.locize.com/i18next" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + }, + { + "type": "individual", + "url": "https://www.locize.com" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2" + }, + "peerDependencies": { + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/i18next-http-backend": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.6.tgz", + "integrity": "sha512-mBOqy8993jtqAoj6XaI1XeC/8/9v6EPS+681ziegrPvTB0DoaCY7PpTS0SpY56qLMoS4OI1TZEM2Zf59zNh05w==", + "license": "MIT", + "dependencies": { + "cross-fetch": "4.1.0" + } + }, + "node_modules/i18next-http-backend/node_modules/cross-fetch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", + "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, "node_modules/iceberg-js": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", @@ -11527,6 +13929,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-in-the-middle": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", + "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.15.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -12302,6 +14716,15 @@ "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", "license": "MIT" }, + "node_modules/jayson/node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/jayson/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -12467,6 +14890,24 @@ "node": ">=6" } }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-bigint/node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -13034,6 +15475,12 @@ "node": ">=8" } }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -13192,23 +15639,10 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/loglevel": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", - "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - }, - "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/loglevel" - } - }, "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0" }, "node_modules/lru-cache": { @@ -13445,6 +15879,12 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -14500,6 +16940,29 @@ ], "license": "MIT" }, + "node_modules/protobufjs": { + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz", + "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -14638,6 +17101,33 @@ "react": "^19.2.7" } }, + "node_modules/react-i18next": { + "version": "16.6.6", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.6.6.tgz", + "integrity": "sha512-ZgL2HUoW34UKUkOV7uSQFE1CDnRPD+tCR3ywSuWH7u2iapnz86U8Bi3Vrs620qNDzCf1F47NxglCEkchCTDOHw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "html-parse-stringify": "^3.0.1", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "i18next": ">= 25.10.9", + "react": ">= 16.8.0", + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/react-map-gl": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-8.1.1.tgz", @@ -14878,6 +17368,19 @@ "node": ">=0.10.0" } }, + "node_modules/require-in-the-middle": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", + "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3" + }, + "engines": { + "node": ">=9.3.0 || >=8.10.0 <9.0.0" + } + }, "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -15279,15 +17782,6 @@ "utf-8-validate": "^6.0.0" } }, - "node_modules/rpc-websockets/node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/rpc-websockets/node_modules/uuid": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", @@ -17045,6 +19539,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.7.0.tgz", + "integrity": "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/utf-8-validate": { "version": "6.0.6", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.6.tgz", @@ -17445,6 +19948,15 @@ } } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -17980,7 +20492,6 @@ "version": "2.9.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", - "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index f8beaf58..47cd5d9f 100644 --- a/package.json +++ b/package.json @@ -7,19 +7,15 @@ "scripts": { "dev": "node scripts/dev.mjs", "dev:vite": "vite", - "server": "tsx server/index.ts", + "server": "tsx server/index.js", "server:js": "node server/index.js", - "start": "tsx server/index.ts", + "start": "tsx server/index.js", "dev:all": "node scripts/dev.mjs", "build": "vite build", "preview": "vite preview", "test": "vitest run", - "benchmark:routing": "vitest run test/routing-benchmark-spike.test.js --reporter=verbose", - "security:lockfiles": "node scripts/verify-lockfile-hashes.js", - "security:lockfiles:offline": "node scripts/verify-lockfile-hashes.js --offline", - "security:install": "bash scripts/sandbox-install.sh", - "security:scan-lifecycle": "bash scripts/sandbox-install.sh --scan", - "security:ai-code": "node scripts/audit-ai-code.js", + "test:server-features": "node --test server/tests/telemetry-ws.test.js", + "test:ws-cluster": "node server/tests/ws-cluster.js", "test:watch": "vitest", "test:e2e:throttling": "playwright test --project=throttling", "lint": "eslint .", @@ -47,6 +43,7 @@ "@types/pg": "^8.23.1", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@types/ws": "^8.18.1", "@vitejs/plugin-react": "^6.0.2", "eslint": "^9.0.0", "fake-indexeddb": "^6.2.5", @@ -66,6 +63,10 @@ "@apollo/server": "^5.5.1", "@as-integrations/express4": "^1.1.2", "@aztec/bb.js": "^0.87.9", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/auto-instrumentations-node": "^0.67.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.214.0", + "@opentelemetry/sdk-node": "^0.214.0", "@creit-tech/stellar-wallets-kit": "^2.4.0", "@noir-lang/noir_js": "^1.0.0-beta.9", "@stellar/stellar-sdk": "^16.0.0", @@ -75,6 +76,8 @@ "dataloader": "^2.2.3", "express": "^4.21.0", "fuse.js": "^7.5.0", + "i18next": "^25.8.13", + "i18next-http-backend": "^3.0.2", "express-rate-limit": "^8.6.2", "ioredis": "^5.4.1", "graphql": "^16.14.2", @@ -82,7 +85,9 @@ "pg": "^8.23.0", "react": "^19.2.7", "react-dom": "^19.2.7", + "react-i18next": "^16.5.4", "react-map-gl": "^8.1.1", - "react-router-dom": "^7.18.0" + "react-router-dom": "^7.18.0", + "ws": "^8.18.3" } } diff --git a/public/locales/ar/common.json b/public/locales/ar/common.json new file mode 100644 index 00000000..a5a3db4f --- /dev/null +++ b/public/locales/ar/common.json @@ -0,0 +1,19 @@ +{ + "nav": { + "howItWorks": "كيف يعمل", + "trustSafety": "الثقة والسلامة", + "coverage": "التغطية", + "ranking": "المستجيبون", + "requestHelp": "اطلب المساعدة" + }, + "hero": { + "headline": "تصل المساعدة عبر الناس.", + "sub": "أرسل طلباً، واظهر على الخريطة، ودع مجتمعك يستجيب.", + "cta_primary": "احصل على المساعدة", + "cta_secondary": "انضم إلى المجتمع" + }, + "footer": { + "tagline": "شبكة مساعدة مجتمعية تصل إليك عبر أقرب شخص.", + "copyright": "© 2026 HelPhone · صُممت للجيران" + } +} diff --git a/public/locales/ar/help.json b/public/locales/ar/help.json new file mode 100644 index 00000000..76fbef8d --- /dev/null +++ b/public/locales/ar/help.json @@ -0,0 +1,14 @@ +{ + "nav": { "back": "رجوع" }, + "modes": { "getHelp": "اطلب المساعدة", "offerHelp": "قدّم المساعدة" }, + "getHelp": { + "heading": "اطلب المساعدة بالقرب منك", + "sub": "أكمل الخطوات أدناه. سيتم تنبيه الأشخاص القريبين.", + "step1": { + "title": "موقعك", + "allowAccess": "السماح بالوصول إلى الموقع", + "searchPlaceholder": "أو ابحث عن مدينة أو بلد...", + "searchButton": "بحث" + } + } +} diff --git a/render.yaml b/render.yaml index 9746f1e0..6301110b 100644 --- a/render.yaml +++ b/render.yaml @@ -24,6 +24,12 @@ services: # are stored in Redis and managed via /admin/whitelist (see docs/security-runbook.md). - key: REDIS_URL sync: false + # Set OTEL_EXPORTER_OTLP_ENDPOINT to an OTLP/HTTP collector to export traces. + - key: OTEL_EXPORTER_OTLP_ENDPOINT + sync: false + # Protect GET /metrics when the backend is exposed to the public internet. + - key: METRICS_BEARER_TOKEN + sync: false - key: WHITELIST_ADMIN_TOKEN sync: false # Number of proxy hops in front of the app so req.ip is the real client. diff --git a/server/index.js b/server/index.js index f00f7793..46709f10 100644 --- a/server/index.js +++ b/server/index.js @@ -1,4 +1,6 @@ +import "./telemetryBootstrap.js"; import cluster from "node:cluster"; +import { createServer } from "node:http"; import express from "express"; import compression from "compression"; import { readFileSync } from "fs"; @@ -22,6 +24,11 @@ import { } from "./middleware/whitelist.js"; import { startMaintenanceScheduler } from "./db/maintenance.js"; import { getMaintenanceConfig } from "./env.js"; +import { telemetryErrorHandler, traceIdFromRequest, shutdownTelemetry } from "./telemetry.js"; +import { requestMetrics, renderMetrics, observeDuration } from "./middleware/metrics.js"; +import { connectEventBus, publishContractEvent, closeEventBus } from "./lib/redisPubSub.js"; +import { attachWebSocketRelay } from "./routes/wsCluster.js"; +import { createHealthRouter } from "./routes/health.js"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -128,6 +135,21 @@ app.use(poolMonitorMiddleware); app.use(createCorsMiddleware()); app.use(compression()); app.use(express.json({ limit: "1mb" })); +app.use(requestMetrics); +app.use((req, res, next) => { + const traceId = traceIdFromRequest(req); + if (traceId) res.setHeader("x-trace-id", traceId); + next(); +}); +app.use("/health", createHealthRouter({ readiness: () => _ready })); +app.get("/metrics", (req, res) => { + const token = process.env.METRICS_BEARER_TOKEN; + if (token && req.get("authorization") !== `Bearer ${token}`) { + return res.status(401).end(); + } + res.type("text/plain; version=0.0.4; charset=utf-8").send(renderMetrics()); +}); +app.post("/api/telemetry/errors", (req, res) => telemetryErrorHandler(req, res)); // Behind a proxy (Render), req.ip is the proxy unless TRUST_PROXY is set to the // number of hops (e.g. "1"); whitelist matching and rate limiting both use it. @@ -194,6 +216,7 @@ function decodeTopicSymbol(topicScVal) { } function broadcast(event) { + void publishContractEvent(event).catch((err) => console.error("[events] relay publish failed:", err.message || err)); const payload = `data: ${JSON.stringify(event)}\n\n`; for (const res of sseClients) { res.write(payload); @@ -208,6 +231,7 @@ async function seedCursor() { } async function pollContractEvents() { + const startedAt = process.hrtime.bigint(); try { if (eventCursor === null) { eventCursor = await seedCursor(); @@ -230,6 +254,9 @@ async function pollContractEvents() { // A single failed poll must not kill the loop or drop the cursor — // just retry next tick. Log so operators can notice sustained failure. console.error("[events] poll failed:", err.message || err); + } finally { + observeDuration("helphone_soroban_rpc_duration_seconds", { operation: "get_events" }, + Number(process.hrtime.bigint() - startedAt) / 1e9); } } @@ -307,6 +334,8 @@ app.get("/health/pool", (req, res) => { }); app.post("/zk/prove", async (req, res) => { + const startedAt = process.hrtime.bigint(); + let outcome = "success"; try { const { inputs } = req.body; if (!inputs) { @@ -333,8 +362,12 @@ app.post("/zk/prove", async (req, res) => { nullifier, }); } catch (err) { + outcome = "error"; console.error("[prover] Error:", err); res.status(500).json({ success: false, error: err.message }); + } finally { + observeDuration("helphone_zk_proof_duration_seconds", { outcome }, + Number(process.hrtime.bigint() - startedAt) / 1e9); } }); @@ -571,14 +604,24 @@ export function startServer() { .then(({ runMigrationsAtStartup }) => runMigrationsAtStartup()) .catch((err) => console.error("[migrate] startup failure:", err)); - return app.listen(PORT, () => { - const server = app.listen(PORT, () => { + void connectEventBus().catch((err) => console.error("[events] Redis relay unavailable:", err.message)); + const server = createServer(app); + const relay = attachWebSocketRelay(server); + server.listen(PORT, () => { // Off-peak VACUUM ANALYZE / REINDEX CONCURRENTLY (opt-in: DB_MAINTENANCE_ENABLED=true) if (getMaintenanceConfig().enabled) startMaintenanceScheduler(); console.log(`ZK Prover worker ${process.pid} on http://localhost:${PORT}`); ensureProver().catch((err) => console.error("[prover] Init failed:", err)); }); applyKeepAliveTuning(server); + const shutdown = () => { + relay.close().catch((err) => console.error("[events] WebSocket shutdown failed:", err.message)); + closeEventBus().catch((err) => console.error("[events] Redis shutdown failed:", err.message)); + shutdownTelemetry().catch((err) => console.error("[telemetry] shutdown failed:", err.message)); + server.close(); + }; + process.once("SIGTERM", shutdown); + process.once("SIGINT", shutdown); return server; } diff --git a/server/index.ts b/server/index.ts index 66632e0e..d5e89911 100644 --- a/server/index.ts +++ b/server/index.ts @@ -1,147 +1,5 @@ -import express, { Request, Response } from 'express' -import cors from 'cors' -import { readFileSync } from 'fs' -import { dirname, join } from 'path' -import { fileURLToPath } from 'url' -import { - rpc, - TransactionBuilder, - Operation, - Account, - Keypair, - BASE_FEE, - Networks, - nativeToScVal, - SorobanDataBuilder, -} from '@stellar/stellar-sdk' -import { SorobanStateExporter, loadLatestSnapshot } from './indexer/exporter.js' -import { authMiddleware } from './middleware/auth.js' -import { createCspMiddleware, createHtmlHandler } from './middleware/csp.js' -import { getStats, pingDatabase, query } from './db/connection.js' -import { createGraphQLHandler } from './graphql/server.js' - -const __dirname = dirname(fileURLToPath(import.meta.url)) -const app = express() -const PORT = process.env.PORT || 3001 - -// Reuse TCP sockets across sequential requests (see middleware/keepAlive.ts) -app.use(keepAliveMiddleware()) - -app.use( - cors({ - origin: process.env.ALLOWED_ORIGINS - ? process.env.ALLOWED_ORIGINS.split(',') - : ['https://helphone.com', 'https://staging.helphone.com', 'http://localhost:3000'], - methods: ['GET', 'POST', 'OPTIONS'], - preflightContinue: false, - optionsSuccessStatus: 204, - }) -) -app.use(createCspMiddleware()) -app.use(express.json({ limit: '1mb' })) - -// Behind a proxy (Render), req.ip is the proxy unless TRUST_PROXY is set to the -// number of hops (e.g. "1"); whitelist matching and rate limiting both use it. -if (process.env.TRUST_PROXY) { - const hops = Number(process.env.TRUST_PROXY) - app.set('trust proxy', Number.isNaN(hops) ? process.env.TRUST_PROXY : hops) -} - -// Verified emergency-service subnets / API keys bypass rate limiting. The -// whitelist must run before the limiter, which honours `req.bypassRateLimit`. -export const whitelistStore = createWhitelistStore(createDefaultRedisClient()) -const whitelist = createWhitelistMiddleware(whitelistStore) -app.use(whitelist) -app.use( - '/admin/whitelist', - createWhitelistAdminRouter({ - store: whitelistStore, - adminToken: process.env.WHITELIST_ADMIN_TOKEN, - onChange: () => whitelist.invalidate(), - }) -) -if (process.env.NODE_ENV !== 'test') app.use(generalLimiter) - -const stateExporter = new SorobanStateExporter() - -// Health Check Endpoints -app.get('/health', (_req: Request, res: Response) => { - res.json({ status: 'ok', server: 'helphone-indexer-server', timestamp: new Date().toISOString() }) -}) - -app.get('/zk/health', (_req: Request, res: Response) => { - res.json({ status: 'ready', ready: true }) -}) - -// GraphQL aggregation layer (#528), alongside the REST routes below. -app.use( - '/graphql', - createGraphQLHandler({ - query: (sql, params) => query(sql, params) as Promise<{ rows: Record[] }>, - health: { ping: pingDatabase, stats: getStats }, - }) -) - -// Soroban State Export Endpoints -app.post('/api/state/export', async (_req: Request, res: Response) => { - try { - const snapshot = await stateExporter.exportState() - res.json({ success: true, snapshot }) - } catch (err: any) { - res.status(500).json({ success: false, error: err.message }) - } -}) - -app.get('/api/state/snapshots/latest', (_req: Request, res: Response) => { - try { - const snapshot = loadLatestSnapshot() - if (!snapshot) { - return res.status(404).json({ success: false, error: 'No snapshots available' }) - } - res.json({ success: true, snapshot }) - } catch (err: any) { - res.status(500).json({ success: false, error: err.message }) - } -}) - -// Secure Authenticated Endpoint Example using Cryptographic Auth Middleware -app.post('/api/protected/action', authMiddleware, (req: Request, res: Response) => { - res.json({ success: true, message: 'Authenticated payload verified successfully', user: (req as any).authenticatedUser }) -}) - -// Built frontend (nonce-injected HTML). Only active when `vite build` output -// exists, so an API-only deployment keeps behaving exactly as before. -const DIST_DIR = join(__dirname, '..', 'dist') -app.use(express.static(DIST_DIR, { index: false })) -app.get(/^\/(?!api\/|zk\/|health$|metrics).*/, createHtmlHandler({ htmlPath: join(DIST_DIR, 'index.html') })) - -// Automated Daily State Snapshot Cron (Interval fallback) -const CRON_INTERVAL_MS = 24 * 60 * 60 * 1000 -let exporterInterval: NodeJS.Timeout | null = null - -function scheduleStateBackup() { - console.log('[server] Initializing Soroban Contract State Daily Backup Cron...') - stateExporter.exportState().catch((err) => console.error('[server] Initial state backup error:', err)) - - exporterInterval = setInterval(() => { - console.log('[server] Executing scheduled daily state snapshot export...') - stateExporter.exportState().catch((err) => console.error('[server] Scheduled state backup error:', err)) - }, CRON_INTERVAL_MS) -} +import { startServer } from './index.js' if (process.env.NODE_ENV !== 'test') { - // Apply schema migrations automatically at boot so the database schema - // stays in sync on every deploy (non-fatal; server serves even on failure). - void runMigrationsAtStartup() - - app.listen(PORT, () => { - const server = app.listen(PORT, () => { - console.log(`HelPhone Server running on http://localhost:${PORT}`) - // Off-peak VACUUM ANALYZE / REINDEX CONCURRENTLY (opt-in: DB_MAINTENANCE_ENABLED=true) - if (getMaintenanceConfig().enabled) startMaintenanceScheduler() - scheduleStateBackup() - }) - applyKeepAliveTuning(server) + startServer() } - -export { app, stateExporter } diff --git a/server/lib/redisPubSub.js b/server/lib/redisPubSub.js new file mode 100644 index 00000000..40b7dce2 --- /dev/null +++ b/server/lib/redisPubSub.js @@ -0,0 +1,68 @@ +import { EventEmitter } from "node:events"; + +const CHANNEL = "helphone:contract-events:v1"; +const localBus = new EventEmitter(); +let publisher; +let subscriber; +let connecting; + +export async function connectEventBus({ redisUrl = process.env.REDIS_URL } = {}) { + if (!redisUrl) return false; + if (connecting) return connecting; + connecting = (async () => { + const Redis = (await import("ioredis")).default; + publisher = new Redis(redisUrl, { maxRetriesPerRequest: 2, lazyConnect: false }); + subscriber = new Redis(redisUrl, { maxRetriesPerRequest: 2, lazyConnect: false }); + publisher.on("error", (error) => console.error("[events:redis-publisher]", error.message)); + subscriber.on("error", (error) => console.error("[events:redis-subscriber]", error.message)); + subscriber.on("message", (channel, message) => { + if (channel !== CHANNEL) return; + try { + const event = JSON.parse(message); + if (isContractEvent(event)) localBus.emit("event", event); + } catch (error) { + console.error("[events:redis] discarded malformed event", error.message); + } + }); + await subscriber.subscribe(CHANNEL); + return true; + })().catch((error) => { + publisher?.disconnect(); + subscriber?.disconnect(); + publisher = undefined; + subscriber = undefined; + connecting = undefined; + throw error; + }); + return connecting; +} + +function isContractEvent(value) { + return value && typeof value === "object" && + typeof value.topic === "string" && value.topic.length <= 64 && + Number.isSafeInteger(value.ledger) && typeof value.id === "string" && value.id.length <= 256 && + (value.tenantId === undefined || (typeof value.tenantId === "string" && /^[A-Za-z0-9_-]{1,128}$/.test(value.tenantId))); +} + +export async function publishContractEvent(event) { + if (!isContractEvent(event)) throw new TypeError("Invalid contract event"); + if (publisher) { + const firstPublisher = await publisher.set(`helphone:event:seen:${event.id}`, "1", "EX", 60, "NX"); + if (firstPublisher !== "OK") return false; + await publisher.publish(CHANNEL, JSON.stringify(event)); + } + else localBus.emit("event", event); + return true; +} + +export function subscribeContractEvents(handler) { + localBus.on("event", handler); + return () => localBus.off("event", handler); +} + +export async function closeEventBus() { + await Promise.all([publisher?.quit(), subscriber?.quit()].filter(Boolean)); + publisher = undefined; + subscriber = undefined; + connecting = undefined; +} diff --git a/server/middleware/logger.js b/server/middleware/logger.js index 29615e87..d7625ce9 100644 --- a/server/middleware/logger.js +++ b/server/middleware/logger.js @@ -8,6 +8,10 @@ export function logger(opts = {}) { return (req, res, next) => { const start = Date.now(); const { method, originalUrl } = req; + const traceparent = req.get('traceparent'); + const traceId = typeof traceparent === 'string' && /^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/i.test(traceparent) + ? traceparent.split('-')[1].toLowerCase() + : null; const originalEnd = res.end.bind(res); let logged = false; const doLog = () => { @@ -20,7 +24,7 @@ export function logger(opts = {}) { try { if (opts.includePoolStats || req.query.pool === '1' || process.env.DEBUG_POOL) pool = getPool().getStats(); } catch {} - const entry = { ts: new Date().toISOString(), method, url: originalUrl, status, durationMs: duration, bytes: len, ip: req.ip, ua: req.get('user-agent')?.slice(0, 120) }; + const entry = { ts: new Date().toISOString(), traceId, method, url: originalUrl, status, durationMs: duration, bytes: len, ip: req.ip, ua: req.get('user-agent')?.slice(0, 120) }; if (pool) entry.pool = pool; if (duration > slowThreshold) entry.slow = true; const line = JSON.stringify(entry); diff --git a/server/middleware/logger.ts b/server/middleware/logger.ts index 828ccec2..ee6f6654 100644 --- a/server/middleware/logger.ts +++ b/server/middleware/logger.ts @@ -31,6 +31,10 @@ export function logger(opts: LoggerOptions = {}) { return (req: Request, res: Response, next: NextFunction) => { const start = Date.now(); const { method, originalUrl } = req; + const traceparent = req.get('traceparent'); + const traceId = typeof traceparent === 'string' && /^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/i.test(traceparent) + ? traceparent.split('-')[1].toLowerCase() + : null; // Capture end to log const originalEnd = res.end.bind(res) as typeof res.end; @@ -47,6 +51,7 @@ export function logger(opts: LoggerOptions = {}) { const entry: Record = { ts: new Date().toISOString(), + traceId, method, url: originalUrl, status, diff --git a/server/middleware/metrics.js b/server/middleware/metrics.js new file mode 100644 index 00000000..135cae19 --- /dev/null +++ b/server/middleware/metrics.js @@ -0,0 +1,63 @@ +const buckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5]; +const counts = new Map(); +const durations = new Map(); + +export function requestMetrics(req, res, next) { + const startedAt = process.hrtime.bigint(); + res.on("finish", () => { + const route = req.route?.path ? `${req.baseUrl}${req.route.path}` : "unmatched"; + const labels = { method: req.method, route, status: String(res.statusCode) }; + const key = Object.values(labels).join("\0"); + counts.set(key, { labels, value: (counts.get(key)?.value || 0) + 1 }); + observeDuration("helphone_http_request_duration_seconds", labels, + Number(process.hrtime.bigint() - startedAt) / 1e9); + }); + next(); +} + +export function observeDuration(name, labels, seconds) { + if (!Number.isFinite(seconds) || seconds < 0) return; + const labelKey = Object.entries(labels).sort().map(([key, value]) => `${key}=${value}`).join("\0"); + const key = `${name}\0${labelKey}`; + const entry = durations.get(key) || { name, labels, count: 0, sum: 0, buckets: Array(buckets.length).fill(0) }; + entry.count += 1; + entry.sum += seconds; + const bucket = buckets.findIndex((bound) => seconds <= bound); + if (bucket >= 0) entry.buckets[bucket] += 1; + durations.set(key, entry); +} + +export function renderMetrics() { + const lines = []; + const addFamily = (name, help, type, samples) => { + lines.push(`# HELP ${name} ${help}`, `# TYPE ${name} ${type}`); + for (const sample of samples) { + const labels = Object.entries(sample.labels || {}); + const suffix = labels.length + ? `{${labels.map(([key, value]) => `${key}="${String(value).replaceAll("\\", "\\\\").replaceAll("\n", "\\n").replaceAll('"', '\\"')}"`).join(",")}}` + : ""; + lines.push(`${name}${sample.suffix ? `_${sample.suffix}` : ""}${suffix} ${sample.value}`); + } + }; + + addFamily("helphone_http_requests_total", "HTTP requests handled by this worker.", "counter", [...counts.values()]); + const families = new Map(); + for (const entry of durations.values()) { + const family = families.get(entry.name) || []; + let cumulative = 0; + buckets.forEach((bound, index) => { + cumulative += entry.buckets[index]; + family.push({ labels: { ...entry.labels, le: bound }, value: cumulative, suffix: "bucket" }); + }); + family.push({ labels: { ...entry.labels, le: "+Inf" }, value: entry.count, suffix: "bucket" }); + family.push({ labels: entry.labels, value: entry.sum, suffix: "sum" }, { labels: entry.labels, value: entry.count, suffix: "count" }); + families.set(entry.name, family); + } + for (const [name, samples] of families) addFamily(name, "Observed operation durations in seconds.", "histogram", samples); + return `${lines.join("\n")}\n`; +} + +export function resetMetrics() { + counts.clear(); + durations.clear(); +} diff --git a/server/middleware/metrics.ts b/server/middleware/metrics.ts index aa0afa9e..30836320 100644 --- a/server/middleware/metrics.ts +++ b/server/middleware/metrics.ts @@ -15,12 +15,13 @@ export type Labels = Record export interface MetricSample { labels?: Labels value: number + suffix?: 'bucket' | 'sum' | 'count' } export interface MetricFamily { name: string help: string - type: 'gauge' | 'counter' + type: 'gauge' | 'counter' | 'histogram' samples: MetricSample[] } @@ -53,7 +54,7 @@ export function renderPrometheus(families: MetricFamily[]): string { const labelStr = labels.length ? `{${labels.map(([k, v]) => `${k}="${escapeLabelValue(v)}"`).join(',')}}` : '' - lines.push(`${f.name}${labelStr} ${formatValue(s.value)}`) + lines.push(`${f.name}${s.suffix ? `_${s.suffix}` : ''}${labelStr} ${formatValue(s.value)}`) } } return lines.join('\n') + '\n' @@ -64,14 +65,33 @@ export function renderPrometheus(families: MetricFamily[]): string { // its own counts, which Prometheus sums across scrape targets. const requestCounts = new Map() +const HTTP_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5] +const durations = new Map() + +export function observeDuration(name: string, help: string, labels: Labels, seconds: number): void { + if (!/^[a-zA-Z_:][a-zA-Z0-9_:]*$/.test(name) || !Number.isFinite(seconds) || seconds < 0) return + const key = `${name}\u0000${Object.entries(labels).sort().map(([k, v]) => `${k}=${v}`).join('\u0000')}` + const entry = durations.get(key) ?? { labels, count: 0, sum: 0, buckets: Array(HTTP_BUCKETS.length).fill(0) } + entry.count += 1 + entry.sum += seconds + const bucket = HTTP_BUCKETS.findIndex((bound) => seconds <= bound) + if (bucket >= 0) entry.buckets[bucket] += 1 + durations.set(key, entry) +} export const requestMetrics: RequestHandler = (req, res, next) => { + const startedAt = process.hrtime.bigint() res.on('finish', () => { // Route template when Express matched one, never the raw URL — raw paths // carry addresses/ids and would explode label cardinality. const route = req.route?.path ? `${req.baseUrl}${req.route.path}` : 'unmatched' const key = `${req.method}\u0000${route}\u0000${res.statusCode}` requestCounts.set(key, (requestCounts.get(key) ?? 0) + 1) + observeDuration('helphone_http_request_duration_seconds', 'HTTP request duration in seconds.', { + method: req.method, + route, + status: String(res.statusCode), + }, Number(process.hrtime.bigint() - startedAt) / 1e9) }) next() } @@ -88,6 +108,37 @@ export function httpRequestFamily(): MetricFamily { } } +export function durationFamilies(): MetricFamily[] { + const families = new Map() + for (const [key, entry] of durations) { + const name = key.split('\u0000', 1)[0] + let family = families.get(name) + if (!family) { + family = { name, help: 'Observed operation durations in seconds.', type: 'histogram', samples: [] } + families.set(name, family) + } + let cumulative = 0 + HTTP_BUCKETS.forEach((bound, index) => { + cumulative += entry.buckets[index] + family!.samples.push({ labels: { ...entry.labels, le: String(bound) }, value: cumulative, suffix: 'bucket' }) + }) + family.samples.push({ labels: { ...entry.labels, le: '+Inf' }, value: entry.count, suffix: 'bucket' }) + family.samples.push({ labels: entry.labels, value: entry.sum, suffix: 'sum' }) + family.samples.push({ labels: entry.labels, value: entry.count, suffix: 'count' }) + } + return [...families.values()] +} + +export async function measureDuration(name: string, labels: Labels, operation: () => Promise): Promise { + const startedAt = process.hrtime.bigint() + try { + return await operation() + } finally { + observeDuration(name, 'Observed operation durations in seconds.', labels, Number(process.hrtime.bigint() - startedAt) / 1e9) + } +} + export function resetRequestMetrics(): void { requestCounts.clear() + durations.clear() } diff --git a/server/routes/health.js b/server/routes/health.js new file mode 100644 index 00000000..60432f32 --- /dev/null +++ b/server/routes/health.js @@ -0,0 +1,11 @@ +import { Router } from "express"; + +export function createHealthRouter({ readiness = () => true } = {}) { + const healthRouter = Router(); + healthRouter.get("/live", (_req, res) => res.json({ status: "ok" })); + healthRouter.get("/ready", (_req, res) => { + const ready = readiness(); + res.status(ready ? 200 : 503).json({ status: ready ? "ready" : "not_ready" }); + }); + return healthRouter; +} diff --git a/server/routes/health.ts b/server/routes/health.ts new file mode 100644 index 00000000..95f5a1a2 --- /dev/null +++ b/server/routes/health.ts @@ -0,0 +1,11 @@ +import { Router } from 'express' + +export function createHealthRouter(readiness: () => boolean = () => true) { + const router = Router() + router.get('/live', (_req, res) => res.json({ status: 'ok' })) + router.get('/ready', (_req, res) => { + const ready = readiness() + res.status(ready ? 200 : 503).json({ status: ready ? 'ready' : 'not_ready' }) + }) + return router +} diff --git a/server/routes/wsCluster.js b/server/routes/wsCluster.js new file mode 100644 index 00000000..5630755c --- /dev/null +++ b/server/routes/wsCluster.js @@ -0,0 +1,87 @@ +import { WebSocketServer, WebSocket } from "ws"; +import { subscribeContractEvents } from "../lib/redisPubSub.js"; + +const EVENT_TOPICS = new Set(["RqCreated", "RqAcptd", "LocUpd", "Arrived", "Resolved", "Cancelled"]); +const MAX_BUFFERED_BYTES = 1024 * 1024; + +export function attachWebSocketRelay(server, { heartbeatMs = 30_000 } = {}) { + const wss = new WebSocketServer({ noServer: true, maxPayload: 4096, clientTracking: true }); + + server.on("upgrade", (request, socket, head) => { + let pathname; + try { + pathname = new URL(request.url || "/", "http://localhost").pathname; + } catch { + socket.destroy(); + return; + } + if (pathname !== "/events/ws") return; + wss.handleUpgrade(request, socket, head, (client) => wss.emit("connection", client, request)); + }); + + wss.on("connection", (client, request) => { + const tenant = new URL(request.url || "/", "http://localhost").searchParams.get("tenant"); + if (tenant !== null && !/^[A-Za-z0-9_-]{1,128}$/.test(tenant)) { + client.close(1008, "Invalid tenant filter"); + return; + } + const tenantId = tenant; + const subscription = { tenantId, topics: new Set(EVENT_TOPICS) }; + client.isAlive = true; + client.on("pong", () => { client.isAlive = true; }); + client.on("message", (raw) => { + try { + const message = JSON.parse(raw.toString()); + if (message?.type !== "subscribe" || !Array.isArray(message.topics) || message.topics.length > EVENT_TOPICS.size) { + client.close(1008, "Invalid subscription"); + return; + } + if (!message.topics.every((topic) => EVENT_TOPICS.has(topic))) { + client.close(1008, "Unsupported topic"); + return; + } + subscription.topics = new Set(message.topics); + client.send(JSON.stringify({ type: "subscribed", topics: [...subscription.topics], tenant: tenantId })); + } catch { + client.close(1008, "Invalid message"); + } + }); + client.send(JSON.stringify({ type: "ready", topics: [...subscription.topics], tenant: tenantId })); + client.subscription = subscription; + }); + + const unsubscribe = subscribeContractEvents((event) => { + for (const client of wss.clients) { + if (client.readyState !== WebSocket.OPEN || client.bufferedAmount > MAX_BUFFERED_BYTES) { + if (client.readyState === WebSocket.OPEN) client.close(1013, "Client is too slow"); + continue; + } + const filter = client.subscription; + if (!filter?.topics.has(event.topic)) continue; + if (filter.tenantId && event.tenantId !== filter.tenantId) continue; + client.send(JSON.stringify({ type: "contract-event", ...event })); + } + }); + + const heartbeat = setInterval(() => { + for (const client of wss.clients) { + if (client.isAlive === false) { + client.terminate(); + continue; + } + client.isAlive = false; + client.ping(); + } + }, heartbeatMs); + heartbeat.unref?.(); + + return { + wss, + close: async () => { + clearInterval(heartbeat); + unsubscribe(); + for (const client of wss.clients) client.close(1001, "Server shutting down"); + await new Promise((resolve) => wss.close(resolve)); + }, + }; +} diff --git a/server/telemetry.js b/server/telemetry.js new file mode 100644 index 00000000..ddd7575d --- /dev/null +++ b/server/telemetry.js @@ -0,0 +1,46 @@ +import { NodeSDK } from "@opentelemetry/sdk-node"; +import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node"; +import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; + +let sdk; + +export function initializeTelemetry() { + if (sdk) return; + const configuredEndpoint = process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || process.env.OTEL_EXPORTER_OTLP_ENDPOINT; + const endpoint = configuredEndpoint + ? configuredEndpoint.endsWith("/v1/traces") + ? configuredEndpoint + : `${configuredEndpoint.replace(/\/$/, "")}/v1/traces` + : undefined; + sdk = new NodeSDK({ + instrumentations: [getNodeAutoInstrumentations()], + ...(endpoint ? { traceExporter: new OTLPTraceExporter({ url: endpoint }) } : {}), + }); + sdk.start(); +} + +export function traceIdFromRequest(req) { + const traceparent = req.get?.("traceparent") || req.headers?.traceparent; + const match = typeof traceparent === "string" && + traceparent.match(/^00-([0-9a-f]{32})-([0-9a-f]{16})-[0-9a-f]{2}$/i); + return match && !/^0+$/.test(match[1]) ? match[1].toLowerCase() : null; +} + +export function telemetryErrorHandler(req, res) { + const traceId = traceIdFromRequest(req); + const { message, componentStack } = req.body || {}; + console.error(JSON.stringify({ + event: "client_error", + traceId, + message: typeof message === "string" ? message.slice(0, 500) : "Unknown client error", + componentStack: typeof componentStack === "string" ? componentStack.slice(0, 2000) : undefined, + timestamp: new Date().toISOString(), + })); + res.status(202).json({ accepted: true, traceId }); +} + +export async function shutdownTelemetry() { + if (!sdk) return; + await sdk.shutdown(); + sdk = undefined; +} diff --git a/server/telemetryBootstrap.js b/server/telemetryBootstrap.js new file mode 100644 index 00000000..58b4b2ae --- /dev/null +++ b/server/telemetryBootstrap.js @@ -0,0 +1,3 @@ +import { initializeTelemetry } from "./telemetry.js"; + +initializeTelemetry(); diff --git a/server/tests/telemetry-ws.test.js b/server/tests/telemetry-ws.test.js new file mode 100644 index 00000000..6c78514b --- /dev/null +++ b/server/tests/telemetry-ws.test.js @@ -0,0 +1,57 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { createServer } from "node:http"; +import { EventEmitter } from "node:events"; +import WebSocket from "ws"; +import { requestMetrics, renderMetrics, resetMetrics } from "../middleware/metrics.js"; +import { connectEventBus, publishContractEvent } from "../lib/redisPubSub.js"; +import { attachWebSocketRelay } from "../routes/wsCluster.js"; + +test("Prometheus endpoint data includes bounded HTTP request histograms", () => { + resetMetrics(); + const req = { method: "GET", baseUrl: "/api", route: { path: "/events" } }; + const res = new EventEmitter(); + res.statusCode = 200; + requestMetrics(req, res, () => {}); + res.emit("finish"); + + const output = renderMetrics(); + assert.match(output, /# TYPE helphone_http_requests_total counter/); + assert.match(output, /# TYPE helphone_http_request_duration_seconds histogram/); + assert.match(output, /helphone_http_request_duration_seconds_bucket\{method="GET",route="\/api\/events",status="200",le="\+Inf"\} 1/); +}); + +test("WebSocket relay applies topic and tenant filters and emits health heartbeats", async () => { + await connectEventBus({ redisUrl: "" }); + const server = createServer(); + const relay = attachWebSocketRelay(server, { heartbeatMs: 1000 }); + await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); + const address = server.address(); + const socket = new WebSocket(`ws://127.0.0.1:${address.port}/events/ws?tenant=team-a`); + const messages = []; + socket.on("message", (value) => messages.push(JSON.parse(value.toString()))); + + try { + await new Promise((resolve, reject) => { + socket.once("open", resolve); + socket.once("error", reject); + }); + await new Promise((resolve) => setTimeout(resolve, 25)); + socket.send(JSON.stringify({ type: "subscribe", topics: ["RqCreated"] })); + await new Promise((resolve) => setTimeout(resolve, 25)); + await publishContractEvent({ topic: "RqCreated", ledger: 4, id: "event-other", tenantId: "team-b" }); + await publishContractEvent({ topic: "LocUpd", ledger: 5, id: "event-wrong-topic", tenantId: "team-a" }); + await publishContractEvent({ topic: "RqCreated", ledger: 6, id: "event-match", tenantId: "team-a" }); + await new Promise((resolve) => setTimeout(resolve, 25)); + + assert.ok(messages.some((message) => message.type === "subscribed")); + assert.deepEqual( + messages.filter((message) => message.type === "contract-event").map((message) => message.id), + ["event-match"], + ); + } finally { + socket.close(); + await relay.close(); + await new Promise((resolve) => server.close(resolve)); + } +}); diff --git a/server/tests/ws-cluster.js b/server/tests/ws-cluster.js new file mode 100644 index 00000000..6c9a84e9 --- /dev/null +++ b/server/tests/ws-cluster.js @@ -0,0 +1,47 @@ +import WebSocket from "ws"; + +const urls = (process.env.WS_CLUSTER_URLS || "").split(",").map((url) => url.trim()).filter(Boolean); +if (urls.length < 3) { + console.error("Set WS_CLUSTER_URLS to three comma-separated /events/ws URLs."); + process.exit(2); +} + +const clientCount = 1000; +const timeoutMs = Number(process.env.WS_CLUSTER_TIMEOUT_MS || 20_000); +const sockets = []; + +function connect(url) { + return new Promise((resolve, reject) => { + const socket = new WebSocket(url, { handshakeTimeout: timeoutMs }); + const timeout = setTimeout(() => reject(new Error(`Timed out connecting to ${url}`)), timeoutMs); + socket.once("open", () => { + clearTimeout(timeout); + socket.send(JSON.stringify({ type: "subscribe", topics: ["RqCreated", "LocUpd", "Arrived"] })); + sockets.push(socket); + resolve(socket); + }); + socket.once("error", (error) => { + clearTimeout(timeout); + reject(error); + }); + }); +} + +try { + const started = Date.now(); + const results = await Promise.allSettled(Array.from({ length: clientCount }, (_, index) => + connect(urls[index % urls.length]), + )); + const failed = results.filter((result) => result.status === "rejected"); + console.log(JSON.stringify({ + clients: clientCount, + connected: clientCount - failed.length, + failed: failed.length, + endpoints: urls.length, + durationMs: Date.now() - started, + }, null, 2)); + if (failed.length) process.exitCode = 1; +} finally { + for (const socket of sockets) socket.close(1000, "Load test complete"); + await new Promise((resolve) => setTimeout(resolve, 250)); +} diff --git a/src/App.tsx b/src/App.tsx index 8048d629..6b50f188 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react' import { Link } from 'react-router-dom' import { FeatureFlagProvider, useFeatureFlag } from './lib/featureFlags.js' import { WalletProvider } from './contexts/WalletContext.js' +import LanguageSwitcher from './components/LanguageSwitcher' function AppContent() { const videoRef = useRef(null) @@ -55,8 +56,7 @@ function AppContent() { style={{ position: 'fixed', top: 0, - left: 0, - right: 0, + insetInline: 0, zIndex: 100, background: 'rgba(28, 44, 36, 0.92)', backdropFilter: 'blur(8px)', @@ -98,6 +98,7 @@ function AppContent() { {sorobanBackupEnabled && ( 🛡️ State Backup Active )} + diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index 3e874e90..0f1a49de 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -1,4 +1,5 @@ import { Component, type ReactNode } from "react"; +import { reportClientError } from "../lib/telemetry"; interface Props { children: ReactNode; @@ -21,7 +22,8 @@ export default class ErrorBoundary extends Component { } componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { - console.error("[ErrorBoundary] Component error caught:", error, errorInfo); + const traceId = reportClientError(error, errorInfo.componentStack || undefined); + console.error("[ErrorBoundary] Component error caught:", { error, traceId }); } handleReload = () => { diff --git a/src/components/LanguageSwitcher.tsx b/src/components/LanguageSwitcher.tsx index bf88f44b..a5457dc7 100644 --- a/src/components/LanguageSwitcher.tsx +++ b/src/components/LanguageSwitcher.tsx @@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next' const LANGUAGES: { code: string; label: string }[] = [ { code: 'en', label: 'EN' }, + { code: 'ar', label: 'العربية' }, ] interface LanguageSwitcherProps { @@ -22,17 +23,12 @@ interface LanguageSwitcherProps { */ export default function LanguageSwitcher({ style }: LanguageSwitcherProps) { const { i18n } = useTranslation() - const currentLang = i18n.language - - if (LANGUAGES.length <= 1) { - // Nothing to switch — hide until a second language is added. - return null - } + const currentLang = i18n.resolvedLanguage || i18n.language return (
{LANGUAGES.map(({ code, label }) => { - const isActive = currentLang === code + const isActive = currentLang.split('-')[0] === code return (
+
+

+ DAO proposal timelock +

+

+ Passed proposals must be queued and wait 48 hours before permissionless execution. Security guardians can approve emergency cancellation. +

+
+ +