From 2eaad4ea0f831120645e6c540ca7ec8926d920cd Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Fri, 23 Jan 2026 15:10:58 -0500 Subject: [PATCH 1/4] design doc Signed-off-by: Connor Tsui --- BENCHMARK_WEBSITE_REDESIGN.md | 1696 +++++++++++++++++++++++++++++++++ REUSE.toml | 3 +- 2 files changed, 1698 insertions(+), 1 deletion(-) create mode 100644 BENCHMARK_WEBSITE_REDESIGN.md diff --git a/BENCHMARK_WEBSITE_REDESIGN.md b/BENCHMARK_WEBSITE_REDESIGN.md new file mode 100644 index 00000000000..ff38d572f19 --- /dev/null +++ b/BENCHMARK_WEBSITE_REDESIGN.md @@ -0,0 +1,1696 @@ +# Vortex Benchmarks Website: Architecture & Implementation Plan + +This document describes the architecture for a high-performance benchmarks visualization website for Vortex. It serves as both a design specification and implementation guide for humans and AI agents working on this project. + +_This is mostly Claude-written, but it is also the result of quite a lot of prototyping and research on how to make our benchmarks website not absolutely terrible with respect to load times._ + +--- + +## Table of Contents + +1. [Overview & Goals](#overview--goals) +2. [Current State & Problems](#current-state--problems) +3. [Architecture Overview](#architecture-overview) +4. [Data Model & Schema](#data-model--schema) +5. [Server Architecture](#server-architecture) +6. [Client Architecture](#client-architecture) +7. [API Design](#api-design) +8. [Deployment & Infrastructure](#deployment--infrastructure) +9. [Implementation Plan](#implementation-plan) +10. [Future Considerations](#future-considerations) + +--- + +## Overview & Goals + +### What We're Building + +A benchmarks visualization website (https://bench.vortex.dev/) that displays performance data for the Vortex columnar file format across multiple benchmark suites. The site shows time-series-like charts of benchmark results over git commits, allowing users to track performance regressions and improvements. + +### Goals + +1. **Fast initial load**: Sub-second time to first contentful paint +2. **Interactive charts**: Instant zoom/pan/scroll across entire commit history (up to 5000+ commits) +3. **Real-time updates**: New benchmark results from CI appear within minutes +4. **Maintainability**: Rust-native stack (Leptos) for team familiarity + +### Secondary Goals + +- Dogfooding: Use Vortex file format for storing benchmark data via DuckDB extension +- Reusability: Architected as a library that others can adapt for their benchmarking needs +- SEO optimization: This would be a nice to have, but speed is paramount +- Mobile-first design: Desktop is primary use case, but this should work on mobile too +- Public API for benchmark data (internal tooling only) + +--- + +## Current State & Problems + +### Current Architecture + +``` +GitHub Actions (CI) -> JSON files -> S3 bucket -> Client downloads entire dataset +``` + +- Single monolithic JSON file containing all benchmark results (~80MB uncompressed, ~8MB gzipped) +- Separate commits.json for ordering commits by timestamp +- Client downloads everything, then processes in JavaScript +- Data has been manually truncated to ~2000 commits to keep things more manageable (would be ~4500 at this point) + +### Pain Points + +| Problem | Impact | +| ----------------------------------------- | -------------------------------------------------- | +| 8MB gzipped download | 10-15 seconds on fast internet | +| Client-side JSON parsing + join | Additional 10 seconds processing | +| No incremental loading | Must wait for everything before seeing anything | +| Schema changes require JSON restructuring | Difficult to add new targets/benchmarks | +| No server-side computation | Summary statistics computed client-side repeatedly | +| Manual data truncation | Losing historical data to keep site usable | + +### Data Volume + +- ~2000 commits currently (truncated), would be ~4500+ without truncation +- 10+ benchmark groups (TPC-H at multiple scale factors, ClickBench, compression, random access, various micro-benchmarks) + - TPC-H groups: 19 queries each (× multiple scale factors) + - ClickBench: 44 queries + - etc. +- ~200 individual charts across all groups +- Series names vary by group (e.g., "vortex", "parquet", "duckdb:vortex", "datafusion:parquet") + +--- + +## Architecture Overview + +### High-Level Design + +``` +┌─────────────────┐ ┌─────────────────────────────────────────────┐ +│ GitHub Actions │ POST │ AWS Infrastructure │ +│ (benchmark CI) │ ───────► │ │ +└─────────────────┘ │ ┌─────────────────────────────────────┐ │ + │ │ CloudFront (CDN) │ │ + Users ─────────────►│ │ - Caches HTML/WASM/static assets │ │ + (global) │ │ - Caches API responses (60s TTL) │ │ + │ └──────────────┬──────────────────────┘ │ + │ │ │ + │ ▼ │ + │ ┌─────────────────────────────────────┐ │ + │ │ EC2 / ECS (Leptos) │ │ + │ │ │ │ + │ │ ┌───────────────────────────────┐ │ │ + │ │ │ DuckDB (embedded) │ │ │ + │ │ │ + Vortex Extension │ │ │ + │ │ │ │ │ │ + │ │ │ commits.vortex │ │ │ + │ │ │ compression.vortex │ │ │ + │ │ │ tpch_sf1.vortex │ │ │ + │ │ │ tpch_sf10.vortex │ │ │ + │ │ │ clickbench.vortex │ │ │ + │ │ │ random_access.vortex │ │ │ + │ │ └───────────────────────────────┘ │ │ + │ └─────────────────────────────────────┘ │ + │ │ │ + │ ▼ │ + │ ┌─────────────────────────────────────┐ │ + │ │ S3 Bucket │ │ + │ │ - Vortex file backups (hourly) │ │ + │ │ - Disaster recovery │ │ + │ └─────────────────────────────────────┘ │ + └─────────────────────────────────────────────┘ +``` + +## Key Architectural Decisions + +### Decision 1: Server-Side DuckDB (Optionally with Vortex Extension) + +**Choice**: Embedded DuckDB on server, optionally using Vortex extension for storage. + +**Alternatives Considered**: + +- Client-side DuckDB-WASM: Adds complexity, requires shipping data to client +- Static JSON files on CDN: Current approach, proven to be too slow +- PostgreSQL/MySQL: Overkill for this workload + +**Rationale**: + +- DuckDB is extremely fast for analytical queries (~5ms for typical chart query) +- Vortex extension lets us dogfood our format, but plain DuckDB works too +- Schema evolution is trivial (`ALTER TABLE ADD COLUMN`) +- Keep in mind future library extraction: storage should be swappable + +### Decision 2: Leptos with Islands Architecture + +**Choice**: Leptos SSR with `#[island]` components for interactive charts + +**Alternatives Considered**: + +- Dioxus: Better cross-platform, but weaker SSR/streaming support +- Yew: No streaming SSR, less active development +- Next.js/React: Team prefers Rust, would require JS expertise + +**Rationale**: + +- SSR means fast initial paint (HTML renders before WASM loads) +- Islands keep WASM bundle small (only interactive parts ship as WASM) +- Fine-grained reactivity makes chart updates efficient +- Team already knows Rust + +### Decision 3: Progressive Data Loading + +**Choice**: SSR with most recent N commits (configurable, default ~50), lazy-load full history on demand per chart + +**Alternatives Considered**: + +- Load all data upfront: 20-30MB initial payload for large groups +- Paginated loading: Poor UX for time-series charts +- Virtual scrolling of data: Complex, doesn't match use case + +**Rationale**: + +- Initial HTML is small (~500KB for 44 charts × 50 commits) +- Users see useful content immediately +- Full history loads only when user explicitly needs it +- Each chart loads independently (don't pay for charts you don't view) +- N is configurable (25-100 range, tune based on testing) + +### Decision 4: No Materialized Views + +**Choice**: Direct queries against Vortex tables with indexes + +**Alternatives Considered**: + +- 1000+ materialized views (one per chart): Complexity, refresh overhead +- Pre-computed JSON cache: Loses benefits of SQL, cache invalidation issues + +**Rationale**: + +- Data volume is small (~100MB total across all groups) +- DuckDB queries complete in <10ms with proper indexes +- Materialized views add complexity without meaningful performance gain +- Easier to add new charts/series without maintaining view definitions + +--- + +## Data Model & Schema + +### Overview + +Each benchmark group is stored as a separate table (or Vortex file when using the Vortex extension). All tables share a common `commits` table for ordering. + +**Important**: All benchmark measurements are stored as **unsigned 64-bit integers** (typically nanoseconds). Conversion to human-readable units (seconds, milliseconds, MB/s) happens at display time. This preserves precision and simplifies storage. + +**Sparse Data**: Not all commits have benchmark data. The `commits` table contains all commits, but benchmark tables only have rows for commits where benchmarks actually ran. Queries use LEFT JOIN to include all commits, with NULL values for missing benchmark data. + +### Commits Table + +```sql +-- commits table +-- Stores git commit metadata for ordering and display +CREATE TABLE commits ( + commit_hash VARCHAR PRIMARY KEY, + timestamp TIMESTAMP NOT NULL, + message VARCHAR, + author VARCHAR +); + +-- Index for efficient "most recent N commits" queries +CREATE INDEX idx_commits_timestamp ON commits(timestamp DESC); +``` + +### Benchmark Group Tables + +Each benchmark group follows one of two patterns. Series columns are named dynamically based on the targets being compared (e.g., "vortex", "parquet", "duckdb_vortex", "datafusion_parquet"). + +**Note on series naming**: Series names may be compound (e.g., "duckdb:vortex" means "DuckDB engine reading Vortex format"). In SQL column names, colons are replaced with underscores. + +#### Pattern A: Single Chart (e.g., compression, random_access) + +```sql +-- compression table +-- No chart dimension - just one chart per group +-- All values are u64 (nanoseconds, bytes, etc.) +CREATE TABLE compression ( + commit_hash VARCHAR NOT NULL REFERENCES commits(commit_hash), + -- Series columns - names vary by group + -- Stored as UBIGINT (u64), converted to display units in UI + vortex_throughput_ns UBIGINT, -- nanoseconds per operation + parquet_throughput_ns UBIGINT, + lance_throughput_ns UBIGINT, + vortex_compressed_bytes UBIGINT, -- bytes + parquet_compressed_bytes UBIGINT, + lance_compressed_bytes UBIGINT, + PRIMARY KEY (commit_hash) +); +``` + +#### Pattern B: Multiple Charts (e.g., TPC-H, ClickBench) + +```sql +-- tpch_sf1 table +-- Multiple charts (queries), each showing multiple series (targets) +-- Series names can be compound: "duckdb_vortex" = DuckDB engine + Vortex format +CREATE TABLE tpch_sf1 ( + commit_hash VARCHAR NOT NULL REFERENCES commits(commit_hash), + chart VARCHAR NOT NULL, -- 'q1', 'q2', ..., 'q19' + -- Series columns (nullable for sparse data) + -- All times stored as nanoseconds (u64) + vortex_ns UBIGINT, -- Vortex native reader + parquet_ns UBIGINT, -- Parquet native reader + duckdb_vortex_ns UBIGINT, -- DuckDB reading Vortex + duckdb_parquet_ns UBIGINT, -- DuckDB reading Parquet + datafusion_vortex_ns UBIGINT, + datafusion_parquet_ns UBIGINT, + lance_ns UBIGINT, + PRIMARY KEY (commit_hash, chart) +); + +-- Index for filtering by chart +CREATE INDEX idx_tpch_sf1_chart ON tpch_sf1(chart); +``` + +```sql +-- clickbench table +CREATE TABLE clickbench ( + commit_hash VARCHAR NOT NULL REFERENCES commits(commit_hash), + chart VARCHAR NOT NULL, -- 'q0', 'q1', ..., 'q43' + vortex_ns UBIGINT, + parquet_ns UBIGINT, + duckdb_vortex_ns UBIGINT, + duckdb_parquet_ns UBIGINT, + PRIMARY KEY (commit_hash, chart) +); + +CREATE INDEX idx_clickbench_chart ON clickbench(chart); +``` + +### Schema Evolution + +Adding a new target (series) to an existing benchmark group: + +```sql +ALTER TABLE tpch_sf1 ADD COLUMN arrow_ns UBIGINT; +``` + +- New column is nullable by default (existing rows have NULL) +- No data migration required +- UI picks up new series from configuration + +Adding a new chart to an existing group: + +```sql +INSERT INTO clickbench (commit_hash, chart, vortex_ns, parquet_ns) +VALUES ('abc123', 'q44', 150000000, 200000000); +``` + +Adding a new benchmark group: + +1. Create table with appropriate schema +2. Add group metadata to configuration (see [Group Configuration](#group-configuration)) +3. CI starts posting results to new endpoint + +### Group Configuration + +Store benchmark group metadata in a configuration file or table: + +```rust +// benchmark_groups.rs +pub struct BenchmarkGroup { + pub id: &'static str, // "tpch_sf1" + pub display_name: &'static str, // "TPC-H Scale Factor 1" + pub chart_column: Option<&'static str>, // Some("chart") or None + pub series: Vec, + pub summary_type: SummaryType, // How to compute summary stats +} + +pub struct SeriesConfig { + pub column: &'static str, // "duckdb_vortex_ns" (SQL column name) + pub display_name: &'static str, // "DuckDB + Vortex" (human readable) + pub color: &'static str, // "#3b82f6" + pub unit: MeasurementUnit, // How to convert/display values +} + +/// All benchmark data is stored as u64. This enum defines how to display it. +pub enum MeasurementUnit { + Nanoseconds, // Display as seconds, ms, or μs depending on magnitude + Bytes, // Display as B, KB, MB, GB + BytesPerSecond, // Throughput: display as MB/s, GB/s + Ratio, // Compression ratio: value / 1_000_000 (stored as ratio * 1e6) + Count, // Raw count, no conversion +} + +impl MeasurementUnit { + /// Convert u64 stored value to f64 display value with appropriate unit + pub fn format(&self, value: u64) -> (f64, &'static str) { + match self { + MeasurementUnit::Nanoseconds => { + let ns = value as f64; + if ns >= 1e9 { (ns / 1e9, "s") } + else if ns >= 1e6 { (ns / 1e6, "ms") } + else if ns >= 1e3 { (ns / 1e3, "μs") } + else { (ns, "ns") } + } + MeasurementUnit::Bytes => { + let b = value as f64; + if b >= 1e9 { (b / 1e9, "GB") } + else if b >= 1e6 { (b / 1e6, "MB") } + else if b >= 1e3 { (b / 1e3, "KB") } + else { (b, "B") } + } + // ... etc + } + } +} + +pub enum SummaryType { + GeometricMean, // For TPC-H, ClickBench (ratios/times) + ArithmeticMean, // For throughput benchmarks + Custom(fn(&[u64]) -> f64), +} +``` + +--- + +## Server Architecture + +### Technology Stack + +- **Framework**: Leptos 0.8+ with Axum backend +- **Database**: DuckDB (embedded), optionally with Vortex extension +- **Runtime**: Tokio async runtime + +### Project Structure + +``` +bench-website/ +├── Cargo.toml +├── src/ +│ ├── main.rs # Entry point, Axum router setup +│ ├── lib.rs # Leptos app root +│ ├── db/ +│ │ ├── mod.rs +│ │ ├── connection.rs # DuckDB connection pool +│ │ ├── queries.rs # SQL query functions +│ │ └── models.rs # Data structures +│ ├── api/ +│ │ ├── mod.rs +│ │ ├── ingest.rs # POST endpoint for CI +│ │ └── charts.rs # Server functions for chart data +│ ├── components/ +│ │ ├── mod.rs +│ │ ├── app.rs # Root app component +│ │ ├── layout.rs # Navigation, layout +│ │ ├── group_page.rs # Benchmark group page +│ │ ├── chart.rs # Interactive chart island +│ │ └── summary.rs # Summary statistics component +│ └── config/ +│ ├── mod.rs +│ └── groups.rs # Benchmark group definitions +├── style/ +│ └── main.css +└── data/ # Vortex files (gitignored, populated at runtime) + ├── commits.vortex + ├── compression.vortex + ├── tpch_sf1.vortex + └── ... +``` + +### DuckDB Connection Management + +```rust +// src/db/connection.rs +use duckdb::{Connection, Result}; +use std::sync::Arc; + +/// DuckDB connection wrapper +/// +/// DuckDB supports concurrent reads AND writes from the same process +/// (MVCC handles isolation). We use Arc to share the connection across +/// async tasks. DuckDB's internal locking handles thread safety. +pub struct DbPool { + conn: Arc, +} + +impl DbPool { + pub fn new(config: &StorageConfig) -> Result { + let conn = Connection::open(&config.database_path)?; + + // Optionally load Vortex extension if configured + if config.use_vortex_extension { + conn.execute("INSTALL vortex FROM 'path/to/extension'; LOAD vortex;", [])?; + } + + // Create tables if they don't exist + Self::init_schema(&conn)?; + + Ok(Self { + conn: Arc::new(conn), + }) + } + + fn init_schema(conn: &Connection) -> Result<()> { + conn.execute_batch(r#" + CREATE TABLE IF NOT EXISTS commits ( + commit_hash VARCHAR PRIMARY KEY, + timestamp TIMESTAMP NOT NULL, + message VARCHAR, + author VARCHAR + ); + CREATE INDEX IF NOT EXISTS idx_commits_timestamp + ON commits(timestamp DESC); + "#)?; + Ok(()) + } + + /// Execute a query (DuckDB handles concurrent access internally) + pub fn query(&self, f: F) -> Result + where + F: FnOnce(&Connection) -> Result, + { + f(&self.conn) + } +} + +/// Configuration for storage backend (supports library reuse) +pub struct StorageConfig { + pub database_path: String, // ":memory:" or file path + pub use_vortex_extension: bool, // false for standard DuckDB + pub vortex_extension_path: Option, +} +``` + +### Ingest Endpoint + +```rust +// src/api/ingest.rs +use axum::{extract::State, http::StatusCode, Json}; +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct IngestRequest { + pub group: String, // "tpch_sf1", "clickbench", etc. + pub commit_hash: String, // git commit SHA + pub commit_timestamp: i64, // Unix timestamp (seconds) + pub commit_message: Option, + pub commit_author: Option, + pub chart: Option, // None for single-chart groups, Some("q1") for multi-chart + pub results: HashMap, // series_name -> value (always u64) +} + +/// POST /api/ingest +/// +/// Called by GitHub Actions after each benchmark run. +/// DuckDB handles concurrent writes via MVCC. +pub async fn ingest_benchmark( + State(db): State, + Json(req): Json, +) -> Result { + // Verify CI token (from header) + // ... token verification ... + + // Upsert commit metadata + db.query(|conn| { + conn.execute( + r#"INSERT INTO commits (commit_hash, timestamp, message, author) + VALUES (?, to_timestamp(?), ?, ?) + ON CONFLICT (commit_hash) DO NOTHING"#, + params![ + &req.commit_hash, + req.commit_timestamp, + &req.commit_message, + &req.commit_author, + ], + ) + }).map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; + + // Build dynamic INSERT for benchmark results + let columns: Vec<_> = req.results.keys().map(|s| s.as_str()).collect(); + let placeholders: Vec<_> = columns.iter().map(|_| "?").collect(); + + let sql = if let Some(chart) = &req.chart { + format!( + r#"INSERT INTO {group} (commit_hash, chart, {cols}) + VALUES (?, ?, {placeholders}) + ON CONFLICT (commit_hash, chart) DO UPDATE SET {updates}"#, + group = req.group, + cols = columns.join(", "), + placeholders = placeholders.join(", "), + updates = columns.iter().map(|c| format!("{c} = EXCLUDED.{c}")).collect::>().join(", "), + ) + } else { + format!( + r#"INSERT INTO {group} (commit_hash, {cols}) + VALUES (?, {placeholders}) + ON CONFLICT (commit_hash) DO UPDATE SET {updates}"#, + group = req.group, + cols = columns.join(", "), + placeholders = placeholders.join(", "), + updates = columns.iter().map(|c| format!("{c} = EXCLUDED.{c}")).collect::>().join(", "), + ) + }; + + db.query(|conn| { + // Execute with parameters... + // Note: actual implementation needs to handle dynamic params + }).map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; + + // Trigger async backup to S3 (don't block response) + tokio::spawn(backup_to_s3(req.group.clone())); + + Ok(StatusCode::OK) +} +``` + +### Example POST Requests from CI + +**Single-chart benchmark (compression):** + +```bash +curl -X POST https://bench.vortex.dev/api/ingest \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${BENCH_CI_TOKEN}" \ + -d '{ + "group": "compression", + "commit_hash": "abc123def456789", + "commit_timestamp": 1705968000, + "commit_message": "feat: improve compression ratio", + "commit_author": "developer@example.com", + "results": { + "vortex_compress_ns": 1500000000, + "parquet_compress_ns": 2100000000, + "vortex_decompress_ns": 800000000, + "parquet_decompress_ns": 950000000, + "vortex_size_bytes": 52428800, + "parquet_size_bytes": 58720256 + } + }' +``` + +**Multi-chart benchmark group, single query (TPC-H q1):** + +```bash +curl -X POST https://bench.vortex.dev/api/ingest \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${BENCH_CI_TOKEN}" \ + -d '{ + "group": "tpch_sf1", + "commit_hash": "abc123def456789", + "commit_timestamp": 1705968000, + "commit_message": "feat: improve compression ratio", + "commit_author": "developer@example.com", + "chart": "q1", + "results": { + "vortex_ns": 150000000, + "parquet_ns": 200000000, + "duckdb_vortex_ns": 180000000, + "duckdb_parquet_ns": 175000000, + "datafusion_vortex_ns": 220000000, + "datafusion_parquet_ns": 210000000 + } + }' +``` + +**Multi-chart benchmark group, all queries at once (recommended):** + +For efficiency, CI can POST multiple charts at once: + +```bash +curl -X POST https://bench.vortex.dev/api/ingest/batch \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${BENCH_CI_TOKEN}" \ + -d '{ + "group": "tpch_sf1", + "commit_hash": "abc123def456789", + "commit_timestamp": 1705968000, + "commit_message": "feat: improve compression ratio", + "commit_author": "developer@example.com", + "charts": { + "q1": { + "vortex_ns": 150000000, + "parquet_ns": 200000000, + "duckdb_vortex_ns": 180000000 + }, + "q2": { + "vortex_ns": 85000000, + "parquet_ns": 120000000, + "duckdb_vortex_ns": 95000000 + }, + "q3": { + "vortex_ns": 220000000, + "parquet_ns": 310000000, + "duckdb_vortex_ns": 250000000 + } + } + }' +``` + +**GitHub Actions workflow snippet:** + +```yaml +# .github/workflows/benchmarks.yml +- name: Run TPC-H Benchmarks + run: cargo bench --bench tpch -- --output json > results.json + +- name: Upload Results + env: + BENCH_CI_TOKEN: ${{ secrets.BENCH_CI_TOKEN }} + run: | + # Parse results and POST to benchmark server + # This script transforms benchmark output to our API format + python scripts/upload_benchmarks.py \ + --group tpch_sf1 \ + --commit ${{ github.sha }} \ + --timestamp $(git show -s --format=%ct ${{ github.sha }}) \ + --message "$(git show -s --format=%s ${{ github.sha }})" \ + --author "$(git show -s --format=%ae ${{ github.sha }})" \ + --results results.json \ + --endpoint https://bench.vortex.dev/api/ingest +``` + +--- + +## Client Architecture + +### Rendering Strategy + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ Initial Request │ +│ │ +│ 1. Server receives request for /benchmarks/clickbench │ +│ │ +│ 2. Leptos SSR queries DuckDB: │ +│ WITH recent AS ( │ +│ SELECT commit_hash, timestamp FROM commits │ +│ ORDER BY timestamp DESC LIMIT {N} -- configurable, e.g. 50 │ +│ ) │ +│ SELECT * FROM clickbench JOIN recent USING (commit_hash) │ +│ │ +│ 3. Server renders complete HTML with: │ +│ - Navigation, layout (static) │ +│ - 44 charts with N data points each (SSR'd canvas/svg) │ +│ - Island markers for hydration │ +│ - Serialized props for each island │ +│ │ +│ 4. HTML streams to browser (~500KB for N=50) │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ Browser Receives │ +│ │ +│ 5. HTML renders immediately - user sees charts with N commits │ +│ │ +│ 6. WASM bundle loads (~300KB gzipped) │ +│ │ +│ 7. Islands hydrate - charts become interactive │ +│ - Zoom/pan works within N-commit range │ +│ - "Show full history" button enabled │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ (on user action) +┌─────────────────────────────────────────────────────────────────────────┐ +│ Lazy Load Full History │ +│ │ +│ 8. User clicks "Show full history" on Chart 7 │ +│ │ +│ 9. Client calls server function get_full_chart_history("q6") │ +│ │ +│ 10. Server queries DuckDB for all commits for that chart │ +│ │ +│ 11. ~500KB of data returns for that one chart (5000 commits) │ +│ │ +│ 12. Chart re-renders with full history │ +│ User can now zoom/pan across entire commit range │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### Component Hierarchy + +``` +App +├── Layout +│ ├── Navigation (static) +│ │ └── Group links (compression, tpch_sf1, clickbench, ...) +│ └── Main Content +│ └── (router) +│ +└── Routes + ├── / -> HomePage (static) + │ + ├── /benchmarks/:group -> GroupPage + │ ├── GroupHeader (static) + │ │ ├── Title + │ │ └── Summary statistics + │ │ + │ └── ChartList + │ ├── ChartWithLazyHistory [island] (chart=q1) + │ ├── ChartWithLazyHistory [island] (chart=q2) + │ ├── ... + │ └── ChartWithLazyHistory [island] (chart=qN) + │ + └── /compare -> ComparePage (future) +``` + +### Key Components + +#### GroupPage (Server Component) + +```rust +// src/components/group_page.rs + +/// Configurable initial commit count (tune based on testing) +const INITIAL_COMMITS: usize = 50; // Could be 25-100 + +#[component] +pub fn GroupPage(group_id: String) -> impl IntoView { + // This runs on the server during SSR + let group_config = get_group_config(&group_id); + + // Fetch most recent N commits of data for ALL charts in this group + let initial_data = create_resource( + move || group_id.clone(), + |group| async move { + get_initial_group_data(group, INITIAL_COMMITS).await + } + ); + + view! { +
+ + + }> + {move || initial_data.get().map(|data| { + let charts = group_by_chart(data); + view! { +
+ + } + } + /> +
+ } + })} +
+
+ } +} +``` + +#### ChartWithLazyHistory (Island Component) + +```rust +// src/components/chart.rs + +/// Commit metadata shared across all charts +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CommitInfo { + pub hash: String, + pub timestamp: i64, // Unix timestamp + pub message: Option, +} + +/// A single measurement point on a chart. +/// Only exists for commits that have actual benchmark data. +/// If a commit has no data for a series, there simply won't be a ChartPoint for it. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ChartPoint { + pub commit_idx: usize, // Index into CommitInfo array + pub series: String, // e.g., "vortex_ns", "duckdb_parquet_ns" + pub value: u64, // Raw measurement (e.g., nanoseconds) +} + +/// All data needed to render a chart +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ChartData { + /// All commits in the time range, ordered by timestamp (oldest first). + /// This includes commits WITHOUT benchmark data - the x-axis should show + /// all commits consistently, with gaps where data is missing. + pub commits: Vec, + + /// Measurement points - ONLY for commits that have data. + /// Sparse: if a commit has no data for a series, there's no ChartPoint. + /// When rendering, check if commit_idx exists for the series. + pub points: Vec, + + /// Series names available in this chart (e.g., ["vortex_ns", "parquet_ns"]) + pub series_names: Vec, +} + +impl ChartData { + /// Get all points for a specific series. + /// Returns (commit_idx, value) pairs - only for commits WITH data. + /// Gaps in commit indices indicate missing data (should show as line breaks). + pub fn series_points(&self, series: &str) -> Vec<(usize, u64)> { + self.points + .iter() + .filter(|p| p.series == series) + .map(|p| (p.commit_idx, p.value)) + .collect() + } + + /// Check if a specific commit has data for a series + pub fn has_data(&self, commit_idx: usize, series: &str) -> bool { + self.points.iter().any(|p| p.commit_idx == commit_idx && p.series == series) + } +} + +// ============================================================================ +// HANDLING SPARSE DATA IN CHARTS +// ============================================================================ +// +// Not all commits have benchmark data. This can happen because: +// - Benchmarks only run on certain branches or commit types +// - A benchmark was added after many commits already existed +// - CI failures prevented benchmark runs +// - A new series (target) was added to an existing benchmark +// +// The data model handles this by: +// 1. `commits` array contains ALL commits in the time range (from LEFT JOIN) +// 2. `points` array contains ONLY commits with actual measurements +// 3. When rendering, absence of a ChartPoint for a commit_idx = no data +// +// Chart rendering should: +// - Show all commits on x-axis (consistent spacing) +// - Draw line segments only between consecutive commits WITH data +// - Show gaps (line breaks) where data is missing +// - Tooltips should show "No data" for commits without measurements +// - Do NOT interpolate or connect across gaps +// ============================================================================ + +/// Interactive chart with lazy-loaded full history +/// +/// This is an island - ships as WASM for client-side interactivity. +/// Initial data (N commits) comes from SSR. Full history loads on demand. +#[island] +pub fn ChartWithLazyHistory( + group: String, + chart: String, + initial_data: ChartData, + config: GroupConfig, +) -> impl IntoView { + let (data, set_data) = create_signal(initial_data.clone()); + let (full_history_loaded, set_full_history_loaded) = create_signal(false); + let (loading_history, set_loading_history) = create_signal(false); + + // View range as indices into commits array + let (view_start, set_view_start) = create_signal(0usize); + let (view_end, set_view_end) = create_signal(initial_data.commits.len()); + + let load_full_history = move |_| { + if full_history_loaded.get() || loading_history.get() { + return; + } + set_loading_history.set(true); + + let group = group.clone(); + let chart = chart.clone(); + + spawn_local(async move { + match get_full_chart_history(group, Some(chart)).await { + Ok(full_data) => { + let len = full_data.commits.len(); + set_data.set(full_data); + set_view_end.set(len); + set_full_history_loaded.set(true); + } + Err(e) => { + log::error!("Failed to load history: {}", e); + } + } + set_loading_history.set(false); + }); + }; + + // Auto-load full history when user pans past available data + let on_view_change = move |new_start: usize, new_end: usize| { + if new_start > 0 && !full_history_loaded.get() { + load_full_history(()); + } + set_view_start.set(new_start); + set_view_end.set(new_end); + }; + + view! { +
+
+

{&chart}

+ + + + + + + + {move || format!("{} commits", data.get().commits.len())} + + +
+ + + + +
"← Pan left for full history"
+
+
+ } +} +``` + +#### ChartCanvas (Chart Rendering) + +For charting, use one of these approaches: + +**Option A: plotters + Canvas (Recommended)** + +```rust +use plotters::prelude::*; +use plotters_canvas::CanvasBackend; + +#[component] +fn ChartCanvas( + data: ReadSignal, + view_start: ReadSignal, + view_end: ReadSignal, + config: GroupConfig, + on_view_change: impl Fn(usize, usize) + 'static, +) -> impl IntoView { + let canvas_ref = create_node_ref::(); + + create_effect(move |_| { + let Some(canvas) = canvas_ref.get() else { return }; + let chart_data = data.get(); + let start = view_start.get(); + let end = view_end.get().min(chart_data.commits.len()); + + let backend = CanvasBackend::with_canvas_object(canvas.clone()).unwrap(); + let root = backend.into_drawing_area(); + root.fill(&WHITE).unwrap(); + + // Draw each series - handle sparse data by drawing line segments + // only between consecutive commits that have data + for series_name in &chart_data.series_names { + let points = chart_data.series_points(series_name); + // Filter to visible range [start, end) + let visible: Vec<_> = points.iter() + .filter(|(idx, _)| *idx >= start && *idx < end) + .collect(); + + // Draw line segments between consecutive points + // Gaps in commit indices mean missing data - don't connect across gaps + for window in visible.windows(2) { + let (idx1, val1) = window[0]; + let (idx2, val2) = window[1]; + // Only draw line if commits are adjacent (no gap) + // Or always draw lines and let gaps show as visual discontinuities + // ... plotters line drawing ... + } + } + } + }); + + let on_wheel = move |e: WheelEvent| { + e.prevent_default(); + // Zoom logic + }; + + view! { + + } +} +``` + +**Option B: Custom SVG** + +Alternative if you need more control over rendering. Build SVG elements directly with Leptos's `view!` macro, using computed scales for x/y positioning. More verbose but avoids external dependencies. + +### Data Refresh + +Poll for updates periodically (e.g., every 60 seconds) or rely on users refreshing the page. Since benchmarks don't run that frequently, simple polling is sufficient. + +--- + +## API Design + +### Server Functions (Leptos) + +```rust +// src/api/charts.rs + +/// Number of commits to show in initial view (configurable) +const INITIAL_COMMIT_COUNT: usize = 50; + +/// Get initial data for a benchmark group (most recent N commits) +/// Called during SSR to populate initial page +#[server(GetInitialGroupData)] +pub async fn get_initial_group_data( + group: String, +) -> Result { + let db = use_context::().unwrap(); + let config = get_group_config(&group)?; + + // Use LEFT JOIN: we want ALL recent commits, even those without benchmark data. + // This ensures the x-axis is consistent across all charts. + // Commits without data will have NULL values for series columns. + let sql = if config.chart_column.is_some() { + format!(r#" + WITH recent AS ( + SELECT commit_hash, timestamp, message + FROM commits + ORDER BY timestamp DESC + LIMIT {limit} + ) + SELECT r.commit_hash, r.timestamp, r.message, b.chart, b.* + FROM recent r + LEFT JOIN {group} b ON r.commit_hash = b.commit_hash + ORDER BY b.chart, r.timestamp DESC + "#, limit = INITIAL_COMMIT_COUNT, group = group) + } else { + format!(r#" + WITH recent AS ( + SELECT commit_hash, timestamp, message + FROM commits + ORDER BY timestamp DESC + LIMIT {limit} + ) + SELECT r.commit_hash, r.timestamp, r.message, b.* + FROM recent r + LEFT JOIN {group} b ON r.commit_hash = b.commit_hash + ORDER BY r.timestamp DESC + "#, limit = INITIAL_COMMIT_COUNT, group = group) + }; + + let rows = db.query(|conn| conn.query(&sql, []))?; + Ok(GroupData::from_rows(rows, &config)) +} + +/// Get full history for a single chart +/// Called client-side when user requests full history +#[server(GetFullChartHistory)] +pub async fn get_full_chart_history( + group: String, + chart: Option, +) -> Result { + let db = use_context::().unwrap(); + + // Use LEFT JOIN: include all commits, benchmark data may be NULL. + // For multi-chart groups, filter by chart in the ON clause to get + // NULL for commits that don't have data for this specific chart. + let sql = if let Some(ref chart_name) = chart { + format!(r#" + SELECT c.commit_hash, c.timestamp, c.message, b.* + FROM commits c + LEFT JOIN {group} b ON c.commit_hash = b.commit_hash + AND b.chart = ? + ORDER BY c.timestamp ASC + "#, group = group) + } else { + format!(r#" + SELECT c.commit_hash, c.timestamp, c.message, b.* + FROM commits c + LEFT JOIN {group} b ON c.commit_hash = b.commit_hash + ORDER BY c.timestamp ASC + "#, group = group) + }; + + let rows = db.query(|conn| { + if let Some(chart_name) = chart { + conn.query(&sql, [&chart_name]) + } else { + conn.query(&sql, []) + } + })?; + + Ok(ChartData::from_rows(rows)) +} + +/// Get summary statistics for a benchmark group +#[server(GetGroupSummary)] +pub async fn get_group_summary( + group: String, +) -> Result { + let db = use_context::().unwrap(); + let config = get_group_config(&group)?; + + // Compute statistics over recent commits + // Note: geometric mean requires special handling for u64 values + let sql = format!(r#" + WITH recent AS ( + SELECT commit_hash FROM commits + ORDER BY timestamp DESC + LIMIT 100 -- Summary over last 100 commits + ) + SELECT + AVG(CAST(vortex_ns AS DOUBLE)) as vortex_avg, + EXP(AVG(LN(CAST(NULLIF(vortex_ns, 0) AS DOUBLE)))) as vortex_geomean, + PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY vortex_ns) as vortex_median, + -- ... same for other series ... + FROM {group} + WHERE commit_hash IN (SELECT commit_hash FROM recent) + "#, group = group); + + // ... +} +``` + +### REST Endpoint (CI Ingest) + +``` +POST /api/ingest +Content-Type: application/json +Authorization: Bearer + +{ + "group": "tpch_sf1", + "commit_hash": "abc123def456", + "commit_timestamp": 1705968000, + "chart": "q1", + "results": { + "vortex_ns": 150000000, + "parquet_ns": 200000000, + "lance_ns": 180000000 + } +} + +Response: 200 OK +``` + +--- + +## Deployment & Infrastructure + +### AWS Architecture + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ AWS Account │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ CloudFront │ │ +│ │ Distribution: bench.vortex.dev │ │ +│ │ - Origin: ALB │ │ +│ │ - Cache behaviors: │ │ +│ │ - /api/*: TTL 60s, stale-while-revalidate │ │ +│ │ - /pkg/*: TTL 1 year (WASM, immutable) │ │ +│ │ - /*: TTL 60s (HTML) │ │ +│ └─────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ Application Load Balancer │ │ +│ │ - Health check: /health │ │ +│ │ - Target: ECS service │ │ +│ └─────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ ECS Fargate │ │ +│ │ Service: bench-website │ │ +│ │ - Task: 1 vCPU, 2GB RAM │ │ +│ │ - Container: bench-website:latest │ │ +│ │ - Volume: EFS mount at /data │ │ +│ └─────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ EFS (Elastic File System) │ │ +│ │ - Stores Vortex files │ │ +│ │ - Persists across container restarts │ │ +│ │ - Single-AZ (cost savings, acceptable for this use case) │ │ +│ └─────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ S3 │ │ +│ │ Bucket: vortex-benchmarks-backup │ │ +│ │ - Hourly backups of Vortex files │ │ +│ │ - Lifecycle: Delete after 30 days │ │ +│ └─────────────────────────────────────────────────────────────────┘ │ +│ │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +### Alternative: Simpler Single EC2 + +For lower traffic/budget: + +``` +CloudFront -> EC2 (t3.medium) -> Local EBS for data + -> S3 for backups +``` + +This is simpler and cheaper (~$30/month) but less resilient. Acceptable for internal tooling. + +### Environment Variables + +```bash +# Required +BENCH_DATA_DIR=/data # Path to Vortex files +BENCH_CI_TOKEN= # Token for CI authentication +BENCH_S3_BUCKET=vortex-benchmarks-backup + +# Optional +BENCH_PORT=3000 +BENCH_LOG_LEVEL=info +RUST_LOG=bench_website=debug +``` + +### CI/CD Pipeline + +```yaml +# .github/workflows/deploy.yml +name: Deploy Benchmarks Website + +on: + push: + branches: [main] + paths: + - "bench-website/**" + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build Docker image + run: | + docker build -t bench-website ./bench-website + + - name: Push to ECR + run: | + aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_REGISTRY + docker tag bench-website:latest $ECR_REGISTRY/bench-website:latest + docker push $ECR_REGISTRY/bench-website:latest + + - name: Deploy to ECS + run: | + aws ecs update-service --cluster bench --service bench-website --force-new-deployment +``` + +### Dockerfile + +```dockerfile +# Build stage +FROM rust:1.75 as builder + +WORKDIR /app +COPY . . + +# Install wasm target for Leptos client +RUN rustup target add wasm32-unknown-unknown + +# Install cargo-leptos +RUN cargo install cargo-leptos + +# Build both server and client +RUN cargo leptos build --release + +# Runtime stage +FROM debian:bookworm-slim + +RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Copy server binary +COPY --from=builder /app/target/release/bench-website . + +# Copy client WASM and assets +COPY --from=builder /app/target/site ./site + +# Copy Vortex extension (pre-built) +COPY --from=builder /app/vortex_duckdb.so /usr/local/lib/ + +ENV BENCH_DATA_DIR=/data +ENV BENCH_PORT=3000 + +EXPOSE 3000 + +CMD ["./bench-website"] +``` + +--- + +## Implementation Plan + +The implementation is divided into phases. Each phase produces a working system; later phases add optimizations. **Always get things working before optimizing.** + +### Phase 1: Data Layer Foundation + +**Goal**: DuckDB + Vortex storing real benchmark data, queryable via SQL + +**Steps**: + +1.1. **Set up project structure** + +- Create `bench-website/` directory in vortex repo +- Initialize Cargo workspace with leptos, axum, duckdb dependencies +- Create basic module structure (db/, api/, components/) + + 1.2. **Implement DuckDB connection layer** + +- Create `DbPool` wrapper around DuckDB connection +- Optionally load Vortex extension +- Write helper functions for common query patterns +- Add integration tests with in-memory DuckDB + + 1.3. **Define schemas for all benchmark groups** + +- Create SQL schema files for each group +- Write migration script to create tables +- Document schema in this file (already done above) + + 1.4. **Implement ingest endpoint** + +- POST /api/ingest handler +- CI token verification +- INSERT OR REPLACE logic for idempotency +- Test with curl / httpie + + 1.5. **Migrate existing JSON data** + +- Write one-time migration script +- Parse existing JSON files +- Bulk insert into DuckDB/Vortex tables +- Verify data integrity (row counts, spot checks) + + 1.6. **Set up CI integration** + +- Modify existing benchmark workflows to POST results +- Add BENCH_CI_TOKEN secret to GitHub +- Test end-to-end: commit -> benchmark -> POST -> verify in DB + +**Deliverable**: Running server that accepts benchmark results and stores them in Vortex files. Can query data via DuckDB CLI. + +### Phase 2: Basic Website (No Lazy Loading) + +**Goal**: Functional website showing all charts with all data + +**Steps**: + +2.1. **Set up Leptos app shell** + +- Configure cargo-leptos +- Create App component with router +- Add basic CSS (can use Tailwind or simple custom CSS) +- Verify SSR works (view page source shows content) + + 2.2. **Implement navigation and layout** + +- Header with site title +- Sidebar/tabs for benchmark groups +- Responsive layout basics + + 2.3. **Create GroupPage component** + +- Route: /benchmarks/:group +- Fetch ALL data for group (no lazy loading yet) +- Display loading state via Suspense + + 2.4. **Implement basic chart rendering** + +- Choose charting approach (plotters or SVG) +- Render static charts (no interactivity yet) +- Display all series with different colors +- Add axis labels, legend + + 2.5. **Add chart interactivity (zoom/pan)** + +- Convert chart to island component +- Implement mouse wheel zoom +- Implement click-drag pan +- Ensure smooth 60fps rendering + + 2.6. **Implement summary statistics** + +- Server-side computation (DuckDB aggregates) +- Display above charts +- Different summary types per group (geomean for TPC-H, etc.) + + 2.7. **Handle sparse data gracefully** + +- Skip null values in series +- Don't break line connections (or show gaps appropriately) +- Show "no data" indicator for missing series + +**Deliverable**: Fully functional website. May be slow for ClickBench (loads all 44 charts × 5000 commits) but everything works. + +### Phase 3: Progressive Loading Optimization + +**Goal**: Fast initial load with lazy history loading + +**Steps**: + +3.1. **Modify data fetching for initial load** + +- Change `get_initial_group_data` to fetch only last 50 commits +- Update GroupPage to pass initial_data to charts +- Verify initial HTML size is reasonable (~500KB for ClickBench) + + 3.2. **Implement `GetFullChartHistory` server function** + +- Fetches all commits for single chart +- Called from client when user requests full history +- Returns data sorted by timestamp ASC + + 3.3. **Add lazy loading UI to ChartWithLazyHistory** + +- "Show full history" button +- Loading state indicator +- History loaded indicator (commit count badge) + + 3.4. **Implement auto-load on pan** + +- Detect when user pans left past available data +- Automatically trigger full history load +- Smooth transition as data expands + + 3.5. **Add data refresh polling** + +- Poll every 60 seconds for updates +- Only refresh if new data available +- Update charts without full page reload + +**Deliverable**: Production-ready website with fast initial load and on-demand history. + +### Phase 4: Deployment & Production Hardening + +**Goal**: Reliable production deployment on AWS + +**Steps**: + +4.1. **Create Dockerfile** + +- Multi-stage build (builder + runtime) +- Include Vortex DuckDB extension +- Minimize image size + + 4.2. **Set up AWS infrastructure** + +- Create ECS cluster and service (or EC2 instance) +- Configure EFS for data persistence +- Set up S3 bucket for backups +- Configure CloudFront distribution + + 4.3. **Implement backup system** + +- Hourly backup to S3 after successful ingests +- Restore script for disaster recovery +- Test backup/restore cycle + + 4.4. **Add monitoring and alerting** + +- Health check endpoint +- CloudWatch metrics (request latency, error rate) +- Alerts for failures + + 4.5. **Configure caching** + +- CloudFront cache behaviors +- Cache-Control headers on API responses +- Verify cache invalidation on data updates + + 4.6. **Security hardening** + +- HTTPS only (CloudFront handles TLS) +- CI token rotation procedure +- Rate limiting on ingest endpoint + + 4.7. **Documentation** + +- Update this design doc with any changes +- Runbook for common operations +- Architecture diagram for team reference + +**Deliverable**: Production deployment at bench.vortex.dev with monitoring and backups. + +### Phase 5: Polish & Future Features (Optional) + +**Goal**: Nice-to-have improvements + +**Steps**: + +5.1. **Compare view** + +- Select two commits to compare +- Show diff in benchmark results +- Highlight regressions/improvements + + 5.2. **Permalink to specific chart/commit** + +- URL includes chart and view range +- Share links to specific data points + + 5.3. **Export functionality** + +- Download chart as PNG/SVG +- Export data as CSV + + 5.4. **Performance profiling** + +- Measure actual load times +- Identify bottlenecks +- Optimize as needed (may not be necessary) + +--- + +## Library Reusability (Future Work) + +This project should eventually be architected so others can use it for their own benchmark visualization needs without requiring Vortex. This is not a priority for initial implementation but should be kept in mind during development. + +**Key abstraction points to consider:** + +1. **Storage backend** — The DuckDB + Vortex combination should be swappable for plain DuckDB, PostgreSQL, or other databases +2. **Configuration-driven groups** — Benchmark group definitions should be data-driven (config file or similar) rather than hardcoded +3. **Unit conversion** — The `MeasurementUnit` system for converting u64 raw values to display units should be reusable + +**Potential crate structure (future):** + +``` +bench-viz/ # Library crate (reusable) +├── src/ +│ ├── storage/ # Storage trait + implementations +│ ├── config/ # Group/series configuration +│ └── components/ # Leptos components + +bench-vortex/ # Vortex-specific binary +├── src/ +│ ├── main.rs +│ └── groups.rs # Vortex benchmark group definitions +``` + +For now, implement directly in the vortex repo. Refactor into a library later if there's demand. + +--- + +## Future Considerations + +### Scaling Beyond Current Needs + +The current design handles ~125MB of data and ~200 charts comfortably. If data grows significantly: + +- **More commits (10,000+)**: Consider time-based partitioning of Vortex files (e.g., one file per year) +- **More benchmark groups**: No architectural changes needed, just more files +- **Higher write throughput**: DuckDB handles concurrent writes well, but could add write batching if needed +- **Global low latency**: Deploy read replicas in multiple regions (sync Vortex files to S3, read from nearest region) + +### Alternative Charting Libraries + +If plotters or custom SVG proves insufficient: + +- **uPlot**: Extremely fast (47KB), handles 100k+ points, but requires JS interop +- **ECharts via charming**: Full-featured, but large bundle (~1MB) +- **D3 via wasm-bindgen**: Maximum flexibility, moderate complexity + +### Vortex-Specific Optimizations + +Since we're dogfooding Vortex: + +- **Compression**: Ensure Vortex files use appropriate encoding for benchmark data (u64 integers and strings) +- **Predicate pushdown**: DuckDB + Vortex should push down filters efficiently; verify with EXPLAIN +- **Column pruning**: Queries should only read needed columns; verify Vortex extension does this + +--- + +## Appendix: Quick Reference + +### Common Commands + +```bash +# Local development +cd bench-website +cargo leptos watch + +# Run tests +cargo test + +# Build for production +cargo leptos build --release + +# Query DuckDB directly (for debugging) +duckdb data/benchmarks.duckdb +> SELECT * FROM tpch_sf1 WHERE chart = 'q1' LIMIT 10; + +# Ingest test data +curl -X POST http://localhost:3000/api/ingest \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $BENCH_CI_TOKEN" \ + -d '{"group": "tpch_sf1", "commit_hash": "abc123", "commit_timestamp": 1705968000, "chart": "q1", "results": {"vortex_ns": 100000000}}' +``` + +### Key Files + +| File | Purpose | +| ------------------------- | ------------------------------- | +| `src/db/connection.rs` | DuckDB pool and query helpers | +| `src/api/ingest.rs` | CI ingest endpoint | +| `src/api/charts.rs` | Server functions for chart data | +| `src/components/chart.rs` | Interactive chart island | +| `src/config/groups.rs` | Benchmark group definitions | +| `data/*.vortex` | Vortex data files (gitignored) | + +### Data Sizes (Estimates) + +| Group | Charts | Commits | Rows | ~Size | +| ---------------------- | -------- | ------- | -------- | ---------- | +| compression | 1 | 5000 | 5,000 | 2MB | +| random_access | 1 | 5000 | 5,000 | 2MB | +| tpch_sf1 | 19 | 5000 | 95,000 | 15MB | +| tpch_sf10 | 19 | 5000 | 95,000 | 15MB | +| tpch_sf100 | 19 | 5000 | 95,000 | 15MB | +| clickbench | 44 | 5000 | 220,000 | 25MB | +| other micro-benchmarks | ~100 | 5000 | ~500,000 | 50MB | +| commits | - | 5000 | 5,000 | 1MB | +| **Total** | **~200** | - | **~1M** | **~125MB** | + +Note: Actual current data is ~2000 commits (manually truncated). With full history restored, expect ~125MB total. \ No newline at end of file diff --git a/REUSE.toml b/REUSE.toml index 46787ae62e2..28ba02515ff 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -26,7 +26,8 @@ SPDX-License-Identifier = "CC-BY-4.0" # Utility code. [[annotations]] -path = ["**/README.md", "CLAUDE.md", "CONTRIBUTING.md", "STYLE.md", "tsc/**.md"] +# TODO(connor): Remove this vvv +path = ["**/README.md", "CLAUDE.md", "CONTRIBUTING.md", "STYLE.md", "tsc/**.md", "BENCHMARK_WEBSITE_REDESIGN.md"] SPDX-FileCopyrightText = "Copyright the Vortex contributors" SPDX-License-Identifier = "CC-BY-4.0" From 815e156533d9a9ded0db8e40e6a190810de79fee Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Fri, 23 Jan 2026 18:52:45 -0500 Subject: [PATCH 2/4] better plan of attack Signed-off-by: Connor Tsui --- BENCHMARK_WEBSITE_REDESIGN.md | 355 ++++++++++++++++------------------ 1 file changed, 164 insertions(+), 191 deletions(-) diff --git a/BENCHMARK_WEBSITE_REDESIGN.md b/BENCHMARK_WEBSITE_REDESIGN.md index ff38d572f19..3b3eb4b220b 100644 --- a/BENCHMARK_WEBSITE_REDESIGN.md +++ b/BENCHMARK_WEBSITE_REDESIGN.md @@ -16,7 +16,7 @@ _This is mostly Claude-written, but it is also the result of quite a lot of prot 6. [Client Architecture](#client-architecture) 7. [API Design](#api-design) 8. [Deployment & Infrastructure](#deployment--infrastructure) -9. [Implementation Plan](#implementation-plan) +9. [Plan of Attack](#plan-of-attack) 10. [Future Considerations](#future-considerations) --- @@ -38,8 +38,8 @@ A benchmarks visualization website (https://bench.vortex.dev/) that displays per - Dogfooding: Use Vortex file format for storing benchmark data via DuckDB extension - Reusability: Architected as a library that others can adapt for their benchmarking needs -- SEO optimization: This would be a nice to have, but speed is paramount -- Mobile-first design: Desktop is primary use case, but this should work on mobile too +- SEO optimization: Nice to have, but speed is paramount +- Mobile-first design: Desktop is primary use case, but should work on mobile too - Public API for benchmark data (internal tooling only) --- @@ -1361,256 +1361,229 @@ CMD ["./bench-website"] --- -## Implementation Plan +## Plan of Attack -The implementation is divided into phases. Each phase produces a working system; later phases add optimizations. **Always get things working before optimizing.** +This is a **prototype-first, library-oriented** implementation plan. The goal is to build a reusable benchmark visualization library that can be plugged into any data source, with Vortex-specific integration handled separately. -### Phase 1: Data Layer Foundation +### Key Design Decisions -**Goal**: DuckDB + Vortex storing real benchmark data, queryable via SQL +- **Plotters** for charting (Rust-native, Canvas rendering) +- **Plain DuckDB for prototyping** - Add Vortex extension once core functionality works +- **Mock data for development** - Real data pipeline is separate; design for pluggability +- **Library-first mindset** - Keep Vortex-specific details separate from core visualization -**Steps**: - -1.1. **Set up project structure** - -- Create `bench-website/` directory in vortex repo -- Initialize Cargo workspace with leptos, axum, duckdb dependencies -- Create basic module structure (db/, api/, components/) - - 1.2. **Implement DuckDB connection layer** - -- Create `DbPool` wrapper around DuckDB connection -- Optionally load Vortex extension -- Write helper functions for common query patterns -- Add integration tests with in-memory DuckDB - - 1.3. **Define schemas for all benchmark groups** - -- Create SQL schema files for each group -- Write migration script to create tables -- Document schema in this file (already done above) - - 1.4. **Implement ingest endpoint** +### Key Simplifications -- POST /api/ingest handler -- CI token verification -- INSERT OR REPLACE logic for idempotency -- Test with curl / httpie +1. **Get a working prototype ASAP** - Mock/hardcode where possible +2. **Start with 1-2 benchmark groups** - Not all schemas upfront +3. **CI integration comes last** - Once everything works locally +4. **Design for pluggability** - Core lib shouldn't depend on Vortex specifics - 1.5. **Migrate existing JSON data** - -- Write one-time migration script -- Parse existing JSON files -- Bulk insert into DuckDB/Vortex tables -- Verify data integrity (row counts, spot checks) - - 1.6. **Set up CI integration** - -- Modify existing benchmark workflows to POST results -- Add BENCH_CI_TOKEN secret to GitHub -- Test end-to-end: commit -> benchmark -> POST -> verify in DB - -**Deliverable**: Running server that accepts benchmark results and stores them in Vortex files. Can query data via DuckDB CLI. +--- -### Phase 2: Basic Website (No Lazy Loading) +### Phase 1: Minimal Working Prototype -**Goal**: Functional website showing all charts with all data +**Goal**: See a Leptos page rendering one chart with mock data from DuckDB **Steps**: -2.1. **Set up Leptos app shell** - -- Configure cargo-leptos -- Create App component with router -- Add basic CSS (can use Tailwind or simple custom CSS) -- Verify SSR works (view page source shows content) - - 2.2. **Implement navigation and layout** - -- Header with site title -- Sidebar/tabs for benchmark groups -- Responsive layout basics - - 2.3. **Create GroupPage component** - -- Route: /benchmarks/:group -- Fetch ALL data for group (no lazy loading yet) -- Display loading state via Suspense +1.1. **Set up project structure** +- Create `bench-website/` with basic Leptos + Axum skeleton +- Add `duckdb` and `plotters` crate dependencies +- Single `main.rs` with inline routes and components + +1.2. **Mock data setup** +- Create test data generator that produces representative benchmark data +- Create TWO tables only: + - `commits` (commit_hash, timestamp, message, author) + - `random_access` (simple single-chart group with ~3 series) +- Generate ~100 mock commits with realistic patterns (some regressions, improvements) +- Store as local DuckDB file (plain DuckDB for now, Vortex extension added after Phase 3) + +1.3. **Single chart page** +- Hardcoded route `/` that queries DuckDB +- Server-side render a basic HTML page with data +- Render using plotters to Canvas (or SVG initially to validate data flow) +- No islands, no interactivity yet - just prove SSR + DuckDB works + +**Deliverable**: `cargo leptos watch` shows a page with mock benchmark data - 2.4. **Implement basic chart rendering** +--- -- Choose charting approach (plotters or SVG) -- Render static charts (no interactivity yet) -- Display all series with different colors -- Add axis labels, legend +### Phase 2: Basic Interactive Charts - 2.5. **Add chart interactivity (zoom/pan)** +**Goal**: Interactive charts with zoom/pan for two benchmark groups -- Convert chart to island component -- Implement mouse wheel zoom -- Implement click-drag pan -- Ensure smooth 60fps rendering +**Steps**: - 2.6. **Implement summary statistics** +2.1. **Plotters integration** +- Set up plotters with Canvas backend for WASM +- Render actual line charts for random_access mock data +- Show multiple series with different colors +- Basic axis labels and legend -- Server-side computation (DuckDB aggregates) -- Display above charts -- Different summary types per group (geomean for TPC-H, etc.) +2.2. **Add second benchmark group (multi-chart)** +- Add `tpch_sf1` table with mock data (multi-chart: q1-q19) +- Create `/benchmarks/:group` route +- GroupPage component that lists multiple charts +- Demonstrate the "Pattern B" schema (commit_hash + chart + series columns) - 2.7. **Handle sparse data gracefully** +2.3. **Make charts interactive (islands)** +- Convert chart component to `#[island]` +- Implement zoom/pan with mouse wheel via plotters +- Test that WASM hydration works -- Skip null values in series -- Don't break line connections (or show gaps appropriately) -- Show "no data" indicator for missing series +**Deliverable**: Two working benchmark groups with interactive charts -**Deliverable**: Fully functional website. May be slow for ClickBench (loads all 44 charts × 5000 commits) but everything works. +--- -### Phase 3: Progressive Loading Optimization +### Phase 3: Library Interface & Configuration -**Goal**: Fast initial load with lazy history loading +**Goal**: Clean interface for plugging in real data sources **Steps**: -3.1. **Modify data fetching for initial load** - -- Change `get_initial_group_data` to fetch only last 50 commits -- Update GroupPage to pass initial_data to charts -- Verify initial HTML size is reasonable (~500KB for ClickBench) - - 3.2. **Implement `GetFullChartHistory` server function** - -- Fetches all commits for single chart -- Called from client when user requests full history -- Returns data sorted by timestamp ASC +3.1. **Define pluggable interfaces** +- `BenchmarkGroup` trait/struct for group configuration +- `SeriesConfig` for series metadata (colors, units, display names) +- `MeasurementUnit` enum for value formatting (ns → ms, bytes → MB, etc.) - 3.3. **Add lazy loading UI to ChartWithLazyHistory** +3.2. **Configuration-driven groups** +- Move hardcoded group definitions to configuration +- Support both "single chart" and "multi-chart" patterns +- Dynamic schema creation based on config -- "Show full history" button -- Loading state indicator -- History loaded indicator (commit count badge) +3.3. **Navigation & layout** +- Sidebar with all configured benchmark groups +- Responsive design basics +- URL routing for all groups - 3.4. **Implement auto-load on pan** +3.4. **Add Vortex extension** +- Load Vortex DuckDB extension +- Convert DuckDB storage to Vortex files +- Verify queries work correctly with Vortex tables -- Detect when user pans left past available data -- Automatically trigger full history load -- Smooth transition as data expands +**Deliverable**: Clean library interface with Vortex storage, easy to add new benchmark groups - 3.5. **Add data refresh polling** - -- Poll every 60 seconds for updates -- Only refresh if new data available -- Update charts without full page reload - -**Deliverable**: Production-ready website with fast initial load and on-demand history. +--- -### Phase 4: Deployment & Production Hardening +### Phase 4: Progressive Loading Optimization -**Goal**: Reliable production deployment on AWS +**Goal**: Fast initial load, lazy history on demand **Steps**: -4.1. **Create Dockerfile** - -- Multi-stage build (builder + runtime) -- Include Vortex DuckDB extension -- Minimize image size +4.1. **Limit initial data** +- Fetch only last 50 commits on page load +- Verify initial HTML is small (~500KB) - 4.2. **Set up AWS infrastructure** +4.2. **Lazy load full history** +- "Show full history" button per chart +- Server function to fetch all commits for one chart +- Auto-trigger on pan-left -- Create ECS cluster and service (or EC2 instance) -- Configure EFS for data persistence -- Set up S3 bucket for backups -- Configure CloudFront distribution - - 4.3. **Implement backup system** - -- Hourly backup to S3 after successful ingests -- Restore script for disaster recovery -- Test backup/restore cycle +4.3. **Performance tuning** +- Measure actual load times +- Add indexes if needed +- Cache headers for API responses - 4.4. **Add monitoring and alerting** +**Deliverable**: Sub-second initial page load -- Health check endpoint -- CloudWatch metrics (request latency, error rate) -- Alerts for failures +--- - 4.5. **Configure caching** +### Phase 5: Ingest Endpoint -- CloudFront cache behaviors -- Cache-Control headers on API responses -- Verify cache invalidation on data updates +**Goal**: Generic API for submitting benchmark results - 4.6. **Security hardening** +**Steps**: -- HTTPS only (CloudFront handles TLS) -- CI token rotation procedure -- Rate limiting on ingest endpoint +5.1. **Implement POST /api/ingest** +- Accept benchmark results in standardized format +- Simple token auth (env var) +- UPSERT logic for idempotency +- Works for any configured benchmark group - 4.7. **Documentation** +5.2. **Batch ingest support** +- POST /api/ingest/batch for multiple charts at once +- Useful for groups like TPC-H where all queries run together -- Update this design doc with any changes -- Runbook for common operations -- Architecture diagram for team reference +**Deliverable**: Working ingest API (integration with specific CI is user's responsibility) -**Deliverable**: Production deployment at bench.vortex.dev with monitoring and backups. +--- -### Phase 5: Polish & Future Features (Optional) +### Phase 6: Production Readiness -**Goal**: Nice-to-have improvements +**Goal**: Ready for deployment **Steps**: -5.1. **Compare view** - -- Select two commits to compare -- Show diff in benchmark results -- Highlight regressions/improvements - - 5.2. **Permalink to specific chart/commit** +6.1. **Dockerfile** +- Multi-stage Docker build +- Minimal runtime image +- Configurable via environment variables -- URL includes chart and view range -- Share links to specific data points - - 5.3. **Export functionality** - -- Download chart as PNG/SVG -- Export data as CSV +6.2. **Health & observability** +- Health check endpoint +- Basic logging +- Metrics hooks (optional) - 5.4. **Performance profiling** +6.3. **Documentation** +- How to configure benchmark groups +- How to set up ingest +- How to deploy -- Measure actual load times -- Identify bottlenecks -- Optimize as needed (may not be necessary) +**Deliverable**: Production-ready library that can be deployed and integrated --- -## Library Reusability (Future Work) +### What's Deferred Until Later Phases -This project should eventually be architected so others can use it for their own benchmark visualization needs without requiring Vortex. This is not a priority for initial implementation but should be kept in mind during development. +- Vortex DuckDB extension (plain DuckDB for prototyping, add Vortex after Phase 3) +- Vortex-specific CI integration (user will plug in their pipeline) +- Real data import scripts (user has separate pipeline) -**Key abstraction points to consider:** +### What's Explicitly Out of Scope -1. **Storage backend** — The DuckDB + Vortex combination should be swappable for plain DuckDB, PostgreSQL, or other databases -2. **Configuration-driven groups** — Benchmark group definitions should be data-driven (config file or similar) rather than hardcoded -3. **Unit conversion** — The `MeasurementUnit` system for converting u64 raw values to display units should be reusable +- Compare view / permalinks +- Export functionality (PNG/CSV) +- Mobile optimization beyond basics +- Materialized views +- Complex backup/restore automation -**Potential crate structure (future):** +--- -``` -bench-viz/ # Library crate (reusable) -├── src/ -│ ├── storage/ # Storage trait + implementations -│ ├── config/ # Group/series configuration -│ └── components/ # Leptos components +### Files to Create -bench-vortex/ # Vortex-specific binary +``` +bench-website/ +├── Cargo.toml ├── src/ -│ ├── main.rs -│ └── groups.rs # Vortex benchmark group definitions +│ ├── main.rs # Axum server setup, routes +│ ├── lib.rs # Leptos app root +│ ├── mock_data.rs # Test data generation (prototyping only) +│ ├── db/ +│ │ ├── mod.rs +│ │ ├── connection.rs # DuckDB connection pool +│ │ ├── queries.rs # SQL query functions +│ │ └── models.rs # Data structures +│ ├── api/ +│ │ ├── mod.rs +│ │ ├── ingest.rs # POST /api/ingest endpoint +│ │ └── charts.rs # Server functions for chart data +│ ├── components/ +│ │ ├── mod.rs +│ │ ├── app.rs # Root component, router +│ │ ├── layout.rs # Navigation, sidebar +│ │ ├── group_page.rs # Benchmark group page +│ │ └── chart.rs # Interactive chart island (plotters) +│ └── config/ +│ ├── mod.rs +│ └── groups.rs # Benchmark group definitions +├── style/ +│ └── main.css +└── Dockerfile ``` -For now, implement directly in the vortex repo. Refactor into a library later if there's demand. - --- ## Future Considerations @@ -1619,14 +1592,14 @@ For now, implement directly in the vortex repo. Refactor into a library later if The current design handles ~125MB of data and ~200 charts comfortably. If data grows significantly: -- **More commits (10,000+)**: Consider time-based partitioning of Vortex files (e.g., one file per year) -- **More benchmark groups**: No architectural changes needed, just more files +- **More commits (10,000+)**: Consider time-based partitioning (e.g., one file per year) +- **More benchmark groups**: No architectural changes needed, just more tables - **Higher write throughput**: DuckDB handles concurrent writes well, but could add write batching if needed -- **Global low latency**: Deploy read replicas in multiple regions (sync Vortex files to S3, read from nearest region) +- **Global low latency**: Deploy read replicas in multiple regions ### Alternative Charting Libraries -If plotters or custom SVG proves insufficient: +If plotters proves insufficient: - **uPlot**: Extremely fast (47KB), handles 100k+ points, but requires JS interop - **ECharts via charming**: Full-featured, but large bundle (~1MB) From fa0618547965b8ab1dbaca688dda791a525acf4d Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Fri, 23 Jan 2026 19:58:07 -0500 Subject: [PATCH 3/4] phase 1 prototype Signed-off-by: Connor Tsui --- Cargo.lock | 1064 ++++- Cargo.toml | 2 + bench-website/Cargo.lock | 4253 ++++++++++++++++++ bench-website/Cargo.toml | 78 + bench-website/src/components/app.rs | 47 + bench-website/src/components/chart/mod.rs | 166 + bench-website/src/components/chart/paths.rs | 61 + bench-website/src/components/chart/scales.rs | 40 + bench-website/src/components/chart/ticks.rs | 53 + bench-website/src/components/home.rs | 43 + bench-website/src/components/mod.rs | 7 + bench-website/src/db/connection.rs | 111 + bench-website/src/db/mod.rs | 14 + bench-website/src/db/models.rs | 59 + bench-website/src/db/queries.rs | 143 + bench-website/src/lib.rs | 12 + bench-website/src/main.rs | 154 + bench-website/src/mock_data.rs | 89 + bench-website/style/main.css | 105 + 19 files changed, 6499 insertions(+), 2 deletions(-) create mode 100644 bench-website/Cargo.lock create mode 100644 bench-website/Cargo.toml create mode 100644 bench-website/src/components/app.rs create mode 100644 bench-website/src/components/chart/mod.rs create mode 100644 bench-website/src/components/chart/paths.rs create mode 100644 bench-website/src/components/chart/scales.rs create mode 100644 bench-website/src/components/chart/ticks.rs create mode 100644 bench-website/src/components/home.rs create mode 100644 bench-website/src/components/mod.rs create mode 100644 bench-website/src/db/connection.rs create mode 100644 bench-website/src/db/mod.rs create mode 100644 bench-website/src/db/models.rs create mode 100644 bench-website/src/db/queries.rs create mode 100644 bench-website/src/lib.rs create mode 100644 bench-website/src/main.rs create mode 100644 bench-website/src/mock_data.rs create mode 100644 bench-website/style/main.css diff --git a/Cargo.lock b/Cargo.lock index dc468062558..3087b2c8a3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,6 +139,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "any_spawner" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1384d3fe1eecb464229fcf6eebb72306591c56bf27b373561489458a7c73027d" +dependencies = [ + "futures", + "thiserror 2.0.18", + "tokio", + "wasm-bindgen-futures", +] + [[package]] name = "anyhow" version = "1.0.100" @@ -732,6 +744,12 @@ dependencies = [ "futures-lite", ] +[[package]] +name = "async-once-cell" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" + [[package]] name = "async-process" version = "2.5.0" @@ -851,6 +869,36 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "attribute-derive" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05832cdddc8f2650cc2cc187cc2e952b8c133a48eb055f35211f61ee81502d77" +dependencies = [ + "attribute-derive-macro", + "derive-where", + "manyhow", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "attribute-derive-macro" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7cdbbd4bd005c5d3e2e9c885e6fa575db4f4a3572335b974d8db853b6beb61" +dependencies = [ + "collection_literals", + "interpolator", + "manyhow", + "proc-macro-utils", + "proc-macro2", + "quote", + "quote-use", + "syn 2.0.114", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -1208,6 +1256,62 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +dependencies = [ + "axum-core", + "base64", + "bytes", + "form_urlencoded", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "multer", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backon" version = "1.6.0" @@ -1219,6 +1323,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "base16" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8" + [[package]] name = "base64" version = "0.22.1" @@ -1241,6 +1351,30 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bench-website" +version = "0.1.0" +dependencies = [ + "axum", + "chrono", + "console_error_panic_hook", + "duckdb", + "leptos", + "leptos_axum", + "leptos_meta", + "leptos_router", + "rand 0.9.2", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + [[package]] name = "better_io" version = "0.2.0" @@ -1557,6 +1691,12 @@ dependencies = [ "libbz2-rs-sys", ] +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" + [[package]] name = "cast" version = "0.3.0" @@ -1767,6 +1907,17 @@ dependencies = [ "cc", ] +[[package]] +name = "codee" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dbbdc4b4d349732bc6690de10a9de952bd39ba6a065c586e26600b6b0b91f5" +dependencies = [ + "serde", + "serde_json", + "thiserror 2.0.18", +] + [[package]] name = "codespan-reporting" version = "0.13.1" @@ -1865,6 +2016,12 @@ dependencies = [ "regex-lite", ] +[[package]] +name = "collection_literals" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2550f75b8cfac212855f6b1885455df8eaee8fe8e246b647d69146142e016084" + [[package]] name = "colorchoice" version = "1.0.4" @@ -1971,6 +2128,19 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af" +[[package]] +name = "config" +version = "0.15.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30fa8254caad766fc03cb0ccae691e14bf3bd72bfff27f72802ce729551b3d6" +dependencies = [ + "convert_case 0.6.0", + "pathdiff", + "serde_core", + "toml", + "winnow", +] + [[package]] name = "console" version = "0.15.11" @@ -1996,6 +2166,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -2022,6 +2202,12 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "const-str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0664d2867b4a32697dfe655557f5c3b187e9b605b38612a748e5ec99811d160" + [[package]] name = "const_for" version = "0.1.5" @@ -2048,6 +2234,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "const_str_slice_concat" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67855af358fcb20fac58f9d714c94e2b228fe5694c1c9b4ead4a366343eda1b" + [[package]] name = "constant_time_eq" version = "0.3.1" @@ -2060,6 +2252,24 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "convert_case" version = "0.10.0" @@ -2383,6 +2593,12 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + [[package]] name = "datafusion" version = "50.3.0" @@ -3683,6 +3899,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derive-where" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "derive_arbitrary" version = "1.4.2" @@ -3709,7 +3936,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case", + "convert_case 0.10.0", "proc-macro2", "quote", "rustc_version", @@ -3795,12 +4022,35 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" +[[package]] +name = "drain_filter_polyfill" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" + [[package]] name = "dtype_dispatch" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3a5ccdfd6c5e7e2fea9c5cf256f2a08216047fab19c621c3da64e9ae4a1462d" +[[package]] +name = "duckdb" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7eeb487dde618b9f6ab26a451775ad5fac3fabe1ca2b64cbbe90b105f264ccd" +dependencies = [ + "arrow 56.2.0", + "cast", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libduckdb-sys", + "num-integer", + "rust_decimal", + "strum 0.27.2", +] + [[package]] name = "duckdb-bench" version = "0.1.0" @@ -3835,6 +4085,16 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "either_of" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216d23e0ec69759a17f05e1c553f3a6870e5ec73420fbb07807a6f34d5d1d5a4" +dependencies = [ + "paste", + "pin-project-lite", +] + [[package]] name = "encode_unicode" version = "1.0.0" @@ -3905,6 +4165,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1731451909bde27714eacba19c2566362a7f35224f52b153d3f42cf60f72472" + [[package]] name = "errno" version = "0.3.14" @@ -3990,6 +4256,18 @@ dependencies = [ "ext-trait", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fancy-regex" version = "0.11.0" @@ -4240,6 +4518,7 @@ dependencies = [ "futures-core", "futures-task", "futures-util", + "num_cpus", ] [[package]] @@ -4381,6 +4660,27 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "gloo-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http 1.4.0", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "gloo-timers" version = "0.3.0" @@ -4393,6 +4693,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "goldenfile" version = "1.9.1" @@ -4411,6 +4724,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9e2d4c0a8296178d8802098410ca05d86b17a10bb5ab559b3fb404c1f948220" +[[package]] +name = "guardian" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17e2ac29387b1aa07a1e448f7bb4f35b500787971e965b02842b900afa5c8f6f" + [[package]] name = "h2" version = "0.4.13" @@ -4489,6 +4808,15 @@ dependencies = [ "foldhash 0.2.0", ] +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "heck" version = "0.5.0" @@ -4525,6 +4853,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "html-escape" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +dependencies = [ + "utf8-width", +] + [[package]] name = "htmlescape" version = "0.3.1" @@ -4586,6 +4923,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + [[package]] name = "httparse" version = "1.10.1" @@ -4593,7 +4936,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] -name = "humansize" +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humansize" version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" @@ -4607,6 +4956,22 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +[[package]] +name = "hydration_context" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8714ae4adeaa846d838f380fbd72f049197de629948f91bf045329e0cf0a283" +dependencies = [ + "futures", + "js-sys", + "once_cell", + "or_poisoned", + "pin-project-lite", + "serde", + "throw_error", + "wasm-bindgen", +] + [[package]] name = "hyper" version = "1.8.1" @@ -4621,6 +4986,7 @@ dependencies = [ "http 1.4.0", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "pin-utils", @@ -4924,6 +5290,12 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" +[[package]] +name = "interpolator" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" + [[package]] name = "inventory" version = "0.3.21" @@ -5663,6 +6035,228 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "leptos" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9569fc37575a5d64c0512145af7630bf651007237ef67a8a77328199d315bb" +dependencies = [ + "any_spawner", + "base64", + "cfg-if", + "either_of", + "futures", + "getrandom 0.3.4", + "hydration_context", + "leptos_config", + "leptos_dom", + "leptos_hot_reload", + "leptos_macro", + "leptos_server", + "oco_ref", + "or_poisoned", + "paste", + "rand 0.9.2", + "reactive_graph", + "rustc-hash", + "rustc_version", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn", + "slotmap", + "tachys", + "thiserror 2.0.18", + "throw_error", + "typed-builder 0.23.2", + "typed-builder-macro 0.23.2", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm_split_helpers", + "web-sys", +] + +[[package]] +name = "leptos_axum" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0caa95760f87f3067e05025140becefdbdfd36cbc2adac4519f06e1f1edf4af" +dependencies = [ + "any_spawner", + "axum", + "dashmap", + "futures", + "hydration_context", + "leptos", + "leptos_integration_utils", + "leptos_macro", + "leptos_meta", + "leptos_router", + "parking_lot", + "server_fn", + "tachys", + "tokio", + "tower", + "tower-http", +] + +[[package]] +name = "leptos_config" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071fc40aeb9fcab885965bad1887990477253ad51f926cd19068f45a44c59e89" +dependencies = [ + "config", + "regex", + "serde", + "thiserror 2.0.18", + "typed-builder 0.21.2", +] + +[[package]] +name = "leptos_dom" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f4330c88694c5575e0bfe4eecf81b045d14e76a4f8b00d5fd2a63f8779f895" +dependencies = [ + "js-sys", + "or_poisoned", + "reactive_graph", + "send_wrapper", + "tachys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_hot_reload" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d61ec3e1ff8aaee8c5151688550c0363f85bc37845450764c31ff7584a33f38" +dependencies = [ + "anyhow", + "camino", + "indexmap", + "parking_lot", + "proc-macro2", + "quote", + "rstml", + "serde", + "syn 2.0.114", + "walkdir", +] + +[[package]] +name = "leptos_integration_utils" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13cccc9305df53757bae61bf15641bfa6a667b5f78456ace4879dfe0591ae0e8" +dependencies = [ + "futures", + "hydration_context", + "leptos", + "leptos_config", + "leptos_meta", + "leptos_router", + "reactive_graph", +] + +[[package]] +name = "leptos_macro" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86ffd2e9cf3e264e9b3e16bdb086cefa26bd0fa7bc6a26b0cc5f6c1fd3178ed" +dependencies = [ + "attribute-derive", + "cfg-if", + "convert_case 0.10.0", + "html-escape", + "itertools 0.14.0", + "leptos_hot_reload", + "prettyplease", + "proc-macro-error2", + "proc-macro2", + "quote", + "rstml", + "rustc_version", + "server_fn_macro", + "syn 2.0.114", + "uuid", +] + +[[package]] +name = "leptos_meta" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d489e38d3f541e9e43ecc2e3a815527840345a2afca629b3e23fcc1dd254578" +dependencies = [ + "futures", + "indexmap", + "leptos", + "or_poisoned", + "send_wrapper", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_router" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e573711f2fb9ab5d655ec38115220d359eaaf1dcb93cc0ea624543b6dba959" +dependencies = [ + "any_spawner", + "either_of", + "futures", + "gloo-net", + "js-sys", + "leptos", + "leptos_router_macro", + "or_poisoned", + "percent-encoding", + "reactive_graph", + "rustc_version", + "send_wrapper", + "tachys", + "thiserror 2.0.18", + "url", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_router_macro" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "409c0bd99f986c3cfa1a4db2443c835bc602ded1a12784e22ecb28c3ed5a2ae2" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "leptos_server" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf1045af93050bf3388d1c138426393fc131f6d9e46a65519da884c033ed730" +dependencies = [ + "any_spawner", + "base64", + "codee", + "futures", + "hydration_context", + "or_poisoned", + "reactive_graph", + "send_wrapper", + "serde", + "serde_json", + "server_fn", + "tachys", +] + [[package]] name = "levenshtein_automata" version = "0.2.1" @@ -5738,6 +6332,22 @@ version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +[[package]] +name = "libduckdb-sys" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8c60c2d269e63ae5197e4fe9075efffed35dfda0095a5ac8b41f3c765b18456" +dependencies = [ + "flate2", + "pkg-config", + "reqwest", + "serde", + "serde_json", + "tar", + "vcpkg", + "zip", +] + [[package]] name = "libfuzzer-sys" version = "0.4.10" @@ -5804,6 +6414,12 @@ dependencies = [ "bitflags 2.10.0", ] +[[package]] +name = "linear-map" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" + [[package]] name = "link-cplusplus" version = "1.0.12" @@ -5973,6 +6589,29 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58093314a45e00c77d5c508f76e77c3396afbbc0d01506e7fae47b018bac2b1d" +[[package]] +name = "manyhow" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587" +dependencies = [ + "manyhow-macros", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "manyhow-macros" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", +] + [[package]] name = "matchers" version = "0.2.0" @@ -5982,6 +6621,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "matrixmultiply" version = "0.3.10" @@ -6123,6 +6768,23 @@ dependencies = [ "uuid", ] +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 1.4.0", + "httparse", + "memchr", + "mime", + "spin", + "version_check", +] + [[package]] name = "multimap" version = "0.10.1" @@ -6195,6 +6857,12 @@ version = "6.6.666" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf5a574dadd7941adeaa71823ecba5e28331b8313fb2e1c6a5c7e5981ea53ad6" +[[package]] +name = "next_tuple" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60993920e071b0c9b66f14e2b32740a4e27ffc82854dcd72035887f336a09a28" + [[package]] name = "nix" version = "0.29.0" @@ -6566,6 +7234,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "oco_ref" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0423ff9973dea4d6bd075934fdda86ebb8c05bdf9d6b0507067d4a1226371d" +dependencies = [ + "serde", + "thiserror 2.0.18", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -6750,6 +7428,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "or_poisoned" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c04f5d74368e4d0dfe06c45c8627c81bd7c317d52762d118fb9b3076f6420fd" + [[package]] name = "ordered-float" version = "2.10.1" @@ -6958,6 +7642,12 @@ dependencies = [ "stfu8", ] +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "pbkdf2" version = "0.12.2" @@ -7389,6 +8079,17 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "proc-macro-utils" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071" +dependencies = [ + "proc-macro2", + "quote", + "smallvec", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -7398,6 +8099,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", + "version_check", + "yansi", +] + [[package]] name = "prost" version = "0.12.6" @@ -7710,6 +8424,28 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "quote-use" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9619db1197b497a36178cfc736dc96b271fe918875fbf1344c436a7e93d0321e" +dependencies = [ + "quote", + "quote-use-macros", +] + +[[package]] +name = "quote-use-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82ebfb7faafadc06a7ab141a6f67bcfb24cb8beb158c6fe933f2f035afa99f35" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "r-efi" version = "5.3.0" @@ -7962,6 +8698,60 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "reactive_graph" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f0df355582937223ea403e52490201d65295bd6981383c69bfae5a1f8730c2" +dependencies = [ + "any_spawner", + "async-lock", + "futures", + "guardian", + "hydration_context", + "indexmap", + "or_poisoned", + "paste", + "pin-project-lite", + "rustc-hash", + "rustc_version", + "send_wrapper", + "serde", + "slotmap", + "thiserror 2.0.18", + "web-sys", +] + +[[package]] +name = "reactive_stores" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35372f05664a62a3dd389503371a15b8feb3396f99f6ec000de651fddb030942" +dependencies = [ + "dashmap", + "guardian", + "itertools 0.14.0", + "or_poisoned", + "paste", + "reactive_graph", + "reactive_stores_macro", + "rustc-hash", + "send_wrapper", +] + +[[package]] +name = "reactive_stores_macro" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fa40919eb2975100283b2a70e68eafce1e8bcf81f0622ff168e4c2b3f8d46bb" +dependencies = [ + "convert_case 0.8.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -8267,6 +9057,21 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "rstml" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61cf4616de7499fc5164570d40ca4e1b24d231c6833a88bff0fe00725080fd56" +dependencies = [ + "derive-where", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.114", + "syn_derive", + "thiserror 2.0.18", +] + [[package]] name = "rust-ini" version = "0.21.3" @@ -8536,6 +9341,15 @@ version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + [[package]] name = "seq-macro" version = "0.3.6" @@ -8595,6 +9409,28 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_qs" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3faaf9e727533a19351a43cc5a8de957372163c7d35cc48c90b75cdda13c352" +dependencies = [ + "percent-encoding", + "serde", + "thiserror 2.0.18", +] + [[package]] name = "serde_repr" version = "0.1.20" @@ -8636,6 +9472,71 @@ dependencies = [ "serde", ] +[[package]] +name = "server_fn" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353d02fa2886cd8dae0b8da0965289fa8f2ecc7df633d1ce965f62fdf9644d29" +dependencies = [ + "axum", + "base64", + "bytes", + "const-str", + "const_format", + "dashmap", + "futures", + "gloo-net", + "http 1.4.0", + "http-body-util", + "hyper", + "inventory", + "js-sys", + "pin-project-lite", + "rustc_version", + "rustversion", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror 2.0.18", + "throw_error", + "tokio", + "tower", + "tower-layer", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950b8cfc9ff5f39ca879c5a7c5e640de2695a199e18e424c3289d0964cabe642" +dependencies = [ + "const_format", + "convert_case 0.8.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.114", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63eb08f80db903d3c42f64e60ebb3875e0305be502bdc064ec0a0eab42207f00" +dependencies = [ + "server_fn_macro", + "syn 2.0.114", +] + [[package]] name = "sha1" version = "0.10.6" @@ -9025,6 +9926,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb066a04799e45f5d582e8fc6ec8e6d6896040d00898eb4e6a835196815b219" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -9104,6 +10017,40 @@ dependencies = [ "testing_table", ] +[[package]] +name = "tachys" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b2db11e455f7e84e2cc3e76f8a3f3843f7956096265d5ecff781eabe235077" +dependencies = [ + "any_spawner", + "async-trait", + "const_str_slice_concat", + "drain_filter_polyfill", + "either_of", + "erased", + "futures", + "html-escape", + "indexmap", + "itertools 0.14.0", + "js-sys", + "linear-map", + "next_tuple", + "oco_ref", + "or_poisoned", + "parking_lot", + "paste", + "reactive_graph", + "reactive_stores", + "rustc-hash", + "rustc_version", + "send_wrapper", + "slotmap", + "throw_error", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "taffy" version = "0.9.2" @@ -9513,6 +10460,15 @@ dependencies = [ "ordered-float 2.10.1", ] +[[package]] +name = "throw_error" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0ed6038fcbc0795aca7c92963ddda636573b956679204e044492d2b13c8f64" +dependencies = [ + "pin-project-lite", +] + [[package]] name = "time" version = "0.3.45" @@ -9649,6 +10605,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -9758,6 +10726,7 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -9774,13 +10743,19 @@ dependencies = [ "http 1.4.0", "http-body 1.0.1", "http-body-util", + "http-range-header", + "httpdate", "iri-string", + "mime", + "mime_guess", + "percent-encoding", "pin-project-lite", "tokio", "tokio-util", "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -9895,6 +10870,23 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http 1.4.0", + "httparse", + "log", + "rand 0.9.2", + "sha1", + "thiserror 2.0.18", + "utf-8", +] + [[package]] name = "twox-hash" version = "2.1.2" @@ -9904,6 +10896,46 @@ dependencies = [ "rand 0.9.2", ] +[[package]] +name = "typed-builder" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef81aec2ca29576f9f6ae8755108640d0a86dd3161b2e8bca6cfa554e98f77d" +dependencies = [ + "typed-builder-macro 0.21.2", +] + +[[package]] +name = "typed-builder" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31aa81521b70f94402501d848ccc0ecaa8f93c8eb6999eb9747e72287757ffda" +dependencies = [ + "typed-builder-macro 0.23.2", +] + +[[package]] +name = "typed-builder-macro" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecb9ecf7799210407c14a8cfdfe0173365780968dc57973ed082211958e0b18" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "typed-builder-macro" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076a02dc54dd46795c2e9c8282ed40bcfb1e22747e955de9389a1de28190fb26" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "typenum" version = "1.19.0" @@ -10006,6 +11038,12 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8-ranges" version = "1.0.5" @@ -11163,6 +12201,28 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm_split_helpers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a114b3073258dd5de3d812cdd048cca6842342755e828a14dbf15f843f2d1b84" +dependencies = [ + "async-once-cell", + "wasm_split_macros", +] + +[[package]] +name = "wasm_split_macros" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56481f8ed1a9f9ae97ea7b08a5e2b12e8adf9a7818a6ba952b918e09c7be8bf0" +dependencies = [ + "base16", + "quote", + "sha2", + "syn 2.0.114", +] + [[package]] name = "web-sys" version = "0.3.85" diff --git a/Cargo.toml b/Cargo.toml index 86582a0c114..93e107a0d3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,8 @@ members = [ "benchmarks/datafusion-bench", "benchmarks/duckdb-bench", "benchmarks/random-access-bench", + # Benchmarks Website + "bench-website", ] exclude = ["java/testfiles", "wasm-test"] resolver = "2" diff --git a/bench-website/Cargo.lock b/bench-website/Cargo.lock new file mode 100644 index 00000000000..596f7a49728 --- /dev/null +++ b/bench-website/Cargo.lock @@ -0,0 +1,4253 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "const-random", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "any_spawner" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1384d3fe1eecb464229fcf6eebb72306591c56bf27b373561489458a7c73027d" +dependencies = [ + "futures", + "thiserror 2.0.18", + "tokio", + "wasm-bindgen-futures", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "arrow" +version = "56.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e833808ff2d94ed40d9379848a950d995043c7fb3e81a30b383f4c6033821cc" +dependencies = [ + "arrow-arith", + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-data", + "arrow-ord", + "arrow-row", + "arrow-schema", + "arrow-select", + "arrow-string", +] + +[[package]] +name = "arrow-arith" +version = "56.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad08897b81588f60ba983e3ca39bda2b179bdd84dced378e7df81a5313802ef8" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "chrono", + "num", +] + +[[package]] +name = "arrow-array" +version = "56.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8548ca7c070d8db9ce7aa43f37393e4bfcf3f2d3681df278490772fd1673d08d" +dependencies = [ + "ahash 0.8.12", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "chrono", + "half", + "hashbrown 0.16.1", + "num", +] + +[[package]] +name = "arrow-buffer" +version = "56.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e003216336f70446457e280807a73899dd822feaf02087d31febca1363e2fccc" +dependencies = [ + "bytes", + "half", + "num", +] + +[[package]] +name = "arrow-cast" +version = "56.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919418a0681298d3a77d1a315f625916cb5678ad0d74b9c60108eb15fd083023" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", + "atoi", + "base64", + "chrono", + "comfy-table", + "half", + "lexical-core", + "num", + "ryu", +] + +[[package]] +name = "arrow-data" +version = "56.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5c64fff1d142f833d78897a772f2e5b55b36cb3e6320376f0961ab0db7bd6d0" +dependencies = [ + "arrow-buffer", + "arrow-schema", + "half", + "num", +] + +[[package]] +name = "arrow-ord" +version = "56.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8f82583eb4f8d84d4ee55fd1cb306720cddead7596edce95b50ee418edf66f" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", +] + +[[package]] +name = "arrow-row" +version = "56.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d07ba24522229d9085031df6b94605e0f4b26e099fb7cdeec37abd941a73753" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "half", +] + +[[package]] +name = "arrow-schema" +version = "56.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3aa9e59c611ebc291c28582077ef25c97f1975383f1479b12f3b9ffee2ffabe" +dependencies = [ + "bitflags", +] + +[[package]] +name = "arrow-select" +version = "56.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c41dbbd1e97bfcaee4fcb30e29105fb2c75e4d82ae4de70b792a5d3f66b2e7a" +dependencies = [ + "ahash 0.8.12", + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "num", +] + +[[package]] +name = "arrow-string" +version = "56.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f5183c150fbc619eede22b861ea7c0eebed8eaac0333eaa7f6da5205fd504d" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", + "memchr", + "num", + "regex", + "regex-syntax", +] + +[[package]] +name = "async-compression" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" +dependencies = [ + "compression-codecs", + "compression-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-once-cell" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "attribute-derive" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05832cdddc8f2650cc2cc187cc2e952b8c133a48eb055f35211f61ee81502d77" +dependencies = [ + "attribute-derive-macro", + "derive-where", + "manyhow", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "attribute-derive-macro" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7cdbbd4bd005c5d3e2e9c885e6fa575db4f4a3572335b974d8db853b6beb61" +dependencies = [ + "collection_literals", + "interpolator", + "manyhow", + "proc-macro-utils", + "proc-macro2", + "quote", + "quote-use", + "syn 2.0.114", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +dependencies = [ + "axum-core", + "base64", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "multer", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "base16" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bench-website" +version = "0.1.0" +dependencies = [ + "axum", + "chrono", + "console_error_panic_hook", + "duckdb", + "leptos", + "leptos_axum", + "leptos_meta", + "leptos_router", + "rand 0.9.2", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "codee" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dbbdc4b4d349732bc6690de10a9de952bd39ba6a065c586e26600b6b0b91f5" +dependencies = [ + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "collection_literals" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2550f75b8cfac212855f6b1885455df8eaee8fe8e246b647d69146142e016084" + +[[package]] +name = "comfy-table" +version = "7.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d05af1e006a2407bedef5af410552494ce5be9090444dbbcb57258c1af3d56" +dependencies = [ + "strum 0.26.3", + "strum_macros 0.26.4", + "unicode-width", +] + +[[package]] +name = "compression-codecs" +version = "0.4.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a" +dependencies = [ + "compression-core", + "flate2", + "memchr", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "config" +version = "0.15.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30fa8254caad766fc03cb0ccae691e14bf3bd72bfff27f72802ce729551b3d6" +dependencies = [ + "convert_case 0.6.0", + "pathdiff", + "serde_core", + "toml", + "winnow", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "const-str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0664d2867b4a32697dfe655557f5c3b187e9b605b38612a748e5ec99811d160" + +[[package]] +name = "const_format" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "const_str_slice_concat" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67855af358fcb20fac58f9d714c94e2b228fe5694c1c9b4ead4a366343eda1b" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + +[[package]] +name = "derive-where" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "drain_filter_polyfill" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" + +[[package]] +name = "duckdb" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7eeb487dde618b9f6ab26a451775ad5fac3fabe1ca2b64cbbe90b105f264ccd" +dependencies = [ + "arrow", + "cast", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libduckdb-sys", + "num-integer", + "rust_decimal", + "strum 0.27.2", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "either_of" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216d23e0ec69759a17f05e1c553f3a6870e5ec73420fbb07807a6f34d5d1d5a4" +dependencies = [ + "paste", + "pin-project-lite", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1731451909bde27714eacba19c2566362a7f35224f52b153d3f42cf60f72472" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" + +[[package]] +name = "flate2" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "gloo-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "guardian" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17e2ac29387b1aa07a1e448f7bb4f35b500787971e965b02842b900afa5c8f6f" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "html-escape" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +dependencies = [ + "utf8-width", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hydration_context" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8714ae4adeaa846d838f380fbd72f049197de629948f91bf045329e0cf0a283" +dependencies = [ + "futures", + "js-sys", + "once_cell", + "or_poisoned", + "pin-project-lite", + "serde", + "throw_error", + "wasm-bindgen", +] + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "interpolator" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" + +[[package]] +name = "inventory" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" +dependencies = [ + "rustversion", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leptos" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9569fc37575a5d64c0512145af7630bf651007237ef67a8a77328199d315bb" +dependencies = [ + "any_spawner", + "base64", + "cfg-if", + "either_of", + "futures", + "getrandom 0.3.4", + "hydration_context", + "leptos_config", + "leptos_dom", + "leptos_hot_reload", + "leptos_macro", + "leptos_server", + "oco_ref", + "or_poisoned", + "paste", + "rand 0.9.2", + "reactive_graph", + "rustc-hash", + "rustc_version", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn", + "slotmap", + "tachys", + "thiserror 2.0.18", + "throw_error", + "typed-builder 0.23.2", + "typed-builder-macro 0.23.2", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm_split_helpers", + "web-sys", +] + +[[package]] +name = "leptos_axum" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0caa95760f87f3067e05025140becefdbdfd36cbc2adac4519f06e1f1edf4af" +dependencies = [ + "any_spawner", + "axum", + "dashmap", + "futures", + "hydration_context", + "leptos", + "leptos_integration_utils", + "leptos_macro", + "leptos_meta", + "leptos_router", + "parking_lot", + "server_fn", + "tachys", + "tokio", + "tower", + "tower-http", +] + +[[package]] +name = "leptos_config" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071fc40aeb9fcab885965bad1887990477253ad51f926cd19068f45a44c59e89" +dependencies = [ + "config", + "regex", + "serde", + "thiserror 2.0.18", + "typed-builder 0.21.2", +] + +[[package]] +name = "leptos_dom" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f4330c88694c5575e0bfe4eecf81b045d14e76a4f8b00d5fd2a63f8779f895" +dependencies = [ + "js-sys", + "or_poisoned", + "reactive_graph", + "send_wrapper", + "tachys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_hot_reload" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d61ec3e1ff8aaee8c5151688550c0363f85bc37845450764c31ff7584a33f38" +dependencies = [ + "anyhow", + "camino", + "indexmap", + "parking_lot", + "proc-macro2", + "quote", + "rstml", + "serde", + "syn 2.0.114", + "walkdir", +] + +[[package]] +name = "leptos_integration_utils" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13cccc9305df53757bae61bf15641bfa6a667b5f78456ace4879dfe0591ae0e8" +dependencies = [ + "futures", + "hydration_context", + "leptos", + "leptos_config", + "leptos_meta", + "leptos_router", + "reactive_graph", +] + +[[package]] +name = "leptos_macro" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86ffd2e9cf3e264e9b3e16bdb086cefa26bd0fa7bc6a26b0cc5f6c1fd3178ed" +dependencies = [ + "attribute-derive", + "cfg-if", + "convert_case 0.10.0", + "html-escape", + "itertools", + "leptos_hot_reload", + "prettyplease", + "proc-macro-error2", + "proc-macro2", + "quote", + "rstml", + "rustc_version", + "server_fn_macro", + "syn 2.0.114", + "uuid", +] + +[[package]] +name = "leptos_meta" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d489e38d3f541e9e43ecc2e3a815527840345a2afca629b3e23fcc1dd254578" +dependencies = [ + "futures", + "indexmap", + "leptos", + "or_poisoned", + "send_wrapper", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_router" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e573711f2fb9ab5d655ec38115220d359eaaf1dcb93cc0ea624543b6dba959" +dependencies = [ + "any_spawner", + "either_of", + "futures", + "gloo-net", + "js-sys", + "leptos", + "leptos_router_macro", + "or_poisoned", + "percent-encoding", + "reactive_graph", + "rustc_version", + "send_wrapper", + "tachys", + "thiserror 2.0.18", + "url", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_router_macro" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "409c0bd99f986c3cfa1a4db2443c835bc602ded1a12784e22ecb28c3ed5a2ae2" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "leptos_server" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf1045af93050bf3388d1c138426393fc131f6d9e46a65519da884c033ed730" +dependencies = [ + "any_spawner", + "base64", + "codee", + "futures", + "hydration_context", + "or_poisoned", + "reactive_graph", + "send_wrapper", + "serde", + "serde_json", + "server_fn", + "tachys", +] + +[[package]] +name = "lexical-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8d125a277f807e55a77304455eb7b1cb52f2b18c143b60e766c120bd64a594" +dependencies = [ + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] + +[[package]] +name = "lexical-parse-float" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a9f232fbd6f550bc0137dcb5f99ab674071ac2d690ac69704593cb4abbea56" +dependencies = [ + "lexical-parse-integer", + "lexical-util", +] + +[[package]] +name = "lexical-parse-integer" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a7a039f8fb9c19c996cd7b2fcce303c1b2874fe1aca544edc85c4a5f8489b34" +dependencies = [ + "lexical-util", +] + +[[package]] +name = "lexical-util" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17" + +[[package]] +name = "lexical-write-float" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c438c87c013188d415fbabbb1dceb44249ab81664efbd31b14ae55dabb6361" +dependencies = [ + "lexical-util", + "lexical-write-integer", +] + +[[package]] +name = "lexical-write-integer" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "409851a618475d2d5796377cad353802345cba92c867d9fbcde9cf4eac4e14df" +dependencies = [ + "lexical-util", +] + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "libduckdb-sys" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8c60c2d269e63ae5197e4fe9075efffed35dfda0095a5ac8b41f3c765b18456" +dependencies = [ + "flate2", + "pkg-config", + "reqwest", + "serde", + "serde_json", + "tar", + "vcpkg", + "zip", +] + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libredox" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +dependencies = [ + "bitflags", + "libc", + "redox_syscall 0.7.0", +] + +[[package]] +name = "linear-map" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "manyhow" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587" +dependencies = [ + "manyhow-macros", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "manyhow-macros" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "memchr", + "mime", + "spin", + "version_check", +] + +[[package]] +name = "next_tuple" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60993920e071b0c9b66f14e2b32740a4e27ffc82854dcd72035887f336a09a28" + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oco_ref" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0423ff9973dea4d6bd075934fdda86ebb8c05bdf9d6b0507067d4a1226371d" +dependencies = [ + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "or_poisoned" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c04f5d74368e4d0dfe06c45c8627c81bd7c317d52762d118fb9b3076f6420fd" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.114", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "proc-macro-utils" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071" +dependencies = [ + "proc-macro2", + "quote", + "smallvec", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", + "version_check", + "yansi", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "quote-use" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9619db1197b497a36178cfc736dc96b271fe918875fbf1344c436a7e93d0321e" +dependencies = [ + "quote", + "quote-use-macros", +] + +[[package]] +name = "quote-use-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82ebfb7faafadc06a7ab141a6f67bcfb24cb8beb158c6fe933f2f035afa99f35" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "reactive_graph" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f0df355582937223ea403e52490201d65295bd6981383c69bfae5a1f8730c2" +dependencies = [ + "any_spawner", + "async-lock", + "futures", + "guardian", + "hydration_context", + "indexmap", + "or_poisoned", + "paste", + "pin-project-lite", + "rustc-hash", + "rustc_version", + "send_wrapper", + "serde", + "slotmap", + "thiserror 2.0.18", + "web-sys", +] + +[[package]] +name = "reactive_stores" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35372f05664a62a3dd389503371a15b8feb3396f99f6ec000de651fddb030942" +dependencies = [ + "dashmap", + "guardian", + "itertools", + "or_poisoned", + "paste", + "reactive_graph", + "reactive_stores_macro", + "rustc-hash", + "send_wrapper", +] + +[[package]] +name = "reactive_stores_macro" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fa40919eb2975100283b2a70e68eafce1e8bcf81f0622ff168e4c2b3f8d46bb" +dependencies = [ + "convert_case 0.8.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_syscall" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rkyv" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rstml" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61cf4616de7499fc5164570d40ca4e1b24d231c6833a88bff0fe00725080fd56" +dependencies = [ + "derive-where", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.114", + "syn_derive", + "thiserror 2.0.18", +] + +[[package]] +name = "rust_decimal" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f703d19852dbf87cbc513643fa81428361eb6940f1ac14fd58155d295a3eb0" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_qs" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3faaf9e727533a19351a43cc5a8de957372163c7d35cc48c90b75cdda13c352" +dependencies = [ + "percent-encoding", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "server_fn" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353d02fa2886cd8dae0b8da0965289fa8f2ecc7df633d1ce965f62fdf9644d29" +dependencies = [ + "axum", + "base64", + "bytes", + "const-str", + "const_format", + "dashmap", + "futures", + "gloo-net", + "http", + "http-body-util", + "hyper", + "inventory", + "js-sys", + "pin-project-lite", + "rustc_version", + "rustversion", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror 2.0.18", + "throw_error", + "tokio", + "tower", + "tower-layer", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950b8cfc9ff5f39ca879c5a7c5e640de2695a199e18e424c3289d0964cabe642" +dependencies = [ + "const_format", + "convert_case 0.8.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.114", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63eb08f80db903d3c42f64e60ebb3875e0305be502bdc064ec0a0eab42207f00" +dependencies = [ + "server_fn_macro", + "syn 2.0.114", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.114", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb066a04799e45f5d582e8fc6ec8e6d6896040d00898eb4e6a835196815b219" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "tachys" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b2db11e455f7e84e2cc3e76f8a3f3843f7956096265d5ecff781eabe235077" +dependencies = [ + "any_spawner", + "async-trait", + "const_str_slice_concat", + "drain_filter_polyfill", + "either_of", + "erased", + "futures", + "html-escape", + "indexmap", + "itertools", + "js-sys", + "linear-map", + "next_tuple", + "oco_ref", + "or_poisoned", + "parking_lot", + "paste", + "reactive_graph", + "reactive_stores", + "rustc-hash", + "rustc_version", + "send_wrapper", + "slotmap", + "throw_error", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tar" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "throw_error" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0ed6038fcbc0795aca7c92963ddda636573b956679204e044492d2b13c8f64" +dependencies = [ + "pin-project-lite", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.9.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +dependencies = [ + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "async-compression", + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "iri-string", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.2", + "sha1", + "thiserror 2.0.18", + "utf-8", +] + +[[package]] +name = "typed-builder" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef81aec2ca29576f9f6ae8755108640d0a86dd3161b2e8bca6cfa554e98f77d" +dependencies = [ + "typed-builder-macro 0.21.2", +] + +[[package]] +name = "typed-builder" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31aa81521b70f94402501d848ccc0ecaa8f93c8eb6999eb9747e72287757ffda" +dependencies = [ + "typed-builder-macro 0.23.2", +] + +[[package]] +name = "typed-builder-macro" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecb9ecf7799210407c14a8cfdfe0173365780968dc57973ed082211958e0b18" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "typed-builder-macro" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076a02dc54dd46795c2e9c8282ed40bcfb1e22747e955de9389a1de28190fb26" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +dependencies = [ + "getrandom 0.3.4", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.114", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasm_split_helpers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a114b3073258dd5de3d812cdd048cca6842342755e828a14dbf15f843f2d1b84" +dependencies = [ + "async-once-cell", + "wasm_split_macros", +] + +[[package]] +name = "wasm_split_macros" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56481f8ed1a9f9ae97ea7b08a5e2b12e8adf9a7818a6ba952b918e09c7be8bf0" +dependencies = [ + "base16", + "quote", + "sha2", + "syn 2.0.114", +] + +[[package]] +name = "web-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "zip" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a05c7c36fde6c09b08576c9f7fb4cda705990f73b58fe011abf7dfb24168b" +dependencies = [ + "arbitrary", + "crc32fast", + "flate2", + "indexmap", + "memchr", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3" + +[[package]] +name = "zmij" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] diff --git a/bench-website/Cargo.toml b/bench-website/Cargo.toml new file mode 100644 index 00000000000..e2d2c4eea06 --- /dev/null +++ b/bench-website/Cargo.toml @@ -0,0 +1,78 @@ +[package] +name = "bench-website" +version = "0.1.0" +edition = "2024" +rust-version = "1.89" +publish = false + +[lib] +crate-type = ["cdylib", "rlib"] + +[[bin]] +name = "bench-website" +path = "src/main.rs" + +[dependencies] +# Leptos core +leptos = { version = "0.8", features = ["nightly"] } +leptos_axum = { version = "0.8", optional = true } +leptos_router = { version = "0.8" } +leptos_meta = { version = "0.8" } + +# Backend +axum = { version = "0.8", optional = true } +tokio = { version = "1", features = ["full"], optional = true } +tower = { version = "0.5", optional = true } +tower-http = { version = "0.6", features = ["fs", "compression-gzip"], optional = true } + +# Database +duckdb = { version = "1", optional = true } + +# Serialization +serde = { version = "1", features = ["derive"] } +serde_json = "1" + +# Utilities +chrono = { version = "0.4", features = ["serde"] } +rand = "0.9" +thiserror = "2" +tracing = "0.1" +tracing-subscriber = { version = "0.3", optional = true } + +# WASM +wasm-bindgen = { version = "0.2", optional = true } +console_error_panic_hook = { version = "0.1", optional = true } + +[features] +default = ["ssr"] +ssr = [ + "leptos/ssr", + "dep:leptos_axum", + "dep:axum", + "dep:tokio", + "dep:tower", + "dep:tower-http", + "dep:duckdb", + "dep:tracing-subscriber", +] +hydrate = [ + "leptos/hydrate", + "dep:wasm-bindgen", + "dep:console_error_panic_hook", +] + +[package.metadata.leptos] +output-name = "bench-website" +site-root = "target/site" +site-pkg-dir = "pkg" +style-file = "style/main.css" +assets-dir = "public" +site-addr = "127.0.0.1:3000" +reload-port = 3001 +browserquery = "defaults" +watch = false +env = "DEV" +bin-features = ["ssr"] +bin-default-features = false +lib-features = ["hydrate"] +lib-default-features = false diff --git a/bench-website/src/components/app.rs b/bench-website/src/components/app.rs new file mode 100644 index 00000000000..27b1a877d19 --- /dev/null +++ b/bench-website/src/components/app.rs @@ -0,0 +1,47 @@ +use leptos::prelude::*; +use leptos_meta::Meta; +use leptos_meta::Title; +use leptos_meta::provide_meta_context; +use leptos_router::components::Route; +use leptos_router::components::Router; +use leptos_router::components::Routes; +use leptos_router::path; + +use crate::components::HomePage; + +#[component] +pub fn App() -> impl IntoView { + provide_meta_context(); + + view! { + + <Meta name="description" content="Performance benchmarks for Vortex columnar file format"/> + + <Router> + <main class="min-h-screen bg-gray-100"> + <nav class="bg-gray-900 text-white p-4 shadow-lg"> + <div class="container mx-auto flex items-center justify-between"> + <a href="/" class="text-xl font-bold hover:text-gray-300 transition-colors"> + "Vortex Benchmarks" + </a> + <a + href="https://github.com/spiraldb/vortex" + target="_blank" + rel="noopener noreferrer" + class="text-gray-300 hover:text-white transition-colors" + > + "GitHub" + </a> + </div> + </nav> + <Routes fallback=|| view! { + <div class="container mx-auto py-8 px-4"> + <h1 class="text-2xl font-bold text-gray-800">"Page not found"</h1> + </div> + }> + <Route path=path!("/") view=HomePage /> + </Routes> + </main> + </Router> + } +} diff --git a/bench-website/src/components/chart/mod.rs b/bench-website/src/components/chart/mod.rs new file mode 100644 index 00000000000..65613a9ccbf --- /dev/null +++ b/bench-website/src/components/chart/mod.rs @@ -0,0 +1,166 @@ +//! SVG chart component for rendering benchmark data. +//! +//! This module provides a server-side rendered SVG chart component. +//! For Phase 1, charts are static. Phase 2 will add interactivity +//! via the Plotters Canvas backend. +//! +//! # Submodules +//! +//! - [`scales`]: Coordinate transformation functions +//! - [`paths`]: SVG path data generation +//! - [`ticks`]: Axis tick mark computation + +pub mod paths; +pub mod scales; +pub mod ticks; + +use leptos::prelude::*; + +use self::paths::generate_series_paths; +use self::scales::HEIGHT; +use self::scales::PADDING; +use self::scales::WIDTH; +use self::ticks::compute_x_ticks; +use self::ticks::compute_y_ticks; +use crate::db::ChartData; + +/// Renders an SVG line chart for benchmark data. +/// +/// Displays multiple series as colored lines with a legend. +/// Axes show time values (Y) and commit dates (X). +#[component] +pub fn Chart(data: ChartData) -> impl IntoView { + let (y_min, y_max) = data.y_range(); + let x_max = data.commits.len() as f64; + + // Generate chart data using helper modules + let paths = generate_series_paths(&data.series, x_max, y_min, y_max); + let y_ticks = compute_y_ticks(y_min, y_max, 5); + let x_ticks = compute_x_ticks(&data.commits, 5); + + let title = data.title.clone(); + let unit = data.unit.clone(); + + view! { + <div class="chart-container bg-white rounded-lg shadow-md p-4 mb-6"> + <h3 class="text-lg font-semibold text-gray-800 mb-4">{title}</h3> + <svg + width=WIDTH.to_string() + height=HEIGHT.to_string() + class="chart-svg" + viewBox=format!("0 0 {} {}", WIDTH, HEIGHT) + > + // Background + <rect + x="0" y="0" + width=WIDTH.to_string() + height=HEIGHT.to_string() + fill="#f9fafb" + /> + + // Grid lines + {y_ticks.iter().map(|tick| { + view! { + <line + x1=PADDING.to_string() + y1=tick.position.to_string() + x2=(WIDTH - PADDING).to_string() + y2=tick.position.to_string() + stroke="#e5e7eb" + stroke-width="1" + /> + } + }).collect_view()} + + // Y-axis + <line + x1=PADDING.to_string() + y1=PADDING.to_string() + x2=PADDING.to_string() + y2=(HEIGHT - PADDING).to_string() + stroke="#9ca3af" + stroke-width="1" + /> + + // X-axis + <line + x1=PADDING.to_string() + y1=(HEIGHT - PADDING).to_string() + x2=(WIDTH - PADDING).to_string() + y2=(HEIGHT - PADDING).to_string() + stroke="#9ca3af" + stroke-width="1" + /> + + // Y-axis labels + {y_ticks.iter().map(|tick| { + view! { + <text + x=(PADDING - 8.0).to_string() + y=tick.position.to_string() + text-anchor="end" + dominant-baseline="middle" + style="font-size: 10px; fill: #6b7280" + > + {tick.label.clone()} + </text> + } + }).collect_view()} + + // Y-axis title + <text + x="15" + y=(HEIGHT / 2.0).to_string() + transform=format!("rotate(-90, 15, {})", HEIGHT / 2.0) + text-anchor="middle" + style="font-size: 12px; fill: #4b5563" + > + {format!("Time ({})", unit)} + </text> + + // X-axis tick labels + {x_ticks.iter().map(|tick| { + view! { + <text + x=tick.position.to_string() + y=(HEIGHT - PADDING + 15.0).to_string() + text-anchor="middle" + style="font-size: 10px; fill: #6b7280" + > + {tick.label.clone()} + </text> + } + }).collect_view()} + + // Series lines + {paths.iter().map(|p| { + view! { + <path + d=p.path_d.clone() + stroke=p.color.clone() + stroke-width="2" + fill="none" + /> + } + }).collect_view()} + </svg> + + // Legend + <div class="flex gap-6 mt-4 justify-center"> + {data.series.iter().map(|s| { + let color = s.color.clone(); + let name = s.display_name.clone(); + view! { + <div class="flex items-center gap-2"> + <div + class="w-4 h-3 rounded-sm" + style=format!("background-color: {}", color) + ></div> + <span class="text-sm text-gray-700">{name}</span> + </div> + } + }).collect_view()} + </div> + </div> + } +} diff --git a/bench-website/src/components/chart/paths.rs b/bench-website/src/components/chart/paths.rs new file mode 100644 index 00000000000..23d62099037 --- /dev/null +++ b/bench-website/src/components/chart/paths.rs @@ -0,0 +1,61 @@ +//! SVG path generation for chart series. + +use super::scales::scale_x; +use super::scales::scale_y; +use crate::db::Series; + +/// A rendered series path ready for SVG output. +pub struct SeriesPath { + /// CSS color value (e.g., "#19a508"). + pub color: String, + /// SVG path data string (e.g., "M 60 200 L 70 195 L 80 198"). + pub path_d: String, +} + +/// Generates SVG path data for all series. +/// +/// # Arguments +/// * `series` - The series data to render +/// * `x_max` - Total number of commits (for X scaling) +/// * `y_min` - Minimum Y value (for Y scaling) +/// * `y_max` - Maximum Y value (for Y scaling) +pub fn generate_series_paths( + series: &[Series], + x_max: f64, + y_min: f64, + y_max: f64, +) -> Vec<SeriesPath> { + series + .iter() + .map(|s| { + let points: Vec<(f64, f64)> = s + .points + .iter() + .map(|p| { + ( + scale_x(p.commit_idx, x_max), + scale_y(p.value_ms(), y_min, y_max), + ) + }) + .collect(); + + let path_d = points + .iter() + .enumerate() + .map(|(i, (x, y))| { + if i == 0 { + format!("M {} {}", x, y) + } else { + format!("L {} {}", x, y) + } + }) + .collect::<Vec<_>>() + .join(" "); + + SeriesPath { + color: s.color.clone(), + path_d, + } + }) + .collect() +} diff --git a/bench-website/src/components/chart/scales.rs b/bench-website/src/components/chart/scales.rs new file mode 100644 index 00000000000..2e89a17fce8 --- /dev/null +++ b/bench-website/src/components/chart/scales.rs @@ -0,0 +1,40 @@ +//! Coordinate scaling functions for chart rendering. +//! +//! These functions transform data coordinates to SVG pixel coordinates. + +/// Chart dimension constants. +pub const WIDTH: f64 = 800.0; +pub const HEIGHT: f64 = 400.0; +pub const PADDING: f64 = 60.0; + +/// Returns the usable chart width (total width minus padding on both sides). +pub fn chart_width() -> f64 { + WIDTH - 2.0 * PADDING +} + +/// Returns the usable chart height (total height minus padding on both sides). +pub fn chart_height() -> f64 { + HEIGHT - 2.0 * PADDING +} + +/// Transforms a commit index to an X pixel coordinate. +/// +/// # Arguments +/// * `idx` - The commit index (0-based) +/// * `x_max` - The total number of commits +pub fn scale_x(idx: usize, x_max: f64) -> f64 { + PADDING + (idx as f64 / x_max) * chart_width() +} + +/// Transforms a data value to a Y pixel coordinate. +/// +/// Note: SVG Y coordinates increase downward, so this inverts the value +/// to place higher values at the top of the chart. +/// +/// # Arguments +/// * `val` - The data value (e.g., milliseconds) +/// * `y_min` - The minimum Y value in the data range +/// * `y_max` - The maximum Y value in the data range +pub fn scale_y(val: f64, y_min: f64, y_max: f64) -> f64 { + HEIGHT - PADDING - ((val - y_min) / (y_max - y_min)) * chart_height() +} diff --git a/bench-website/src/components/chart/ticks.rs b/bench-website/src/components/chart/ticks.rs new file mode 100644 index 00000000000..7cdaa014ddb --- /dev/null +++ b/bench-website/src/components/chart/ticks.rs @@ -0,0 +1,53 @@ +//! Axis tick mark computation for charts. + +use super::scales::scale_x; +use super::scales::scale_y; +use crate::db::CommitInfo; + +/// A tick mark with its pixel position and label text. +pub struct Tick { + /// Pixel coordinate (X for x-ticks, Y for y-ticks). + pub position: f64, + /// Human-readable label. + pub label: String, +} + +/// Computes Y-axis tick marks with evenly spaced values. +/// +/// # Arguments +/// * `y_min` - Minimum Y value +/// * `y_max` - Maximum Y value +/// * `count` - Number of tick marks (not including endpoints adds 1) +pub fn compute_y_ticks(y_min: f64, y_max: f64, count: usize) -> Vec<Tick> { + (0..=count) + .map(|i| { + let val = y_min + (y_max - y_min) * (i as f64 / count as f64); + Tick { + position: scale_y(val, y_min, y_max), + label: format!("{:.0}", val), + } + }) + .collect() +} + +/// Computes X-axis tick marks at evenly spaced commit intervals. +/// +/// Returns tick marks with date labels (MM/DD format). +/// +/// # Arguments +/// * `commits` - The commit data (for timestamps) +/// * `count` - Number of tick marks +pub fn compute_x_ticks(commits: &[CommitInfo], count: usize) -> Vec<Tick> { + let commit_count = commits.len(); + let x_max = commit_count as f64; + + (0..=count) + .filter_map(|i| { + let idx = (i * commit_count.saturating_sub(1)) / count; + commits.get(idx).map(|c| Tick { + position: scale_x(idx, x_max), + label: c.timestamp.format("%m/%d").to_string(), + }) + }) + .collect() +} diff --git a/bench-website/src/components/home.rs b/bench-website/src/components/home.rs new file mode 100644 index 00000000000..e4f8b1e6265 --- /dev/null +++ b/bench-website/src/components/home.rs @@ -0,0 +1,43 @@ +use leptos::prelude::*; + +use crate::components::Chart; +use crate::db::ChartData; + +#[component] +pub fn HomePage() -> impl IntoView { + // Server-side resource to fetch chart data + let chart_data = Resource::new(|| (), |_| get_chart_data()); + + view! { + <div class="container mx-auto py-8 px-4"> + <h1 class="text-3xl font-bold text-gray-900 mb-8"> + "Benchmark Results" + </h1> + + <Suspense fallback=move || view! { + <div class="animate-pulse bg-gray-200 rounded-lg h-96 w-full"></div> + }> + {move || Suspend::new(async move { + match chart_data.await { + Ok(data) => view! { <Chart data=data /> }.into_any(), + Err(e) => view! { + <div class="text-red-600 p-4 bg-red-50 rounded-lg"> + {format!("Error loading data: {}", e)} + </div> + }.into_any(), + } + })} + </Suspense> + </div> + } +} + +/// Server function to fetch chart data from DuckDB. +#[server(GetChartData)] +async fn get_chart_data() -> Result<ChartData, ServerFnError> { + use crate::db::DbPool; + use crate::db::get_random_access_data; + + let db = expect_context::<DbPool>(); + get_random_access_data(&db, 50).map_err(|e| ServerFnError::new(e.to_string())) +} diff --git a/bench-website/src/components/mod.rs b/bench-website/src/components/mod.rs new file mode 100644 index 00000000000..191e997caf3 --- /dev/null +++ b/bench-website/src/components/mod.rs @@ -0,0 +1,7 @@ +mod app; +mod chart; +mod home; + +pub use app::App; +pub use chart::Chart; +pub use home::HomePage; diff --git a/bench-website/src/db/connection.rs b/bench-website/src/db/connection.rs new file mode 100644 index 00000000000..748d8532239 --- /dev/null +++ b/bench-website/src/db/connection.rs @@ -0,0 +1,111 @@ +// Allow std::sync::Mutex for this standalone prototype (no parking_lot dependency) +#![allow(clippy::disallowed_types)] + +//! DuckDB connection management. +//! +//! While DuckDB supports concurrent operations at the database level, the Rust +//! `Connection` type uses `RefCell` internally (for statement caching) which +//! is not `Sync`. To share the connection across Leptos async contexts, we +//! wrap it in a `Mutex`. +//! +//! For this prototype with light load, this is sufficient. For production, +//! consider using a connection pool or multiple connections. + +use std::sync::Arc; +use std::sync::Mutex; + +use duckdb::Connection; +use duckdb::Result as DuckResult; + +/// Shared DuckDB connection for Leptos server functions. +/// +/// Uses `Arc<Mutex<Connection>>` because: +/// - `Arc` allows cheap cloning for sharing across contexts +/// - `Mutex` is required because `Connection` contains `RefCell` (not `Sync`) +/// +/// # Example +/// +/// ```ignore +/// let db = DbPool::new_with_mock_data()?; +/// let data = db.query(|conn| { +/// // Use conn to execute queries +/// })?; +/// ``` +#[derive(Clone)] +pub struct DbPool { + conn: Arc<Mutex<Connection>>, +} + +impl DbPool { + /// Creates a new in-memory DuckDB database with the benchmark schema. + /// + /// The schema includes: + /// - `commits` table for git commit metadata + /// - `random_access` table for benchmark results + pub fn new_in_memory() -> DuckResult<Self> { + let conn = Connection::open_in_memory()?; + Self::init_schema(&conn)?; + Ok(Self { + conn: Arc::new(Mutex::new(conn)), + }) + } + + /// Creates a new database populated with mock benchmark data. + /// + /// Useful for development and testing. Generates 100 commits over 100 days + /// with realistic benchmark patterns including gradual improvements and + /// occasional regressions. + pub fn new_with_mock_data() -> DuckResult<Self> { + let pool = Self::new_in_memory()?; + crate::mock_data::populate_mock_data(&pool)?; + Ok(pool) + } + + /// Initializes the database schema. + fn init_schema(conn: &Connection) -> DuckResult<()> { + conn.execute_batch( + r#" + CREATE TABLE IF NOT EXISTS commits ( + commit_hash VARCHAR PRIMARY KEY, + timestamp TIMESTAMP NOT NULL, + message VARCHAR, + author VARCHAR + ); + + CREATE INDEX IF NOT EXISTS idx_commits_timestamp + ON commits(timestamp DESC); + + CREATE TABLE IF NOT EXISTS random_access ( + commit_hash VARCHAR NOT NULL REFERENCES commits(commit_hash), + vortex_ns UBIGINT, + parquet_ns UBIGINT, + lance_ns UBIGINT, + PRIMARY KEY (commit_hash) + ); + "#, + )?; + Ok(()) + } + + /// Executes a read query against the database. + /// + /// Acquires the mutex lock for the duration of the closure. + pub fn query<T, F>(&self, f: F) -> DuckResult<T> + where + F: FnOnce(&Connection) -> DuckResult<T>, + { + let conn = self.conn.lock().expect("connection lock poisoned"); + f(&conn) + } + + /// Executes a write operation against the database. + /// + /// Acquires the mutex lock for the duration of the closure. + pub fn execute<F>(&self, f: F) -> DuckResult<()> + where + F: FnOnce(&Connection) -> DuckResult<()>, + { + let conn = self.conn.lock().expect("connection lock poisoned"); + f(&conn) + } +} diff --git a/bench-website/src/db/mod.rs b/bench-website/src/db/mod.rs new file mode 100644 index 00000000000..76bd0845c33 --- /dev/null +++ b/bench-website/src/db/mod.rs @@ -0,0 +1,14 @@ +//! Database layer for benchmark data storage and retrieval. +//! +//! This module provides: +//! - [`DbPool`]: Thread-safe DuckDB connection wrapper +//! - Data models ([`ChartData`], [`Series`], [`CommitInfo`], [`DataPoint`]) +//! - Query functions for fetching benchmark results + +mod connection; +mod models; +mod queries; + +pub use connection::DbPool; +pub use models::*; +pub use queries::*; diff --git a/bench-website/src/db/models.rs b/bench-website/src/db/models.rs new file mode 100644 index 00000000000..926f4a19cae --- /dev/null +++ b/bench-website/src/db/models.rs @@ -0,0 +1,59 @@ +use chrono::DateTime; +use chrono::Utc; +use serde::Deserialize; +use serde::Serialize; + +/// Git commit metadata. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CommitInfo { + pub hash: String, + pub timestamp: DateTime<Utc>, + pub message: Option<String>, + pub author: Option<String>, +} + +/// A single data point on a chart. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct DataPoint { + pub commit_idx: usize, + pub value_ns: u64, +} + +impl DataPoint { + pub fn value_ms(&self) -> f64 { + self.value_ns as f64 / 1_000_000.0 + } +} + +/// A series of data points for one benchmark target. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Series { + pub name: String, + pub display_name: String, + pub color: String, + pub points: Vec<DataPoint>, +} + +/// Complete chart data for rendering a benchmark chart. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ChartData { + pub title: String, + pub commits: Vec<CommitInfo>, + pub series: Vec<Series>, + pub unit: String, +} + +impl ChartData { + /// Find min/max Y values across all series (in ms). + pub fn y_range(&self) -> (f64, f64) { + let values: Vec<f64> = self + .series + .iter() + .flat_map(|s| s.points.iter().map(|p| p.value_ms())) + .collect(); + + let min = values.iter().copied().fold(f64::INFINITY, f64::min); + let max = values.iter().copied().fold(0.0, f64::max); + (min.min(0.0), max * 1.1) // Start at 0, add 10% headroom + } +} diff --git a/bench-website/src/db/queries.rs b/bench-website/src/db/queries.rs new file mode 100644 index 00000000000..99724ca7802 --- /dev/null +++ b/bench-website/src/db/queries.rs @@ -0,0 +1,143 @@ +// Allow std::collections::HashMap for this standalone prototype +#![allow(clippy::disallowed_types)] + +//! Query functions for fetching benchmark data from DuckDB. +//! +//! # Timestamp Handling +//! +//! DuckDB's timestamp type doesn't map directly to chrono types via the Rust +//! bindings. We work around this by casting timestamps to VARCHAR in SQL and +//! parsing them back to `DateTime<Utc>` in Rust. + +use std::collections::HashMap; + +use chrono::DateTime; +use chrono::NaiveDateTime; +use chrono::Utc; +use duckdb::Result as DuckResult; + +use super::DbPool; +use super::models::ChartData; +use super::models::CommitInfo; +use super::models::DataPoint; +use super::models::Series; + +/// Fetches random_access benchmark data for the most recent N commits. +/// +/// Returns a [`ChartData`] struct ready for rendering, with commits ordered +/// oldest-first (for left-to-right chart display) and series data for +/// Vortex, Parquet, and Lance. +pub fn get_random_access_data(db: &DbPool, limit: usize) -> DuckResult<ChartData> { + db.query(|conn| { + // First get commits (ordered newest first, then reversed for display) + // Cast timestamp to string for portable reading + let mut commits_stmt = conn.prepare( + r#" + SELECT commit_hash, CAST(timestamp AS VARCHAR), message, author + FROM commits + ORDER BY timestamp DESC + LIMIT ? + "#, + )?; + + let commits: Vec<CommitInfo> = commits_stmt + .query_map([limit], |row| { + Ok(CommitInfo { + hash: row.get(0)?, + timestamp: { + let ts_str: String = row.get(1)?; + // Parse the timestamp string + NaiveDateTime::parse_from_str(&ts_str, "%Y-%m-%d %H:%M:%S") + .map(|ts| DateTime::from_naive_utc_and_offset(ts, Utc)) + .unwrap_or_else(|_| Utc::now()) + }, + message: row.get(2)?, + author: row.get(3)?, + }) + })? + .collect::<Result<Vec<_>, _>>()?; + + // Reverse to get oldest-first for charting + let commits: Vec<_> = commits.into_iter().rev().collect(); + + // Create commit hash -> index map + let hash_to_idx: HashMap<String, usize> = commits + .iter() + .enumerate() + .map(|(i, c)| (c.hash.clone(), i)) + .collect(); + + // Fetch benchmark data + let mut bench_stmt = conn.prepare( + r#" + SELECT r.commit_hash, r.vortex_ns, r.parquet_ns, r.lance_ns + FROM random_access r + JOIN commits c ON r.commit_hash = c.commit_hash + ORDER BY c.timestamp DESC + LIMIT ? + "#, + )?; + + let mut vortex_points = Vec::new(); + let mut parquet_points = Vec::new(); + let mut lance_points = Vec::new(); + + let rows = bench_stmt.query_map([limit], |row| { + let hash: String = row.get(0)?; + let vortex: Option<u64> = row.get(1)?; + let parquet: Option<u64> = row.get(2)?; + let lance: Option<u64> = row.get(3)?; + Ok((hash, vortex, parquet, lance)) + })?; + + for row in rows { + let (hash, vortex, parquet, lance) = row?; + if let Some(&idx) = hash_to_idx.get(&hash) { + if let Some(v) = vortex { + vortex_points.push(DataPoint { + commit_idx: idx, + value_ns: v, + }); + } + if let Some(v) = parquet { + parquet_points.push(DataPoint { + commit_idx: idx, + value_ns: v, + }); + } + if let Some(v) = lance { + lance_points.push(DataPoint { + commit_idx: idx, + value_ns: v, + }); + } + } + } + + Ok(ChartData { + title: "Random Access".to_string(), + commits, + series: vec![ + Series { + name: "vortex".to_string(), + display_name: "Vortex".to_string(), + color: "#19a508".to_string(), + points: vortex_points, + }, + Series { + name: "parquet".to_string(), + display_name: "Parquet".to_string(), + color: "#ef7f1d".to_string(), + points: parquet_points, + }, + Series { + name: "lance".to_string(), + display_name: "Lance".to_string(), + color: "#2D936C".to_string(), + points: lance_points, + }, + ], + unit: "ms".to_string(), + }) + }) +} diff --git a/bench-website/src/lib.rs b/bench-website/src/lib.rs new file mode 100644 index 00000000000..cac53982ff9 --- /dev/null +++ b/bench-website/src/lib.rs @@ -0,0 +1,12 @@ +pub mod components; +pub mod db; +pub mod mock_data; + +pub use components::App; + +#[cfg(feature = "hydrate")] +#[wasm_bindgen::prelude::wasm_bindgen] +pub fn hydrate() { + console_error_panic_hook::set_once(); + leptos::mount::hydrate_body(App); +} diff --git a/bench-website/src/main.rs b/bench-website/src/main.rs new file mode 100644 index 00000000000..b2f5893c487 --- /dev/null +++ b/bench-website/src/main.rs @@ -0,0 +1,154 @@ +#[cfg(feature = "ssr")] +#[tokio::main] +async fn main() { + use std::net::SocketAddr; + + use axum::Router; + use bench_website::App; + use bench_website::db::DbPool; + use leptos::config::LeptosOptions; + use leptos::prelude::*; + use leptos_axum::LeptosRoutes; + use leptos_axum::generate_route_list; + use tokio::net::TcpListener; + + tracing_subscriber::fmt::init(); + + // Initialize database with mock data + let db = DbPool::new_with_mock_data().expect("Failed to initialize database"); + + // Build Leptos configuration manually (works from any directory) + let addr: SocketAddr = "127.0.0.1:3000".parse().unwrap(); + let leptos_options = LeptosOptions::builder() + .output_name("bench-website") + .site_root("target/site") + .site_pkg_dir("pkg") + .site_addr(addr) + .reload_port(3001) + .build(); + let routes = generate_route_list(App); + + // Build router with Leptos routes + let app = Router::new() + .leptos_routes_with_context( + &leptos_options, + routes, + { + let db = db.clone(); + move || provide_context(db.clone()) + }, + { + let leptos_options = leptos_options.clone(); + move || shell(leptos_options.clone()) + }, + ) + .with_state(leptos_options); + + let listener = TcpListener::bind(&addr).await.unwrap(); + tracing::info!("Listening on http://{}", addr); + axum::serve(listener, app.into_make_service()) + .await + .unwrap(); +} + +#[cfg(feature = "ssr")] +fn shell(options: leptos::config::LeptosOptions) -> impl leptos::prelude::IntoView { + use bench_website::App; + use leptos::prelude::*; + use leptos_meta::MetaTags; + + view! { + <!DOCTYPE html> + <html lang="en"> + <head> + <meta charset="utf-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1"/> + <AutoReload options=options.clone()/> + <HydrationScripts options/> + <MetaTags/> + <style>{INLINE_CSS}</style> + </head> + <body> + <App/> + </body> + </html> + } +} + +/// Minimal Tailwind-style utility classes for Phase 1 prototype. +/// Phase 2 will use proper Tailwind CSS with PostCSS. +#[cfg(feature = "ssr")] +const INLINE_CSS: &str = r#" +/* === Reset & Base === */ +* { box-sizing: border-box; margin: 0; padding: 0; } +body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.5; color: #1f2937; } +a { text-decoration: none; color: inherit; } +a:hover { opacity: 0.8; } + +/* === Layout === */ +.min-h-screen { min-height: 100vh; } +.container { width: 100%; max-width: 1280px; margin: 0 auto; } +.mx-auto { margin-left: auto; margin-right: auto; } +.flex { display: flex; } +.items-center { align-items: center; } +.justify-between { justify-content: space-between; } +.justify-center { justify-content: center; } + +/* === Spacing === */ +.gap-2 { gap: 0.5rem; } +.gap-6 { gap: 1.5rem; } +.p-4 { padding: 1rem; } +.px-4 { padding-left: 1rem; padding-right: 1rem; } +.py-8 { padding-top: 2rem; padding-bottom: 2rem; } +.mb-4 { margin-bottom: 1rem; } +.mb-6 { margin-bottom: 1.5rem; } +.mb-8 { margin-bottom: 2rem; } +.mt-4 { margin-top: 1rem; } + +/* === Sizing === */ +.w-4 { width: 1rem; } +.w-full { width: 100%; } +.h-3 { height: 0.75rem; } +.h-96 { height: 24rem; } + +/* === Typography === */ +.text-sm { font-size: 0.875rem; } +.text-lg { font-size: 1.125rem; } +.text-xl { font-size: 1.25rem; } +.text-3xl { font-size: 1.875rem; } +.font-bold { font-weight: 700; } +.font-semibold { font-weight: 600; } + +/* === Colors === */ +.bg-white { background-color: #ffffff; } +.bg-gray-100 { background-color: #f3f4f6; } +.bg-gray-200 { background-color: #e5e7eb; } +.bg-gray-900 { background-color: #111827; } +.bg-red-50 { background-color: #fef2f2; } +.text-white { color: #ffffff; } +.text-gray-300 { color: #d1d5db; } +.text-gray-700 { color: #374151; } +.text-gray-800 { color: #1f2937; } +.text-gray-900 { color: #111827; } +.text-red-600 { color: #dc2626; } + +/* === Borders & Shadows === */ +.rounded-sm { border-radius: 0.125rem; } +.rounded-lg { border-radius: 0.5rem; } +.shadow-md { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); } +.shadow-lg { box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); } + +/* === Transitions & Animations === */ +.transition-colors { transition: color 0.15s ease-in-out; } +@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } +.animate-pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } + +/* === Chart-specific === */ +.chart-container { max-width: 100%; overflow-x: auto; } +.chart-svg { display: block; max-width: 100%; height: auto; } +"#; + +#[cfg(not(feature = "ssr"))] +fn main() { + // Client-side only - hydration is handled by lib.rs +} diff --git a/bench-website/src/mock_data.rs b/bench-website/src/mock_data.rs new file mode 100644 index 00000000000..4e1f6b21b33 --- /dev/null +++ b/bench-website/src/mock_data.rs @@ -0,0 +1,89 @@ +//! Mock data generation for development and testing. + +use chrono::Duration; +use chrono::Utc; +use duckdb::Result as DuckResult; +use rand::Rng; + +use crate::db::DbPool; + +/// Populates the database with realistic mock benchmark data. +/// +/// Generates 100 commits over 100 days with benchmark results that simulate: +/// - Random variation (+/- 10%) in measurements +/// - Gradual Vortex performance improvements (2% every 10 commits) +/// - A regression at commit 50 that gets fixed at commit 55 +/// - ~20% of commits missing benchmark data (simulating CI gaps) +/// +/// Base performance values: +/// - Vortex: 50ms (improving over time) +/// - Parquet: 80ms (constant) +/// - Lance: 65ms (constant) +pub fn populate_mock_data(db: &DbPool) -> DuckResult<()> { + let num_commits = 100; + let mut rng = rand::rng(); + + // Generate commits starting from 100 days ago + let now = Utc::now(); + let start_time = now - Duration::days(100); + + db.execute(|conn| { + // Insert commits + let mut stmt = conn.prepare( + "INSERT INTO commits (commit_hash, timestamp, message, author) VALUES (?, ?, ?, ?)", + )?; + + for i in 0..num_commits { + let timestamp = start_time + Duration::hours((i as i64) * 24); + let hash = format!("{:040x}", i * 12345 + 67890); + let message = format!("commit message #{}", i); + let author = "developer@example.com"; + // Format timestamp as ISO 8601 string for DuckDB + let timestamp_str = timestamp.format("%Y-%m-%d %H:%M:%S").to_string(); + + stmt.execute(duckdb::params![hash, timestamp_str, message, author])?; + } + + // Insert random_access benchmark data + // Not all commits have benchmark data (simulate CI running on ~80% of commits) + let mut bench_stmt = conn.prepare( + "INSERT INTO random_access (commit_hash, vortex_ns, parquet_ns, lance_ns) VALUES (?, ?, ?, ?)", + )?; + + // Base performance values (in nanoseconds) + let mut vortex_base: f64 = 50_000_000.0; // 50ms + let parquet_base: f64 = 80_000_000.0; // 80ms + let lance_base: f64 = 65_000_000.0; // 65ms + + for i in 0..num_commits { + // Skip some commits (20% have no benchmark data) + if rng.random_ratio(2, 10) { + continue; + } + + let hash = format!("{:040x}", i * 12345 + 67890); + + // Add realistic variation (+/- 10%) + let vortex_ns = (vortex_base * (1.0 + rng.random_range(-0.1..0.1))) as u64; + let parquet_ns = (parquet_base * (1.0 + rng.random_range(-0.1..0.1))) as u64; + let lance_ns = (lance_base * (1.0 + rng.random_range(-0.1..0.1))) as u64; + + bench_stmt.execute(duckdb::params![hash, vortex_ns, parquet_ns, lance_ns])?; + + // Simulate performance trends over time + // Vortex gradually improves + if i % 10 == 0 { + vortex_base *= 0.98; // 2% improvement every 10 commits + } + // Occasional regression + if i == 50 { + vortex_base *= 1.15; // 15% regression at commit 50 + } + if i == 55 { + vortex_base *= 0.87; // Fixed at commit 55 + } + } + + Ok(()) + }) +} diff --git a/bench-website/style/main.css b/bench-website/style/main.css new file mode 100644 index 00000000000..8cf12c77a29 --- /dev/null +++ b/bench-website/style/main.css @@ -0,0 +1,105 @@ +/* Basic styles for Phase 1 - replace with TailwindCSS later */ + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + line-height: 1.5; + color: #1f2937; +} + +/* Layout utilities */ +.min-h-screen { min-height: 100vh; } +.container { width: 100%; max-width: 1280px; margin: 0 auto; } +.mx-auto { margin-left: auto; margin-right: auto; } + +/* Flexbox */ +.flex { display: flex; } +.items-center { align-items: center; } +.justify-between { justify-content: space-between; } +.justify-center { justify-content: center; } +.gap-2 { gap: 0.5rem; } +.gap-4 { gap: 1rem; } +.gap-6 { gap: 1.5rem; } + +/* Spacing */ +.p-4 { padding: 1rem; } +.px-4 { padding-left: 1rem; padding-right: 1rem; } +.py-8 { padding-top: 2rem; padding-bottom: 2rem; } +.mb-4 { margin-bottom: 1rem; } +.mb-6 { margin-bottom: 1.5rem; } +.mb-8 { margin-bottom: 2rem; } +.mt-4 { margin-top: 1rem; } + +/* Typography */ +.text-xs { font-size: 0.75rem; } +.text-sm { font-size: 0.875rem; } +.text-lg { font-size: 1.125rem; } +.text-xl { font-size: 1.25rem; } +.text-2xl { font-size: 1.5rem; } +.text-3xl { font-size: 1.875rem; } +.font-bold { font-weight: 700; } +.font-semibold { font-weight: 600; } +.text-center { text-align: center; } + +/* Colors */ +.bg-white { background-color: #ffffff; } +.bg-gray-100 { background-color: #f3f4f6; } +.bg-gray-200 { background-color: #e5e7eb; } +.bg-gray-900 { background-color: #111827; } +.bg-red-50 { background-color: #fef2f2; } +.text-white { color: #ffffff; } +.text-gray-300 { color: #d1d5db; } +.text-gray-500 { color: #6b7280; } +.text-gray-600 { color: #4b5563; } +.text-gray-700 { color: #374151; } +.text-gray-800 { color: #1f2937; } +.text-gray-900 { color: #111827; } +.text-red-600 { color: #dc2626; } +.fill-gray-500 { fill: #6b7280; } +.fill-gray-600 { fill: #4b5563; } + +/* Sizing */ +.w-4 { width: 1rem; } +.h-3 { height: 0.75rem; } +.h-96 { height: 24rem; } +.w-full { width: 100%; } + +/* Borders */ +.rounded { border-radius: 0.25rem; } +.rounded-sm { border-radius: 0.125rem; } +.rounded-lg { border-radius: 0.5rem; } + +/* Effects */ +.shadow-md { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); } +.shadow-lg { box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); } +.transition-colors { transition: color 0.15s ease-in-out; } + +/* Animations */ +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} +.animate-pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } + +/* Links */ +a { text-decoration: none; color: inherit; } +a:hover { opacity: 0.8; } +.hover\:text-white:hover { color: #ffffff; } +.hover\:text-gray-300:hover { color: #d1d5db; } + +/* Chart container */ +.chart-container { + max-width: 100%; + overflow-x: auto; +} + +.chart-svg { + display: block; + max-width: 100%; + height: auto; +} From 2ade3016c13ea2c5a337f6c1f98e8a9608d7dc78 Mon Sep 17 00:00:00 2001 From: Connor Tsui <connor.tsui20@gmail.com> Date: Sat, 24 Jan 2026 16:21:50 -0500 Subject: [PATCH 4/4] remove vortex dogfooding There's really no point to this and it also complicates concurrency control. Really what we want is an Iceberg or Spiral table! Signed-off-by: Connor Tsui <connor.tsui20@gmail.com> --- BENCHMARK_WEBSITE_REDESIGN.md | 81 +++++++++++------------------------ 1 file changed, 26 insertions(+), 55 deletions(-) diff --git a/BENCHMARK_WEBSITE_REDESIGN.md b/BENCHMARK_WEBSITE_REDESIGN.md index 3b3eb4b220b..8e22d87fd88 100644 --- a/BENCHMARK_WEBSITE_REDESIGN.md +++ b/BENCHMARK_WEBSITE_REDESIGN.md @@ -36,7 +36,6 @@ A benchmarks visualization website (https://bench.vortex.dev/) that displays per ### Secondary Goals -- Dogfooding: Use Vortex file format for storing benchmark data via DuckDB extension - Reusability: Architected as a library that others can adapt for their benchmarking needs - SEO optimization: Nice to have, but speed is paramount - Mobile-first design: Desktop is primary use case, but should work on mobile too @@ -100,21 +99,21 @@ GitHub Actions (CI) -> JSON files -> S3 bucket -> Client downloads entire datase │ │ │ │ │ │ ┌───────────────────────────────┐ │ │ │ │ │ DuckDB (embedded) │ │ │ - │ │ │ + Vortex Extension │ │ │ │ │ │ │ │ │ - │ │ │ commits.vortex │ │ │ - │ │ │ compression.vortex │ │ │ - │ │ │ tpch_sf1.vortex │ │ │ - │ │ │ tpch_sf10.vortex │ │ │ - │ │ │ clickbench.vortex │ │ │ - │ │ │ random_access.vortex │ │ │ + │ │ │ benchmarks.duckdb │ │ │ + │ │ │ - commits table │ │ │ + │ │ │ - compression table │ │ │ + │ │ │ - tpch_sf1 table │ │ │ + │ │ │ - clickbench table │ │ │ + │ │ │ - random_access table │ │ │ + │ │ │ - etc │ │ │ │ │ └───────────────────────────────┘ │ │ │ └─────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────┐ │ │ │ S3 Bucket │ │ - │ │ - Vortex file backups (hourly) │ │ + │ │ - DuckDB file backups (hourly) │ │ │ │ - Disaster recovery │ │ │ └─────────────────────────────────────┘ │ └─────────────────────────────────────────────┘ @@ -122,9 +121,9 @@ GitHub Actions (CI) -> JSON files -> S3 bucket -> Client downloads entire datase ## Key Architectural Decisions -### Decision 1: Server-Side DuckDB (Optionally with Vortex Extension) +### Decision 1: Server-Side DuckDB -**Choice**: Embedded DuckDB on server, optionally using Vortex extension for storage. +**Choice**: Embedded DuckDB on server with native storage. **Alternatives Considered**: @@ -135,7 +134,7 @@ GitHub Actions (CI) -> JSON files -> S3 bucket -> Client downloads entire datase **Rationale**: - DuckDB is extremely fast for analytical queries (~5ms for typical chart query) -- Vortex extension lets us dogfood our format, but plain DuckDB works too +- Single-file database is simple to deploy, backup, and manage - Schema evolution is trivial (`ALTER TABLE ADD COLUMN`) - Keep in mind future library extraction: storage should be swappable @@ -176,7 +175,7 @@ GitHub Actions (CI) -> JSON files -> S3 bucket -> Client downloads entire datase ### Decision 4: No Materialized Views -**Choice**: Direct queries against Vortex tables with indexes +**Choice**: Direct queries against DuckDB tables with indexes **Alternatives Considered**: @@ -196,7 +195,7 @@ GitHub Actions (CI) -> JSON files -> S3 bucket -> Client downloads entire datase ### Overview -Each benchmark group is stored as a separate table (or Vortex file when using the Vortex extension). All tables share a common `commits` table for ordering. +Each benchmark group is stored as a separate table. All tables share a common `commits` table for ordering. **Important**: All benchmark measurements are stored as **unsigned 64-bit integers** (typically nanoseconds). Conversion to human-readable units (seconds, milliseconds, MB/s) happens at display time. This preserves precision and simplifies storage. @@ -376,7 +375,7 @@ pub enum SummaryType { ### Technology Stack - **Framework**: Leptos 0.8+ with Axum backend -- **Database**: DuckDB (embedded), optionally with Vortex extension +- **Database**: DuckDB (embedded) - **Runtime**: Tokio async runtime ### Project Structure @@ -408,11 +407,8 @@ bench-website/ │ └── groups.rs # Benchmark group definitions ├── style/ │ └── main.css -└── data/ # Vortex files (gitignored, populated at runtime) - ├── commits.vortex - ├── compression.vortex - ├── tpch_sf1.vortex - └── ... +└── data/ # DuckDB database (gitignored, populated at runtime) + └── benchmarks.duckdb ``` ### DuckDB Connection Management @@ -435,11 +431,6 @@ impl DbPool { pub fn new(config: &StorageConfig) -> Result<Self> { let conn = Connection::open(&config.database_path)?; - // Optionally load Vortex extension if configured - if config.use_vortex_extension { - conn.execute("INSTALL vortex FROM 'path/to/extension'; LOAD vortex;", [])?; - } - // Create tables if they don't exist Self::init_schema(&conn)?; @@ -474,8 +465,6 @@ impl DbPool { /// Configuration for storage backend (supports library reuse) pub struct StorageConfig { pub database_path: String, // ":memory:" or file path - pub use_vortex_extension: bool, // false for standard DuckDB - pub vortex_extension_path: Option<String>, } ``` @@ -1244,7 +1233,7 @@ Response: 200 OK │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ EFS (Elastic File System) │ │ -│ │ - Stores Vortex files │ │ +│ │ - Stores DuckDB database file │ │ │ │ - Persists across container restarts │ │ │ │ - Single-AZ (cost savings, acceptable for this use case) │ │ │ └─────────────────────────────────────────────────────────────────┘ │ @@ -1252,7 +1241,7 @@ Response: 200 OK │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ S3 │ │ │ │ Bucket: vortex-benchmarks-backup │ │ -│ │ - Hourly backups of Vortex files │ │ +│ │ - Hourly backups of DuckDB database │ │ │ │ - Lifecycle: Delete after 30 days │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ @@ -1274,7 +1263,7 @@ This is simpler and cheaper (~$30/month) but less resilient. Acceptable for inte ```bash # Required -BENCH_DATA_DIR=/data # Path to Vortex files +BENCH_DATA_DIR=/data # Path to data directory BENCH_CI_TOKEN=<secret> # Token for CI authentication BENCH_S3_BUCKET=vortex-benchmarks-backup @@ -1348,9 +1337,6 @@ COPY --from=builder /app/target/release/bench-website . # Copy client WASM and assets COPY --from=builder /app/target/site ./site -# Copy Vortex extension (pre-built) -COPY --from=builder /app/vortex_duckdb.so /usr/local/lib/ - ENV BENCH_DATA_DIR=/data ENV BENCH_PORT=3000 @@ -1363,21 +1349,21 @@ CMD ["./bench-website"] ## Plan of Attack -This is a **prototype-first, library-oriented** implementation plan. The goal is to build a reusable benchmark visualization library that can be plugged into any data source, with Vortex-specific integration handled separately. +This is a **prototype-first, library-oriented** implementation plan. The goal is to build a reusable benchmark visualization library that can be plugged into any data source. ### Key Design Decisions - **Plotters** for charting (Rust-native, Canvas rendering) -- **Plain DuckDB for prototyping** - Add Vortex extension once core functionality works +- **DuckDB for storage** - Simple embedded database with fast analytical queries - **Mock data for development** - Real data pipeline is separate; design for pluggability -- **Library-first mindset** - Keep Vortex-specific details separate from core visualization +- **Library-first mindset** - Keep benchmark-specific details separate from core visualization ### Key Simplifications 1. **Get a working prototype ASAP** - Mock/hardcode where possible 2. **Start with 1-2 benchmark groups** - Not all schemas upfront 3. **CI integration comes last** - Once everything works locally -4. **Design for pluggability** - Core lib shouldn't depend on Vortex specifics +4. **Design for pluggability** - Core lib shouldn't depend on specific data sources --- @@ -1398,7 +1384,7 @@ This is a **prototype-first, library-oriented** implementation plan. The goal is - `commits` (commit_hash, timestamp, message, author) - `random_access` (simple single-chart group with ~3 series) - Generate ~100 mock commits with realistic patterns (some regressions, improvements) -- Store as local DuckDB file (plain DuckDB for now, Vortex extension added after Phase 3) +- Store as local DuckDB file 1.3. **Single chart page** - Hardcoded route `/` that queries DuckDB @@ -1458,12 +1444,7 @@ This is a **prototype-first, library-oriented** implementation plan. The goal is - Responsive design basics - URL routing for all groups -3.4. **Add Vortex extension** -- Load Vortex DuckDB extension -- Convert DuckDB storage to Vortex files -- Verify queries work correctly with Vortex tables - -**Deliverable**: Clean library interface with Vortex storage, easy to add new benchmark groups +**Deliverable**: Clean library interface, easy to add new benchmark groups --- @@ -1538,8 +1519,6 @@ This is a **prototype-first, library-oriented** implementation plan. The goal is ### What's Deferred Until Later Phases -- Vortex DuckDB extension (plain DuckDB for prototyping, add Vortex after Phase 3) -- Vortex-specific CI integration (user will plug in their pipeline) - Real data import scripts (user has separate pipeline) ### What's Explicitly Out of Scope @@ -1605,14 +1584,6 @@ If plotters proves insufficient: - **ECharts via charming**: Full-featured, but large bundle (~1MB) - **D3 via wasm-bindgen**: Maximum flexibility, moderate complexity -### Vortex-Specific Optimizations - -Since we're dogfooding Vortex: - -- **Compression**: Ensure Vortex files use appropriate encoding for benchmark data (u64 integers and strings) -- **Predicate pushdown**: DuckDB + Vortex should push down filters efficiently; verify with EXPLAIN -- **Column pruning**: Queries should only read needed columns; verify Vortex extension does this - --- ## Appendix: Quick Reference @@ -1650,7 +1621,7 @@ curl -X POST http://localhost:3000/api/ingest \ | `src/api/charts.rs` | Server functions for chart data | | `src/components/chart.rs` | Interactive chart island | | `src/config/groups.rs` | Benchmark group definitions | -| `data/*.vortex` | Vortex data files (gitignored) | +| `data/benchmarks.duckdb` | DuckDB database (gitignored) | ### Data Sizes (Estimates)