diff --git a/crates/lance-context-master/src/lib.rs b/crates/lance-context-master/src/lib.rs index 7efe511d..48c6f64d 100644 --- a/crates/lance-context-master/src/lib.rs +++ b/crates/lance-context-master/src/lib.rs @@ -4,5 +4,6 @@ pub mod config; pub mod error; pub mod routes; pub mod scanner; +pub mod scheduler; pub mod state; pub mod stats_store; diff --git a/crates/lance-context-master/src/main.rs b/crates/lance-context-master/src/main.rs index b29a72c4..9c2e062c 100644 --- a/crates/lance-context-master/src/main.rs +++ b/crates/lance-context-master/src/main.rs @@ -9,6 +9,7 @@ use tracing_subscriber::EnvFilter; use lance_context_master::config::MasterConfig; use lance_context_master::routes; use lance_context_master::scanner; +use lance_context_master::scheduler; use lance_context_master::state::MasterState; #[tokio::main] @@ -42,6 +43,9 @@ async fn main() { // Background stats scanner (detached; stops when the last Arc drops). let _scanner = scanner::spawn_scanner(&state); + // Single serial compaction worker (+ optional periodic auto-sweep). + let _scheduler = scheduler::spawn_scheduler(&state); + let mut app = Router::new().nest("/api/v1", routes::api_router()); // Serve the built UI (SPA) as a fallback when a `--ui-dir` is configured. diff --git a/crates/lance-context-master/src/routes.rs b/crates/lance-context-master/src/routes.rs index c4eaa35a..2c5b3b7a 100644 --- a/crates/lance-context-master/src/routes.rs +++ b/crates/lance-context-master/src/routes.rs @@ -3,14 +3,18 @@ use std::sync::Arc; use axum::extract::{Path, Query, State}; +use axum::http::StatusCode; use axum::routing::{get, post}; use axum::{Json, Router}; use serde::Deserialize; -use lance_context_api::{ExperimentDetail, ExperimentListResponse, ExperimentSummary}; +use lance_context_api::{ + CompactJobStatus, ExperimentDetail, ExperimentListResponse, ExperimentSummary, +}; use crate::error::MasterError; use crate::scanner; +use crate::scheduler; use crate::state::MasterState; /// Query params for the experiment listing. @@ -101,11 +105,53 @@ pub async fn rescan( Ok(Json(serde_json::json!({ "scanned": n }))) } +/// `POST /api/v1/experiments/{name}/compact` — enqueue a manual compaction. +/// Returns 202 Accepted with the (possibly de-duped) job status. +pub async fn compact_experiment( + State(state): State>, + Path(name): Path, +) -> Result<(StatusCode, Json), MasterError> { + // Only enqueue known experiments. + let exists = state + .registry + .read() + .await + .contains(&name) + .await + .map_err(MasterError::from_lance)?; + if !exists { + return Err(MasterError::NotFound(format!( + "experiment '{}' does not exist", + name + ))); + } + let status = scheduler::enqueue(&state, &name).await; + Ok((StatusCode::ACCEPTED, Json(status))) +} + +/// `GET /api/v1/experiments/{name}/compact/status` — latest compaction job +/// state for an experiment (`None` when never requested). +pub async fn compact_status( + State(state): State>, + Path(name): Path, +) -> Json { + let status = state + .jobs + .lock() + .await + .get(&name) + .cloned() + .unwrap_or(CompactJobStatus::None); + Json(status) +} + /// Build the admin API router (mounted under `/api/v1`). pub fn api_router() -> Router> { Router::new() .route("/experiments", get(list_experiments)) .route("/experiments/{name}", get(get_experiment)) + .route("/experiments/{name}/compact", post(compact_experiment)) + .route("/experiments/{name}/compact/status", get(compact_status)) .route("/rescan", post(rescan)) } diff --git a/crates/lance-context-master/src/scheduler.rs b/crates/lance-context-master/src/scheduler.rs new file mode 100644 index 00000000..c8b61a93 --- /dev/null +++ b/crates/lance-context-master/src/scheduler.rs @@ -0,0 +1,327 @@ +//! Centralized compaction scheduler. +//! +//! Compaction rewrites fragments (`Rewrite` in Lance's conflict matrix). Two +//! concurrent `Rewrite`s on the same dataset conflict, so compaction must have a +//! **single serial driver**. This module is that driver: one background task +//! drains a single queue, so automatic sweeps and manual API triggers share the +//! same serial execution path and can never overlap. `Rewrite` vs the +//! data-plane's `Append` is non-conflicting, so this runs safely alongside live +//! ingest. + +use std::sync::Arc; +use std::time::Duration; + +use chrono::Utc; +use lance_context_api::CompactJobStatus; +use lance_context_core::{CompactionConfig, RolloutStore, RolloutStoreOptions}; +use tokio::task::JoinHandle; + +use crate::state::MasterState; +use crate::stats_store::StatRow; + +/// Build the [`CompactionConfig`] the scheduler applies, from master config. +pub fn compaction_config(state: &MasterState) -> CompactionConfig { + CompactionConfig { + enabled: true, + min_fragments: state.config.min_fragments, + target_rows_per_fragment: state.config.target_rows_per_fragment, + ..Default::default() + } +} + +/// Enqueue an experiment for compaction and mark it `Queued`. De-dupes: if a +/// job for `name` is already queued or running, this is a no-op returning the +/// existing status. +pub async fn enqueue(state: &Arc, name: &str) -> CompactJobStatus { + { + let jobs = state.jobs.lock().await; + if matches!( + jobs.get(name), + Some(CompactJobStatus::Queued) | Some(CompactJobStatus::Running) + ) { + return jobs.get(name).cloned().unwrap(); + } + } + state + .jobs + .lock() + .await + .insert(name.to_string(), CompactJobStatus::Queued); + // Unbounded send only fails if the receiver was dropped (scheduler gone). + let _ = state.compact_tx.send(name.to_string()); + CompactJobStatus::Queued +} + +/// Compact one experiment now (serial, on the scheduler task). Updates the job +/// status map and the stats table's compaction counters on success. +async fn run_compaction(state: &Arc, name: &str) { + state + .jobs + .lock() + .await + .insert(name.to_string(), CompactJobStatus::Running); + + let uri = state.rollout_uri(name); + let config = compaction_config(state); + let opts = RolloutStoreOptions::default(); + + let status = match RolloutStore::open_existing_with_options(&uri, opts).await { + Ok(mut store) => match store.compact(Some(config)).await { + Ok(metrics) => { + update_stats_after_compaction(state, name, &store).await; + CompactJobStatus::Done { + fragments_removed: metrics.fragments_removed, + fragments_added: metrics.fragments_added, + } + } + Err(e) => CompactJobStatus::Failed { + error: e.to_string(), + }, + }, + Err(e) => CompactJobStatus::Failed { + error: e.to_string(), + }, + }; + + if let CompactJobStatus::Failed { error } = &status { + tracing::warn!(store = %name, error = %error, "compaction failed"); + } + state.jobs.lock().await.insert(name.to_string(), status); +} + +/// Refresh the stats row for `name` after a successful compaction: re-observe +/// fragment/row counts and bump `last_compaction`/`total_compactions`. +async fn update_stats_after_compaction(state: &Arc, name: &str, store: &RolloutStore) { + let obs = match store.observe().await { + Ok(obs) => obs, + Err(e) => { + tracing::warn!(store = %name, error = %e, "post-compaction observe failed"); + return; + } + }; + let mut stats = state.stats.lock().await; + let prev_total = match stats.get(name).await { + Ok(Some(row)) => row.total_compactions, + _ => 0, + }; + let row = StatRow { + name: name.to_string(), + uri: state.rollout_uri(name), + row_count: obs.row_count, + fragment_count: obs.fragment_count, + last_updated: obs.last_updated, + pending_wal_generations: obs.pending_wal_generations, + last_compaction: Utc::now().timestamp_millis(), + total_compactions: prev_total + 1, + scanned_at: Utc::now().timestamp_millis(), + }; + if let Err(e) = stats.upsert(&row).await { + tracing::warn!(store = %name, error = %e, "post-compaction stats upsert failed"); + } +} + +/// Enqueue every experiment whose fragment count is at or above the configured +/// threshold, honoring quiet hours. Reads candidates from the stats table. +pub async fn sweep_candidates(state: &Arc) -> lance::Result { + let config = compaction_config(state); + // Quiet-hours gate applies to the whole sweep. + if in_quiet_hours(&config) { + return Ok(0); + } + let rows = state.stats.lock().await.list(None, usize::MAX, 0).await?; + let mut queued = 0; + for row in rows { + if row.fragment_count as usize >= config.min_fragments { + enqueue(state, &row.name).await; + queued += 1; + } + } + Ok(queued) +} + +fn in_quiet_hours(config: &CompactionConfig) -> bool { + if config.quiet_hours.is_empty() { + return false; + } + use chrono::Timelike; + let hour = Utc::now().hour() as u8; + config + .quiet_hours + .iter() + .any(|(start, end)| hour >= *start && hour < *end) +} + +/// Spawn the single serial compaction worker plus (optionally) the periodic +/// auto-sweep. The worker owns the queue receiver and processes one experiment +/// at a time. Returns the worker handle. Panics if called twice (receiver +/// already taken). +pub fn spawn_scheduler(state: &Arc) -> JoinHandle<()> { + let mut rx = state + .compact_rx + .try_lock() + .expect("scheduler: state lock") + .take() + .expect("spawn_scheduler called more than once"); + + // Optional periodic auto-sweep feeds the same queue. + let interval_secs = state.config.compaction_interval_secs; + if interval_secs > 0 { + let sweep_state = state.clone(); + tokio::spawn(async move { + let mut ticker = tokio::time::interval(Duration::from_secs(interval_secs)); + ticker.tick().await; // skip immediate tick + loop { + ticker.tick().await; + match sweep_candidates(&sweep_state).await { + Ok(n) if n > 0 => tracing::info!(queued = n, "auto-sweep queued experiments"), + Ok(_) => {} + Err(e) => tracing::warn!(error = %e, "auto-sweep failed"), + } + } + }); + } + + let worker_state = state.clone(); + tokio::spawn(async move { + while let Some(name) = rx.recv().await { + run_compaction(&worker_state, &name).await; + } + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::config::MasterConfig; + use tempfile::TempDir; + + fn config(dir: &TempDir) -> MasterConfig { + MasterConfig { + data_dir: dir.path().to_string_lossy().to_string(), + host: "127.0.0.1".to_string(), + port: 0, + stats_scan_interval_secs: 0, + scan_concurrency: 4, + compaction_interval_secs: 0, + // Low threshold so a handful of appends crosses it. + min_fragments: 2, + target_rows_per_fragment: 1_048_576, + ui_dir: None, + } + } + + /// Manual enqueue -> serial worker compacts -> job reaches Done and the + /// stats table records a compaction. + #[tokio::test] + async fn manual_compaction_runs_and_updates_stats() { + let dir = TempDir::new().unwrap(); + let state = MasterState::new(config(&dir)).await.unwrap(); + let worker = spawn_scheduler(&state); + + // Build a store with several fragments via repeated base-table appends. + let name = "exp"; + let uri = state.rollout_uri(name); + { + let mut store = RolloutStore::open(&uri).await.unwrap(); + for i in 0..4 { + let rec = crate::scheduler::tests::rollout_record(&format!("r{i}")); + store.add(&[rec]).await.unwrap(); + store.cleanup_own_shard().await.unwrap(); + } + } + state + .registry + .write() + .await + .upsert(name, &uri) + .await + .unwrap(); + // Seed a stats row so post-compaction upsert has a prior counter. + crate::scanner::scan_once(&state).await.unwrap(); + + enqueue(&state, name).await; + + // Wait for the worker to reach a terminal state. + let mut done = None; + for _ in 0..50 { + tokio::time::sleep(Duration::from_millis(50)).await; + if let Some(s) = state.jobs.lock().await.get(name) { + if matches!( + s, + CompactJobStatus::Done { .. } | CompactJobStatus::Failed { .. } + ) { + done = Some(s.clone()); + break; + } + } + } + let status = done.expect("job reached terminal state"); + assert!( + matches!(status, CompactJobStatus::Done { .. }), + "expected Done, got {status:?}" + ); + + let row = state.stats.lock().await.get(name).await.unwrap().unwrap(); + assert_eq!(row.total_compactions, 1); + assert!(row.last_compaction >= 0); + + worker.abort(); + } + + #[tokio::test] + async fn enqueue_dedupes_queued_jobs() { + let dir = TempDir::new().unwrap(); + let state = MasterState::new(config(&dir)).await.unwrap(); + // Do NOT spawn the worker, so jobs stay Queued. + let s1 = enqueue(&state, "x").await; + let s2 = enqueue(&state, "x").await; + assert!(matches!(s1, CompactJobStatus::Queued)); + assert!(matches!(s2, CompactJobStatus::Queued)); + // Only one message should be in the channel; drain and count. + let mut rx = state.compact_rx.lock().await.take().unwrap(); + let mut n = 0; + while rx.try_recv().is_ok() { + n += 1; + } + assert_eq!(n, 1); + } + + /// Minimal rollout record builder for tests (the core struct has no + /// `Default`). + pub fn rollout_record(id: &str) -> lance_context_core::RolloutRecord { + use chrono::TimeZone; + lance_context_core::RolloutRecord { + id: id.to_string(), + rollout_id: "rollout-1".to_string(), + problem_id: "problem-1".to_string(), + dataset: None, + sequence_order: 0, + role: lance_context_core::ROLE_ASSISTANT.to_string(), + created_at: Utc.timestamp_micros(1_700_000_000_000_000).unwrap(), + content: Some("x".to_string()), + content_type: "text/plain".to_string(), + input_tokens: None, + output_tokens: None, + num_input_tokens: None, + num_output_tokens: None, + output_logprobs: None, + input_logprobs: None, + ref_logprobs: None, + loss_mask: None, + advantage: None, + reward: None, + raw_reward: None, + grader_id: None, + score: None, + include_in_training: None, + exclude_reason: None, + policy_version: None, + relationships: vec![], + binary_payload: None, + payload_size: None, + payload_checksum: None, + artifact_type: None, + metadata: None, + } + } +} diff --git a/crates/lance-context-master/src/state.rs b/crates/lance-context-master/src/state.rs index 56ccf0e9..f603231d 100644 --- a/crates/lance-context-master/src/state.rs +++ b/crates/lance-context-master/src/state.rs @@ -1,10 +1,12 @@ //! Shared master state: durable registry (read-only) + stats table (read/write). +use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; +use lance_context_api::CompactJobStatus; use lance_context_core::RolloutRegistry; -use tokio::sync::{Mutex, RwLock}; +use tokio::sync::{mpsc, Mutex, RwLock}; use crate::config::MasterConfig; use crate::stats_store::StatsStore; @@ -24,6 +26,14 @@ pub struct MasterState { pub base_path: PathBuf, /// Effective configuration. pub config: MasterConfig, + /// Enqueues an experiment name for (serial) compaction. Both automatic + /// sweeps and manual API triggers push here, so compaction has a single + /// serial driver — never two concurrent `Rewrite`s. + pub compact_tx: mpsc::UnboundedSender, + /// Receiver half, taken once by [`crate::scheduler::spawn_scheduler`]. + pub compact_rx: Mutex>>, + /// Last-known compaction job state per experiment, for the status endpoint. + pub jobs: Mutex>, } impl MasterState { @@ -40,11 +50,15 @@ impl MasterState { .to_string(); let registry = RolloutRegistry::open_or_create(®istry_uri, None).await?; let stats = StatsStore::open_or_create(&stats_uri, None).await?; + let (compact_tx, compact_rx) = mpsc::unbounded_channel(); Ok(Arc::new(Self { registry: RwLock::new(registry), stats: Mutex::new(stats), base_path, config, + compact_tx, + compact_rx: Mutex::new(Some(compact_rx)), + jobs: Mutex::new(HashMap::new()), })) } diff --git a/crates/lance-context-master/ui/.gitignore b/crates/lance-context-master/ui/.gitignore new file mode 100644 index 00000000..14954bed --- /dev/null +++ b/crates/lance-context-master/ui/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +dist/ +*.local + +tsconfig.tsbuildinfo diff --git a/crates/lance-context-master/ui/index.html b/crates/lance-context-master/ui/index.html new file mode 100644 index 00000000..908075d6 --- /dev/null +++ b/crates/lance-context-master/ui/index.html @@ -0,0 +1,12 @@ + + + + + + lance-context master + + +
+ + + diff --git a/crates/lance-context-master/ui/package-lock.json b/crates/lance-context-master/ui/package-lock.json new file mode 100644 index 00000000..4d382d97 --- /dev/null +++ b/crates/lance-context-master/ui/package-lock.json @@ -0,0 +1,1796 @@ +{ + "name": "lance-context-master-ui", + "version": "0.6.3", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "lance-context-master-ui", + "version": "0.6.3", + "dependencies": { + "@tanstack/react-query": "^5.59.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "zustand": "^4.5.5" + }, + "devDependencies": { + "@types/react": "^18.3.11", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.2", + "typescript": "^5.6.2", + "vite": "^5.4.8" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha1-8vu/6ofESiFZDsUVt3iywm2IZuc=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha1-bwI38PNtLlHAVwpjb67Z0tDv5ik=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha1-gMELFySAgpaLV6hXuRZAlx8gcPc=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha1-zKC4gn5rzzuhdniOfzsYCtbbL6M=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha1-eh3vcEMCQBxH9k+oVYnpdK4hcEI=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha1-8EqW+9hHMkGxB5JD9bPwOjAQq3s=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha1-7yUEilGOgo1zk/rFiC3dc5Idc5Y=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha1-sGJ0elmXuhOGNyATKLv/d5YFdK4=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha1-wKB2bxoTYX2KF0B9erj51IYiXqQ=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha1-fwhx2Zgk0jE31g+G/PYTD9WhtR8=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha1-vYcITO0MeW7Ea9pJLeboPSnon8I=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha1-zzFb6UAhOzVOtKvMC9Aevj9zvCo=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha1-Rav951SJl+NDdsPmn+tHXP+0pgc=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha1-g3uHOHy/XsVTDLY0s8Yi9o7bkzQ=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha1-wkQkUnhYIgYk/Vmlseq0+kE8gDo=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha1-XPJaNomQa1ji8KLys3R4nmYnsV8=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha1-TZ1ABPZFzdME3pWMclFieE7KxwA=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha1-xHsHpBuV2gkH0Ca13YlNmN59Ly0=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha1-gAXjHYJxLuetrvbiPGO3GmJ3CpI=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha1-xxhKMmUz/N8bjuBzPiHHE7l1V18=", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha1-mwQ4T7dxkm36bXrQQyTssqubLig=", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha1-Cdm0NXeA2p6jp9+4M6Hx/0ObQFI=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha1-KZGOwtt1TO3LbBsE3ozWVHr2Rh4=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha1-5JW1OWYOUWkPOSivUKdvsKbM/yo=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha1-wTg4+lc3KDmr3dyR1xVCzuouHiI=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha1-ZGuYmqIL+J/Qcd1dv61po1QuVQ4=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha1-qmFc/ICvlU00WJBuOMoiwYz1wmE=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha1-/G/RGorKVsH284lPK+oEefj2Jrk=", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha1-cKxvoU9ct+H3+Ie8/7aArQmSK1s=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha1-MnH1Oz+T49CT1RjRZJ1taNNG7eI=", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha1-7WLgQjjFcCauqDHFoTC3PA+fJt8=", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha1-55uOtIvzsQb63sGsgkD7l7TmTL4=", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha1-XyIDhgoUO5kZ04PvdXNSH7FUw+Q=", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha1-B7yv2ZMi1a9i9hjLnmqbf0u4Jdw=", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha1-t8z2hnUdaj5EuGJ6uryL4+9i2N4=", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha1-bY8Mdo4HDmQwmvgAS7lOaKsrs7A=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha1-u+Qw9g03jsuI3sshnGAmZzh6YEc=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha1-mdHPKTcnlWDSEEgh9czOIgyyr3A=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha1-CHQVEsENUpVmurqDe0/gUsjzSHs=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha1-Z1tzhTmEESQHNQFhRKsumaYPx10=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha1-G/w86YqmypoJaeTSr3IUTFnBGTs=", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha1-rK01HVgtFXuxRVNdsqb/U91RS1w=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha1-Y0Khn0Q0dRjJPkOxrGnes8Rlah8=", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha1-N1xHbRlylHhRuh4Vro8SMEdEWqE=", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha1-eg7mAfYPmaIMfHxf8MgDiMEYm9Y=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha1-aRKwDSxjHA0Vzhp6tXzWV/Ko+Lo=", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha1-2xXWeByTHzolGj2sOVAcmKYIL9A=", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha1-R9K/TO9tRwsi9YMbQg+JZOC/dV8=", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha1-XphJtmHCIpz5Z6CNvi276ejJkeU=", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha1-WwaZ7l3UhLIiye10r/Q8keqLF/g=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha1-i8UsnXo86NBTPDUanJNd54HaoG8=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha1-ui7z6PsxDwrzVYjycM+lqpbkh2Q=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha1-k7EL2/6K2iJri8DALva39URHTZY=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha1-PoqjjvPJwwCUaHHj/bsMMOCiD4Y=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha1-HXmUOEuwrRvEGSG1BuFkLU+df8M=", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha1-plQPR8+ESla4DKn/ldKs37LO+Xs=", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha1-QE8gRWUYQMv0jakbptD0kPC8LL8=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha1-o0BP/d97R0tIyZuciTtiR7t2W6U=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha1-6KrG1Umzd5ReNJiC8Zm3yOt1yjg=", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha1-bi5E6lAxCzpYIHipFeX+uHnIINQ=", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha1-aJgwLabXegU3zeZLK0xrYGWb0RA=", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha1-MzcXyV3Vpmvvj2Pn74qf2EX9GNA=", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha1-gbwGujgDUgBNAfSCbrfNzO+gW60=", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha1-lafNOd4hOJrWeIpShOqqc44pykw=", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha1-BubbLsG8SLU3THkj74PC6wJLJFI=", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha1-XcgYmIKF4J6IeQxkYt73JBPfLaM=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha1-IID0qTNJ6a/TS+b8GjfgH8i/yA8=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha1-IdZKistmIhckuSPlGvUzPfGvBEs=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha1-jg/NnQIUHjN7TFtc/1dsuadrG6A=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha1-vbTMTv1Y7+gIIDNH8PVGPw6hblI=", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha1-26695a/STq4O7+kV2QFjLny1mGA=", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha1-hBCehf6l+PE1NJn5ZXj9wqDosTg=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha1-NnHOP5uSjVwB+Hl5LVwLYK4U1K0=", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tanstack/query-core": { + "version": "5.101.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tanstack/query-core/-/query-core-5.101.2.tgz", + "integrity": "sha1-yQH7draBaf9eHkQBdATn4ukM4a8=", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.101.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@tanstack/react-query/-/react-query-5.101.2.tgz", + "integrity": "sha1-2IYzG80p34S8BMRdkxkrggfRfH8=", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.101.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha1-PfFfJ7qFMZyqB7oI0HIYibs5wBc=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha1-tYGSlMUReZV6+uw0FEL5NB5BCKk=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha1-VnJRNwHBshmbxtrWNqnXSRWGdm8=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha1-B9cT1szg0mXJhJ2wy+YtP2Hzb3Q=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha1-zz8Oh2177hWpOrkluCv1cKOQSiQ=", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha1-5uWobWAr6spxzlFj+t9fldcJMcc=", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.31", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/react/-/react-18.3.31.tgz", + "integrity": "sha1-teleKP/M6rjZgvM/LrB24XZTwqQ=", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha1-uJ3fLNg7T+r8xOLqQa/fuVoNGU8=", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha1-ZHr057t1rTrdV452KtmEuQ9KJLk=", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.42", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz", + "integrity": "sha1-GV3MdrqiaaSX8LB97Kzhaf7prFg=", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/browserslist/-/browserslist-4.28.5.tgz", + "integrity": "sha1-Q4t9OMDUtHdAu7NneNW9ygGzeDg=", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.42", + "caniuse-lite": "^1.0.30001800", + "electron-to-chromium": "^1.5.387", + "node-releases": "^2.0.50", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001803", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/caniuse-lite/-/caniuse-lite-1.0.30001803.tgz", + "integrity": "sha1-sqXWluBCvIME3NSULDn+Mw+7yyQ=", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha1-S1YPZJ/E6RjdCrdc9JYei8iC2Co=", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha1-7EjA8+mT5QZIyG2lWeJhCZXPmJo=", + "devOptional": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/debug/-/debug-4.4.3.tgz", + "integrity": "sha1-xq5DLZvZZiWC/OCHCbA4xY6ePWo=", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.388", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/electron-to-chromium/-/electron-to-chromium-1.5.388.tgz", + "integrity": "sha1-ACZm39oy4IfET9AbfOFxm27InFc=", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha1-nKMBsSCSKVm3ZjYNisgw2g0CmX0=", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha1-ARo/aYVroYnf+n3I/M6Z0qh5A+U=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha1-ysZAd4XQNnWipeGlMFxpezR9kNY=", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha1-MqbudsPX9S1GsrGuXZP+qFgKJeA=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha1-dNM1ojT2ftGZB/2t+sfM+dQJgl0=", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/json5/-/json5-2.2.3.tgz", + "integrity": "sha1-eM1vGhm9wStz21rQxh79ZsHikoM=", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha1-HaJ+ZxAnGUdpXa9oSOhH8B2EuSA=", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/ms/-/ms-2.1.3.tgz", + "integrity": "sha1-V0yBOM4dK1hh8LRFedut1gxmFbI=", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.15", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha1-NsSQ+tjG6GyCTJQN/d6Zm2ntQxY=", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.50", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/node-releases/-/node-releases-2.0.50.tgz", + "integrity": "sha1-WXGXqFIHHOQvwlUOWOIjJCvLqWk=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha1-PTIa8+q5ObCDyPkpodEs2oHCa2s=", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.16", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/postcss/-/postcss-8.5.16.tgz", + "integrity": "sha1-EjDOC13zVMJMDqRfmc5faognnSg=", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/react/-/react-18.3.1.tgz", + "integrity": "sha1-SauJIAnFOTNiW9FrJTP8dUyrKJE=", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha1-wiZdeVEbV9R5s90/36UVNklMXLQ=", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha1-t+V5w2V/I9BOzL5K0uWKjtUeflM=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha1-2Q/Ey4EfBxMDyJC3eVlWNPNflUE=", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha1-QUumSjsoKJLpRM8hCOzAeNEVzcM=", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-6.3.1.tgz", + "integrity": "sha1-VW0u+GiRRuRtzqS/3QlfNDTf/LQ=", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha1-HOVlD93YerwJnto33P8CTCZnrkY=", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha1-W09Z4VMQqxeiFvXWz1PuR27eZw8=", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha1-ZNdttYcTE2rL60xJEUNmzGzC6A0=", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha1-sXS/plyytSZzLZ8qwKQIAnh28y0=", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/vite/-/vite-5.4.21.tgz", + "integrity": "sha1-hKT3xdhgsHFnbTm6UTwNWY/ccCc=", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha1-27fa+b/YusmrRev2ArjLrQ1dCP0=", + "dev": true, + "license": "ISC" + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha1-fWuyAmoUJBXdi+iJHXhw5tvmX1U=", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + } +} diff --git a/crates/lance-context-master/ui/package.json b/crates/lance-context-master/ui/package.json new file mode 100644 index 00000000..bd4a7476 --- /dev/null +++ b/crates/lance-context-master/ui/package.json @@ -0,0 +1,24 @@ +{ + "name": "lance-context-master-ui", + "private": true, + "version": "0.6.3", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/react-query": "^5.59.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "zustand": "^4.5.5" + }, + "devDependencies": { + "@types/react": "^18.3.11", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.2", + "typescript": "^5.6.2", + "vite": "^5.4.8" + } +} diff --git a/crates/lance-context-master/ui/src/App.tsx b/crates/lance-context-master/ui/src/App.tsx new file mode 100644 index 00000000..308629a2 --- /dev/null +++ b/crates/lance-context-master/ui/src/App.tsx @@ -0,0 +1,196 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { useState } from "react"; +import { + compactionStatus, + getExperiment, + listExperiments, + triggerCompaction, + type CompactJobStatus, + type ExperimentSummary, +} from "./api"; +import { useUiStore } from "./store"; + +function fmtTime(ms: number | null | undefined): string { + if (!ms || ms < 0) return "—"; + return new Date(ms).toLocaleString(); +} + +function statusLabel(s: CompactJobStatus | undefined): string { + if (!s) return ""; + switch (s.state) { + case "queued": + return "queued"; + case "running": + return "running…"; + case "done": + return `done (-${s.fragments_removed}/+${s.fragments_added})`; + case "failed": + return `failed: ${s.error}`; + case "none": + return ""; + } +} + +function CompactButton({ name }: { name: string }) { + const qc = useQueryClient(); + const [poll, setPoll] = useState(false); + const status = useQuery({ + queryKey: ["compact-status", name], + queryFn: () => compactionStatus(name), + refetchInterval: poll ? 1000 : false, + }); + const trigger = useMutation({ + mutationFn: () => triggerCompaction(name), + onSuccess: () => { + setPoll(true); + qc.invalidateQueries({ queryKey: ["compact-status", name] }); + }, + }); + + const s = status.data; + // Stop polling once we reach a terminal state and refresh the list metrics. + if (poll && (s?.state === "done" || s?.state === "failed")) { + setPoll(false); + qc.invalidateQueries({ queryKey: ["experiments"] }); + } + + return ( + + + + {statusLabel(s)} + + + ); +} + +function ExperimentRow({ exp }: { exp: ExperimentSummary }) { + const select = useUiStore((s) => s.select); + return ( + + + (e.preventDefault(), select(exp.name))}> + {exp.name} + + + {exp.row_count.toLocaleString()} + {exp.fragment_count.toLocaleString()} + {exp.pending_wal_generations} + {fmtTime(exp.last_updated)} + {fmtTime(exp.last_compaction)} + + + + + ); +} + +function DetailPanel({ name }: { name: string }) { + const select = useUiStore((s) => s.select); + const detail = useQuery({ + queryKey: ["experiment", name], + queryFn: () => getExperiment(name, true), + }); + const d = detail.data; + return ( +
+ +

{name}

+ {detail.isLoading &&

loading…

} + {d && ( +
+ + + + + + + + +
+ )} +
+ +
+
+ ); +} + +function Metric({ label, value }: { label: string; value: string }) { + return ( +
+
{label}
+
{value}
+
+ ); +} + +export function App() { + const { search, page, pageSize, selected, setSearch, setPage } = useUiStore(); + const list = useQuery({ + queryKey: ["experiments", search, page, pageSize], + queryFn: () => listExperiments(search, pageSize, page * pageSize), + }); + + const total = list.data?.total ?? 0; + const maxPage = Math.max(0, Math.ceil(total / pageSize) - 1); + + return ( +
+

lance-context master

+ + {selected && } + +
+ setSearch(e.target.value)} + style={{ padding: 6, width: 320 }} + /> + {total} experiments +
+ + {list.isLoading &&

loading…

} + {list.isError &&

{String(list.error)}

} + + {list.data && ( + + + + + + + + + + + + + + {list.data.experiments.map((exp) => ( + + ))} + +
namerowsfragmentspendinglast updatedlast compactionaction
+ )} + +
+ + + page {page + 1} / {maxPage + 1} + + +
+
+ ); +} diff --git a/crates/lance-context-master/ui/src/api.ts b/crates/lance-context-master/ui/src/api.ts new file mode 100644 index 00000000..b9ff1a90 --- /dev/null +++ b/crates/lance-context-master/ui/src/api.ts @@ -0,0 +1,70 @@ +// Wire types + fetch helpers for the master admin API. These mirror the Rust +// DTOs in `lance-context-api` (ExperimentSummary / CompactJobStatus). + +export interface ExperimentSummary { + name: string; + uri: string; + row_count: number; + fragment_count: number; + last_updated: number; + pending_wal_generations: number; + last_compaction?: number | null; + total_compactions: number; + scanned_at: number; +} + +export interface ExperimentListResponse { + experiments: ExperimentSummary[]; + total: number; +} + +export type CompactJobStatus = + | { state: "queued" } + | { state: "running" } + | { state: "done"; fragments_removed: number; fragments_added: number } + | { state: "failed"; error: string } + | { state: "none" }; + +const API = "/api/v1"; + +async function json(res: Response): Promise { + if (!res.ok) { + const body = await res.text(); + throw new Error(`${res.status}: ${body}`); + } + return res.json() as Promise; +} + +export async function listExperiments( + search: string, + limit: number, + offset: number, +): Promise { + const params = new URLSearchParams(); + if (search) params.set("search", search); + params.set("limit", String(limit)); + params.set("offset", String(offset)); + return json(await fetch(`${API}/experiments?${params.toString()}`)); +} + +export async function getExperiment( + name: string, + fresh = false, +): Promise<{ summary?: ExperimentSummary } & ExperimentSummary> { + const q = fresh ? "?fresh=true" : ""; + return json(await fetch(`${API}/experiments/${encodeURIComponent(name)}${q}`)); +} + +export async function triggerCompaction(name: string): Promise { + return json( + await fetch(`${API}/experiments/${encodeURIComponent(name)}/compact`, { + method: "POST", + }), + ); +} + +export async function compactionStatus(name: string): Promise { + return json( + await fetch(`${API}/experiments/${encodeURIComponent(name)}/compact/status`), + ); +} diff --git a/crates/lance-context-master/ui/src/main.tsx b/crates/lance-context-master/ui/src/main.tsx new file mode 100644 index 00000000..e479872d --- /dev/null +++ b/crates/lance-context-master/ui/src/main.tsx @@ -0,0 +1,15 @@ +import ReactDOM from "react-dom/client"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { App } from "./App"; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { refetchOnWindowFocus: false, staleTime: 5_000 }, + }, +}); + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/crates/lance-context-master/ui/src/store.ts b/crates/lance-context-master/ui/src/store.ts new file mode 100644 index 00000000..478cf502 --- /dev/null +++ b/crates/lance-context-master/ui/src/store.ts @@ -0,0 +1,23 @@ +import { create } from "zustand"; + +/// UI-local view state: the current search query and pagination cursor. +interface UiState { + search: string; + page: number; + pageSize: number; + selected: string | null; + setSearch: (search: string) => void; + setPage: (page: number) => void; + select: (name: string | null) => void; +} + +export const useUiStore = create((set) => ({ + search: "", + page: 0, + pageSize: 25, + selected: null, + // Changing the search resets pagination to the first page. + setSearch: (search) => set({ search, page: 0 }), + setPage: (page) => set({ page }), + select: (selected) => set({ selected }), +})); diff --git a/crates/lance-context-master/ui/tsconfig.json b/crates/lance-context-master/ui/tsconfig.json new file mode 100644 index 00000000..d1095552 --- /dev/null +++ b/crates/lance-context-master/ui/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/crates/lance-context-master/ui/vite.config.ts b/crates/lance-context-master/ui/vite.config.ts new file mode 100644 index 00000000..188af326 --- /dev/null +++ b/crates/lance-context-master/ui/vite.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +// Dev server proxies the admin API to the running master process so the SPA +// can be developed with `npm run dev` while `lance-context-master` serves data. +export default defineConfig({ + plugins: [react()], + server: { + proxy: { + "/api": { + target: process.env.MASTER_URL ?? "http://localhost:8090", + changeOrigin: true, + }, + }, + }, + build: { + outDir: "dist", + }, +});