diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 693ddbaa00..a6e513ca02 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -19,7 +19,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 components: clippy - name: Cache cargo uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 77309ba511..8792c29c1b 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -17,7 +17,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 components: rustfmt - name: Check formatting run: cargo fmt --all -- --check diff --git a/.github/workflows/pack-ci.yml b/.github/workflows/pack-ci.yml index 6dd6edb402..91e02357a2 100644 --- a/.github/workflows/pack-ci.yml +++ b/.github/workflows/pack-ci.yml @@ -29,8 +29,8 @@ jobs: docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian test: | apt-get update && - rustup install nightly-2026-01-04 && - rustup default nightly-2026-01-04 && + rustup install nightly-2026-02-05 && + rustup default nightly-2026-02-05 && rustup target add x86_64-unknown-linux-gnu && cargo test -p pack-tests --target x86_64-unknown-linux-gnu - host: ubuntu-latest @@ -43,8 +43,8 @@ jobs: echo "https://dl-cdn.alpinelinux.org/alpine/v3.21/community" >> /etc/apk/repositories && apk update && apk add --no-cache libc6-compat pkgconfig dav1d libdav1d dav1d-dev clang-static llvm-dev && - rustup install nightly-2026-01-04 && - rustup default nightly-2026-01-04 && + rustup install nightly-2026-02-05 && + rustup default nightly-2026-02-05 && rustup target add x86_64-unknown-linux-musl && export RUSTFLAGS='--cfg tokio_unstable -Zshare-generics=y -Zthreads=8 -Csymbol-mangling-version=v0 -Ctarget-feature=-crt-static' && cargo test -p pack-tests --target x86_64-unknown-linux-musl @@ -53,8 +53,8 @@ jobs: docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 test: | apt-get update && apt-get install -y libc6-dev-arm64-cross libgcc-s1-arm64-cross && - rustup install nightly-2026-01-04 && - rustup default nightly-2026-01-04 && + rustup install nightly-2026-02-05 && + rustup default nightly-2026-02-05 && rustup target add aarch64-unknown-linux-gnu && export QEMU_LD_PREFIX=/usr/aarch64-linux-gnu && cargo test -p pack-tests --target aarch64-unknown-linux-gnu @@ -68,8 +68,8 @@ jobs: echo "https://dl-cdn.alpinelinux.org/alpine/v3.21/community" >> /etc/apk/repositories && apk update && apk add --no-cache libc6-compat pkgconfig dav1d libdav1d dav1d-dev clang-static llvm-dev && - rustup install nightly-2026-01-04 && - rustup default nightly-2026-01-04 && + rustup install nightly-2026-02-05 && + rustup default nightly-2026-02-05 && rustup target add aarch64-unknown-linux-musl && export RUSTFLAGS='--cfg tokio_unstable -Zshare-generics=y -Zthreads=8 -Csymbol-mangling-version=v0' && cargo test -p pack-tests --target aarch64-unknown-linux-musl @@ -115,7 +115,7 @@ jobs: uses: dtolnay/rust-toolchain@stable if: ${{ !matrix.settings.docker }} with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 targets: ${{ matrix.settings.target }} - name: Cache cargo uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/pack-integration.yml b/.github/workflows/pack-integration.yml index e89404a861..52ea414d4c 100644 --- a/.github/workflows/pack-integration.yml +++ b/.github/workflows/pack-integration.yml @@ -47,7 +47,7 @@ jobs: - name: Setup Rust uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 components: rustfmt, clippy - name: Rust Cache diff --git a/.github/workflows/pack-perf.yml b/.github/workflows/pack-perf.yml index 7775d691a3..532cad5ec5 100644 --- a/.github/workflows/pack-perf.yml +++ b/.github/workflows/pack-perf.yml @@ -42,7 +42,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 components: rustfmt, clippy - name: Cache cargo diff --git a/.github/workflows/pack-release.yml b/.github/workflows/pack-release.yml index 65850d87e1..48845b0539 100644 --- a/.github/workflows/pack-release.yml +++ b/.github/workflows/pack-release.yml @@ -23,8 +23,8 @@ jobs: docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 build: | apt-get update && apt-get install -y libc6-dev-arm64-cross libgcc-s1-arm64-cross && - rustup install nightly-2026-01-04 && - rustup default nightly-2026-01-04 && + rustup install nightly-2026-02-05 && + rustup default nightly-2026-02-05 && rustup target add aarch64-unknown-linux-gnu && npm run build:binding --workspace=@utoo/pack -- --target aarch64-unknown-linux-gnu - host: ubuntu-latest @@ -38,8 +38,8 @@ jobs: echo "https://dl-cdn.alpinelinux.org/alpine/v3.21/community" >> /etc/apk/repositories && apk update && apk add --no-cache libc6-compat pkgconfig dav1d libdav1d dav1d-dev clang-static llvm-dev && - rustup install nightly-2026-01-04 && - rustup default nightly-2026-01-04 && + rustup install nightly-2026-02-05 && + rustup default nightly-2026-02-05 && rustup target add aarch64-unknown-linux-musl && export RUSTFLAGS='--cfg tokio_unstable -Zshare-generics=y -Zthreads=8 -Zunstable-options -Csymbol-mangling-version=v0 -Clinker-flavor=gnu-lld-cc -Clink-self-contained=+linker' && npm run build:binding --workspace=@utoo/pack -- --target aarch64-unknown-linux-musl @@ -48,8 +48,8 @@ jobs: docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian build: | apt-get update && - rustup install nightly-2026-01-04 && - rustup default nightly-2026-01-04 && + rustup install nightly-2026-02-05 && + rustup default nightly-2026-02-05 && rustup target add x86_64-unknown-linux-gnu && npm run build:binding --workspace=@utoo/pack -- --target x86_64-unknown-linux-gnu - host: ubuntu-latest @@ -63,8 +63,8 @@ jobs: echo "https://dl-cdn.alpinelinux.org/alpine/v3.21/community" >> /etc/apk/repositories && apk update && apk add --no-cache libc6-compat pkgconfig dav1d libdav1d dav1d-dev clang-static llvm-dev && - rustup install nightly-2026-01-04 && - rustup default nightly-2026-01-04 && + rustup install nightly-2026-02-05 && + rustup default nightly-2026-02-05 && rustup target add x86_64-unknown-linux-musl && export RUSTFLAGS='--cfg tokio_unstable -Zshare-generics=y -Zthreads=8 -Csymbol-mangling-version=v0' && npm run build:binding --workspace=@utoo/pack -- --target x86_64-unknown-linux-musl @@ -110,7 +110,7 @@ jobs: uses: dtolnay/rust-toolchain@stable if: ${{ !matrix.settings.docker }} with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 targets: ${{ matrix.settings.target }} - name: Cache cargo uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/pm-bench.yml b/.github/workflows/pm-bench.yml index c25e01d19c..c0b661ac31 100644 --- a/.github/workflows/pm-bench.yml +++ b/.github/workflows/pm-bench.yml @@ -40,7 +40,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 - name: Cache cargo uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/pm-ci.yml b/.github/workflows/pm-ci.yml index ed35e55003..93605b1c3b 100644 --- a/.github/workflows/pm-ci.yml +++ b/.github/workflows/pm-ci.yml @@ -82,7 +82,7 @@ jobs: uses: dtolnay/rust-toolchain@stable if: ${{ !matrix.settings.docker }} with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 targets: ${{ matrix.settings.target }} - name: Cache cargo diff --git a/.github/workflows/pm-e2e.yml b/.github/workflows/pm-e2e.yml index 7432e9e2da..fc01b425bd 100644 --- a/.github/workflows/pm-e2e.yml +++ b/.github/workflows/pm-e2e.yml @@ -60,7 +60,7 @@ jobs: - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 targets: ${{ matrix.settings.target }} - name: Cache cargo diff --git a/.github/workflows/pm-release.yml b/.github/workflows/pm-release.yml index 5c415a196b..0d7110d30f 100644 --- a/.github/workflows/pm-release.yml +++ b/.github/workflows/pm-release.yml @@ -94,7 +94,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 targets: ${{ matrix.target }} - name: Cache cargo diff --git a/.github/workflows/taplo.yml b/.github/workflows/taplo.yml index 06e0436190..cfa7d6af6f 100644 --- a/.github/workflows/taplo.yml +++ b/.github/workflows/taplo.yml @@ -19,7 +19,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 - name: Cache cargo uses: Swatinem/rust-cache@v2 with: diff --git a/.github/workflows/utooweb-ci.yml b/.github/workflows/utooweb-ci.yml index 617864f815..fa2328fef1 100644 --- a/.github/workflows/utooweb-ci.yml +++ b/.github/workflows/utooweb-ci.yml @@ -25,7 +25,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 targets: wasm32-unknown-unknown components: rust-src - name: Cache cargo diff --git a/.github/workflows/utooweb-release.yml b/.github/workflows/utooweb-release.yml index 49cd06405f..a52387195a 100644 --- a/.github/workflows/utooweb-release.yml +++ b/.github/workflows/utooweb-release.yml @@ -21,7 +21,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly-2026-01-04 + toolchain: nightly-2026-02-05 targets: wasm32-unknown-unknown components: rust-src - name: Cache cargo diff --git a/agents/tools/analyze_trace.py b/agents/tools/analyze_trace.py index 3650f0899b..cbb08c7ac2 100644 --- a/agents/tools/analyze_trace.py +++ b/agents/tools/analyze_trace.py @@ -3,7 +3,14 @@ Utoopack Performance Analysis Script Based on Utoopack Performance Analysis Agent Protocol -Usage: python3 analyze_trace.py +Key design decisions: +- Thread work is calculated using ONLY top-level (depth=0) spans per thread + to avoid double-counting nested spans. This gives accurate thread utilization. +- Tasks < 10us are excluded from meaningful analysis (tracing instrumentation noise). +- Streaming JSON parsing (ijson) is preferred for large traces, with json.load fallback. +- Thread count only includes threads with actual work events (B/E/X), not metadata-only threads. + +Usage: python3 analyze_trace.py [project_name] """ import json @@ -12,112 +19,142 @@ from collections import defaultdict from datetime import datetime + def analyze_trace(trace_path, output_path, override_project_name=None): """Analyze Chrome Trace and generate performance report following the protocol""" - - print(f"๐Ÿ“Š Loading trace: {trace_path}") - with open(trace_path, 'r') as f: - data = json.load(f) - - events = data if isinstance(data, list) else data.get("traceEvents", []) - print(f"โœ… Analyzing {len(events):,} events...") - # Try to infer project name + print(f"Loading trace: {trace_path}") + trace_size = os.path.getsize(trace_path) + trace_size_gb = trace_size / (1024**3) + print(f" Trace size: {trace_size_gb:.2f} GB") + + # Try streaming parse for large files, fall back to json.load + events = _load_events(trace_path) + print(f" Analyzing {len(events):,} events...") + project_name = override_project_name or os.environ.get("TRACE_PROJECT") or "Unknown Project" - - # Initialize metrics - min_ts, max_ts = float('inf'), float('-inf') - threads = set() - thread_work = 0.0 - - # Task statistics (only meaningful tasks โ‰ฅ10ยตs) - meaningful_tasks = defaultdict(lambda: {'count': 0, 'duration': 0.0}) - noise_count = 0 - - # Duration buckets - buckets = {'<10us': 0, '10us-100us': 0, '100us-1ms': 0, '1ms-10ms': 0, '10ms-100ms': 0, '>100ms': 0} - - # Stack for B/E events - stacks = defaultdict(list) - + + # โ”€โ”€ Pass 1: Pair B/E events into complete spans, collect X events โ”€โ”€ + # Each span: (name, tid, start_ts, duration) + spans = [] + stacks = defaultdict(list) # tid -> [(start_ts, name), ...] + for event in events: ph = event.get('ph') + if ph not in ('B', 'E', 'X'): + continue + tid = event.get('tid') ts = event.get('ts', 0) name = event.get('name', 'unknown') - - if ts > 0: - min_ts = min(min_ts, ts) - max_ts = max(max_ts, ts) - - threads.add(tid) - - if ph == 'X': # Complete event - dur = event.get('dur', 0) - max_ts = max(max_ts, ts + dur) - thread_work += dur - - if dur < 10: - noise_count += 1 - buckets['<10us'] += 1 - else: - if dur < 100: buckets['10us-100us'] += 1 - elif dur < 1000: buckets['100us-1ms'] += 1 - elif dur < 10000: buckets['1ms-10ms'] += 1 - elif dur < 100000: buckets['10ms-100ms'] += 1 - else: buckets['>100ms'] += 1 - - meaningful_tasks[name]['count'] += 1 - meaningful_tasks[name]['duration'] += dur - - elif ph == 'B': # Begin event + + # Ensure numeric types are float (ijson may return Decimal) + ts = float(ts) if ts else 0.0 + + if ph == 'X': + dur = float(event.get('dur', 0)) + spans.append((name, tid, ts, dur)) + elif ph == 'B': stacks[tid].append((ts, name)) - - elif ph == 'E': # End event + elif ph == 'E': if stacks[tid]: start_ts, start_name = stacks[tid].pop() dur = ts - start_ts - thread_work += dur - - if dur < 10: - noise_count += 1 - buckets['<10us'] += 1 - else: - if dur < 100: buckets['10us-100us'] += 1 - elif dur < 1000: buckets['100us-1ms'] += 1 - elif dur < 10000: buckets['1ms-10ms'] += 1 - elif dur < 100000: buckets['10ms-100ms'] += 1 - else: buckets['>100ms'] += 1 - - meaningful_tasks[start_name]['count'] += 1 - meaningful_tasks[start_name]['duration'] += dur - - # Calculate metrics - wall_time_ms = (max_ts - min_ts) / 1000.0 - thread_work_ms = thread_work / 1000.0 - num_threads = len(threads) if threads else 1 - parallelism = thread_work / (max_ts - min_ts) if (max_ts - min_ts) > 0 else 0 + if dur >= 0: + spans.append((start_name, tid, start_ts, dur)) + + print(f" Paired {len(spans):,} complete spans") + + # โ”€โ”€ Pass 2: Calculate thread work WITHOUT nesting overlap โ”€โ”€ + # Group spans by thread, then compute non-overlapping coverage + thread_spans = defaultdict(list) + for name, tid, start, dur in spans: + if dur > 0: + thread_spans[tid].append((start, start + dur)) + + # For each thread, merge overlapping intervals to get true busy time + total_thread_work_us = 0.0 + global_min_ts = float('inf') + global_max_ts = float('-inf') + working_threads = set() + + for tid, intervals in thread_spans.items(): + if not intervals: + continue + working_threads.add(tid) + + # Update global time range + for s, e in intervals: + global_min_ts = min(global_min_ts, s) + global_max_ts = max(global_max_ts, e) + + # Merge overlapping intervals + intervals.sort() + merged = [intervals[0]] + for s, e in intervals[1:]: + if s <= merged[-1][1]: + merged[-1] = (merged[-1][0], max(merged[-1][1], e)) + else: + merged.append((s, e)) + + thread_busy = sum(e - s for s, e in merged) + total_thread_work_us += thread_busy + + # โ”€โ”€ Pass 3: Task-level statistics (per-span, including nested) โ”€โ”€ + # This is for identifying hotspots - nesting is fine here since we want + # to know which *functions* are expensive, not thread occupancy. + meaningful_tasks = defaultdict(lambda: {'count': 0, 'duration': 0.0, 'max': 0.0}) + noise_count = 0 + total_task_count = 0 + buckets = {'<10us': 0, '10us-100us': 0, '100us-1ms': 0, '1ms-10ms': 0, '10ms-100ms': 0, '>100ms': 0} + + for name, tid, start, dur in spans: + total_task_count += 1 + if dur < 10: + noise_count += 1 + buckets['<10us'] += 1 + else: + if dur < 100: + buckets['10us-100us'] += 1 + elif dur < 1000: + buckets['100us-1ms'] += 1 + elif dur < 10000: + buckets['1ms-10ms'] += 1 + elif dur < 100000: + buckets['10ms-100ms'] += 1 + else: + buckets['>100ms'] += 1 + + meaningful_tasks[name]['count'] += 1 + meaningful_tasks[name]['duration'] += dur + meaningful_tasks[name]['max'] = max(meaningful_tasks[name]['max'], dur) + + # โ”€โ”€ Metrics โ”€โ”€ + wall_time_us = global_max_ts - global_min_ts if global_max_ts > global_min_ts else 1 + wall_time_ms = wall_time_us / 1000.0 + thread_work_ms = total_thread_work_us / 1000.0 + num_threads = len(working_threads) + parallelism = total_thread_work_us / wall_time_us if wall_time_us > 0 else 0 utilization = (parallelism / num_threads) * 100 if num_threads > 0 else 0 - - total_tasks = noise_count + sum(t['count'] for t in meaningful_tasks.values()) + meaningful_count = sum(t['count'] for t in meaningful_tasks.values()) - + total_tasks_safe = max(total_task_count, 1) + # Sort tasks - by_count = sorted(meaningful_tasks.items(), key=lambda x: x[1]['count'], reverse=True) by_duration = sorted(meaningful_tasks.items(), key=lambda x: x[1]['duration'], reverse=True) - - # Categorize tasks for Tiered analysis + + # Categorize for tiered analysis categories = { 'P0: Runtime/Resolution': ['resolve', 'plugin', 'turbo_tasks'], 'P1: I/O & Heavy Tasks': ['read', 'stat', 'fs', 'analyze ecmascript'], 'P3: Asset Pipeline': ['parse', 'transform', 'analyze', 'chunk', 'minify', 'generate', 'swc'], 'P4: Bridge/Interop': ['napi', 'wasm', 'bridge'] } - + cat_stats = defaultdict(lambda: {'count': 0, 'duration': 0.0}) for name, stats in meaningful_tasks.items(): - matched = False lower_name = name.lower() + matched = False for cat, keywords in categories.items(): if any(kw in lower_name for kw in keywords): cat_stats[cat]['count'] += stats['count'] @@ -128,214 +165,258 @@ def analyze_trace(trace_path, output_path, override_project_name=None): cat_stats['Other']['count'] += stats['count'] cat_stats['Other']['duration'] += stats['duration'] - # Generate report + # โ”€โ”€ Generate Report โ”€โ”€ report_id = f"utoopack_performance_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}" trace_file = os.path.basename(trace_path) - trace_size_gb = os.path.getsize(trace_path) / (1024**3) - - # Pre-calculate derived metrics - max_thread_work = max(thread_work, 1) # Avoid division by zero - total_tasks_safe = max(total_tasks, 1) # Utilization status if utilization < 60: - util_status = 'โš ๏ธ Suboptimal' + util_status = '\u26a0\ufe0f Suboptimal' elif utilization > 80: - util_status = 'โœ… Good' + util_status = '\u2705 Good' else: - util_status = '๐Ÿ†— Average' + util_status = '\U0001f197 Average' # Workload Distribution Table Rows + # Use thread_work (non-overlapping) as denominator for percentage + work_denom = max(total_thread_work_us, 1) workload_rows = "" for cat_name in ['P0: Runtime/Resolution', 'P1: I/O & Heavy Tasks', 'P3: Asset Pipeline', 'P4: Bridge/Interop', 'Other']: - stats = cat_stats[cat_name] - dur_ms = stats['duration'] / 1000.0 - pct = (stats['duration'] * 100) / max_thread_work - workload_rows += f"| {cat_name} | {stats['count']:,} | {dur_ms:,.1f} | {pct:.1f}% |\n" + s = cat_stats[cat_name] + dur_ms = s['duration'] / 1000.0 + # Note: these percentages can exceed 100% in aggregate because task durations + # include nesting, while thread_work is de-duped. This is expected and useful + # for understanding where time is *attributed* even if overlapping. + pct = (s['duration'] * 100) / work_denom + workload_rows += f"| {cat_name} | {s['count']:,} | {dur_ms:,.1f} | {pct:.1f}% |\n" + + # Top 20 tasks table + top_tasks_rows = "" + for name, stats in by_duration[:20]: + avg_us = stats['duration'] / stats['count'] + total_ms = stats['duration'] / 1000.0 + max_ms = stats['max'] / 1000.0 + pct_work = (stats['duration'] * 100) / work_denom + top_tasks_rows += f"| {total_ms:,.1f} | {stats['count']:,} | {avg_us:.1f} | {max_ms:,.1f} | {pct_work:.1f}% | `{name}` |\n" - report = f"""# ๐Ÿš€ Utoopack Performance Report: Async Task Scheduling Overhead Analysis + noise_pct = (noise_count * 100) / total_tasks_safe + lost_parallelism = max(0, int((1 - utilization / 100) * 100)) -**Report ID**: `{report_id}` -**Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -**Trace File**: `{trace_file}` ({trace_size_gb:.1f}GB, {len(events)/1e6:.2f}M events) + # P0 action + if utilization < 70: + p0_action = f"1. **[P0]** Profile lock contention to address {lost_parallelism}% lost parallelism" + else: + p0_action = "1. **[P0]** Investigate task scheduling gaps for incremental gains" + + # Diagnostic signals + noise_signal = '\u26a0\ufe0f **Significant**' if noise_pct > 60 else '\u2705 Acceptable' + util_signal = '\U0001f6a8 **Low**' if utilization < 60 else ('\U0001f197 Average' if utilization < 80 else '\u2705 Good') + monolith_signal = '\u26a0\ufe0f **Detected**' if buckets['>100ms'] > 5 else '\u2705 Minimal' + p1_io_status = '\u26a0\ufe0f Bound' if cat_stats['P1: I/O & Heavy Tasks']['duration'] / work_denom > 0.3 else '\u2705 Healthy' + p0_sched_status = '\u26a0\ufe0f High' if cat_stats['P0: Runtime/Resolution']['duration'] / work_denom > 0.4 else '\u2705 Normal' + p4_status = '\u26a0\ufe0f High' if cat_stats['P4: Bridge/Interop']['duration'] / work_denom > 0.1 else '\u2705 Low' + p4_examine_signal = '\u26a0\ufe0f Examine' if cat_stats['P4: Bridge/Interop']['duration'] / work_denom > 0.1 else 'Low' + throughput_assessment = '**reasonable** throughput' if utilization > 70 else '**significant loss** of potential parallelism' + + report = f"""# Utoopack Performance Report + +**Report ID**: `{report_id}` +**Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} +**Trace File**: `{trace_file}` ({trace_size_gb:.1f}GB, {len(events)/1e6:.2f}M events) **Test Project**: `{project_name}` --- -## ๐Ÿ“Š Executive Summary - -This report analyzes the performance of Utoopack/Turbopack, covering the full spectrum of the Performance Analysis Protocol (P0-P4). +## Executive Summary ### Key Findings | Metric | Value | Assessment | |--------|-------|------------| | Total Wall Time | **{wall_time_ms:,.1f} ms** | Baseline | -| Total Thread Work | **{thread_work_ms:,.1f} ms** | ~{parallelism:.1f}x parallelism | +| Total Thread Work (de-duped) | **{thread_work_ms:,.1f} ms** | Non-overlapping busy time | +| Effective Parallelism | **{parallelism:.1f}x** | thread_work / wall_time | +| Working Threads | **{num_threads}** | Threads with actual spans | | Thread Utilization | **{utilization:.1f}%** | {util_status} | -| turbo_tasks::function Invocations | **{total_tasks:,}** | Total count | -| Meaningful Tasks (โ‰ฅ 10ยตs) | **{meaningful_count:,}** | ({meaningful_count*100/total_tasks_safe:.1f}% of total) | -| Tracing Noise (< 10ยตs) | **{noise_count:,}** | ({noise_count*100/total_tasks_safe:.1f}% of total) | +| Total Spans | **{total_task_count:,}** | All B/E + X events | +| Meaningful Spans (>= 10us) | **{meaningful_count:,}** | ({meaningful_count*100/total_tasks_safe:.1f}% of total) | +| Tracing Noise (< 10us) | **{noise_count:,}** | ({noise_pct:.1f}% of total) | + +> **Note on Thread Work**: Thread work is computed by merging overlapping intervals +> per thread, eliminating double-counting from nested spans. This gives the true +> wall-clock busy time across all threads. ### Workload Distribution by Tier -| Category | Tasks | Total Time (ms) | % of Work | -|----------|-------|-----------------|-----------| +| Category | Tasks | Total Time (ms) | % of Thread Work | +|----------|-------|-----------------|------------------| {workload_rows} +> **Note**: Percentages may sum to >100% because task durations include nesting +> while thread work is de-duplicated. This is intentional for hotspot attribution. --- -## โšก Parallelization Analysis (P0-P2) +## Parallelization Analysis ### Thread Utilization | Metric | Value | |--------|-------| -| Number of Threads | {num_threads} | -| Total Thread Work | {thread_work_ms:,.1f} ms | -| Avg Work per Thread | {thread_work_ms/num_threads:,.1f} ms | -| Theoretical Parallelism | {parallelism:.2f}x | +| Working Threads | {num_threads} | +| Total Thread Work (de-duped) | {thread_work_ms:,.1f} ms | +| Avg Work per Thread | {thread_work_ms/max(num_threads,1):,.1f} ms | +| Effective Parallelism | {parallelism:.2f}x | | Thread Utilization | **{utilization:.1f}%** | -**Assessment**: With {num_threads} threads available, achieving {parallelism:.1f}x parallelism indicates {'**reasonable** throughput' if utilization > 70 else '**significant loss** of potential parallelism'}. +**Assessment**: With {num_threads} working threads, achieving {parallelism:.1f}x parallelism indicates {throughput_assessment}. --- -## ๐Ÿ“ˆ Top 20 Tasks (Global) - -These are the most significant tasks by total duration: +## Top 20 Tasks by Total Duration -| Total (ms) | Count | Avg (ยตs) | % Work | Task Name | -|------------|-------|----------|--------|-----------| -""" - - for name, stats in by_duration[:20]: - avg_us = stats['duration'] / stats['count'] - total_ms = stats['duration'] / 1000.0 - pct_work = (stats['duration'] * 100) / max_thread_work - report += f"| {total_ms:,.1f} | {stats['count']:,} | {avg_us:.1f} | {pct_work:.1f}% | `{name}` |\n" - - report += f""" +| Total (ms) | Count | Avg (us) | Max (ms) | % Work | Task Name | +|------------|-------|----------|----------|--------|-----------| +{top_tasks_rows} --- -## ๐Ÿ” Deep Dive by Tier +## Deep Dive by Tier -### ๐Ÿ”ด Tier 1: Runtime & Resolution (P0) +### Tier 1: Runtime & Resolution (P0) *Focus: Task scheduling and dependency resolution.* | Metric | Value | Status | |--------|-------|--------| -| Total Scheduling Time | {cat_stats['P0: Runtime/Resolution']['duration']/1000:,.1f} ms | {'โš ๏ธ High' if cat_stats['P0: Runtime/Resolution']['duration']/max_thread_work > 0.4 else 'โœ… Normal'} | -| Resolution Hotspots | {len([n for n in meaningful_tasks if 'resolve' in n.lower()])} tasks | ๐Ÿ” Check Top Tasks | +| Total Scheduling Time | {cat_stats['P0: Runtime/Resolution']['duration']/1000:,.1f} ms | {p0_sched_status} | +| Resolution Hotspots | {len([n for n in meaningful_tasks if 'resolve' in n.lower()])} distinct task types | Check Top Tasks | -**Potential P0 Issues**: -- Low thread utilization ({utilization:.1f}%) suggests critical path serialization or lock contention. -- {noise_count:,} tasks < 10ยตs ({noise_count*100/total_tasks_safe:.1f}%) contribute to scheduler pressure. +**Potential P0 Issues**: +- Thread utilization at {utilization:.1f}% {'suggests critical path serialization or lock contention.' if utilization < 70 else 'is reasonable but may have room for improvement.'} +- {noise_count:,} spans < 10us ({noise_pct:.1f}%) contribute to scheduler pressure. -### ๐ŸŸ  Tier 2: Physical & Resource Barriers (P1) +### Tier 2: Physical & Resource Barriers (P1) *Focus: Hardware utilization, I/O, and heavy monoliths.* | Metric | Value | Status | |--------|-------|--------| -| I/O Work (Estimated) | {cat_stats['P1: I/O & Heavy Tasks']['duration']/1000:,.1f} ms | {'โš ๏ธ Bound' if cat_stats['P1: I/O & Heavy Tasks']['duration']/max_thread_work > 0.3 else 'โœ… Healthy'} | -| Large Tasks (> 100ms) | {buckets['>100ms']} | {'๐Ÿšจ Critical' if buckets['>100ms'] > 5 else 'โœ… Minimal'} | +| I/O Work (Estimated) | {cat_stats['P1: I/O & Heavy Tasks']['duration']/1000:,.1f} ms | {p1_io_status} | +| Large Tasks (> 100ms) | {buckets['>100ms']} | {'Critical' if buckets['>100ms'] > 5 else 'Minimal'} | -**Potential P1 Issues**: -- {buckets['>100ms']} tasks exceed 100ms. These "Heavy Monoliths" are prime candidates for splitting. - -### ๐ŸŸก Tier 3: Architecture & Asset Pipeline (P2-P3) +### Tier 3: Architecture & Asset Pipeline (P2-P3) *Focus: Global state and transformation pipeline.* | Metric | Value | Status | |--------|-------|--------| -| Asset Processing (P3) | {cat_stats['P3: Asset Pipeline']['duration']/1000:,.1f} ms | {cat_stats['P3: Asset Pipeline']['duration']*100/max_thread_work:.1f}% of work | -| Bridge Overhead (P4) | {cat_stats['P4: Bridge/Interop']['duration']/1000:,.1f} ms | {'โš ๏ธ High' if cat_stats['P4: Bridge/Interop']['duration']/max_thread_work > 0.1 else 'โœ… Low'} | +| Asset Processing (P3) | {cat_stats['P3: Asset Pipeline']['duration']/1000:,.1f} ms | {cat_stats['P3: Asset Pipeline']['duration']*100/work_denom:.1f}% of work | +| Bridge Overhead (P4) | {cat_stats['P4: Bridge/Interop']['duration']/1000:,.1f} ms | {p4_status} | --- -## ๐Ÿ’ก Recommendations (Prioritized P0-P2) - -### ๐Ÿšจ Critical: (P0) Improvement - -**Problem**: {utilization:.1f}% thread utilization. -**Action**: -1. Profile lock contention if utilization < 60%. -2. Convert sequential `await` chains to `try_join`. - -### โš ๏ธ High Priority: (P1) Optimization - -**Problem**: {buckets['>100ms']} heavy tasks detected. -**Action**: -1. Identify module-level bottlenecks (e.g., barrel files). -2. Optimize I/O batching for metadata. +## Duration Distribution -### โš ๏ธ Medium Priority: (P3) Pipeline Efficiency - -**Action**: -1. Review transformation logic for frequently changed assets. -2. Minimize cross-language serialization (P4) if overhead exceeds 10%. +| Range | Count | Percentage | +|-------|-------|------------| +| < 10us (noise) | {buckets['<10us']:,} | {buckets['<10us']*100/total_tasks_safe:.1f}% | +| 10us - 100us | {buckets['10us-100us']:,} | {buckets['10us-100us']*100/total_tasks_safe:.1f}% | +| 100us - 1ms | {buckets['100us-1ms']:,} | {buckets['100us-1ms']*100/total_tasks_safe:.1f}% | +| 1ms - 10ms | {buckets['1ms-10ms']:,} | {buckets['1ms-10ms']*100/total_tasks_safe:.1f}% | +| 10ms - 100ms | {buckets['10ms-100ms']:,} | {buckets['10ms-100ms']*100/total_tasks_safe:.1f}% | +| > 100ms | {buckets['>100ms']:,} | {buckets['>100ms']*100/total_tasks_safe:.1f}% | --- -## ๐Ÿ“ Diagnostic Signal Summary +## Diagnostic Signal Summary | Signal | Status | Finding | -|--------|--------|---------|""" - - noise_pct = (noise_count * 100) / total_tasks_safe - report += f"\n| Tracing Noise (P0) | {'โš ๏ธ **Significant**' if noise_pct > 60 else 'โœ… Acceptable'} | {noise_pct:.1f}% of tasks < 10ยตs |" - report += f"\n| Thread Utilization (P0) | {'๐Ÿšจ **Low**' if utilization < 60 else 'โœ… Good'} | {utilization:.1f}% utilization |" - report += f"\n| Heavy Monoliths (P1) | {'โš ๏ธ **Detected**' if buckets['>100ms'] > 5 else 'โœ… Minimal'} | {buckets['>100ms']} tasks > 100ms |" - report += f"\n| Asset Pipeline (P3) | ๐Ÿ” **Review** | {cat_stats['P3: Asset Pipeline']['duration']/1000:,.1f} ms total |" - report += f"\n| Bridge/Interop (P4) | {'โš ๏ธ **Examine**' if cat_stats['P4: Bridge/Interop']['duration']/max_thread_work > 0.1 else 'โœ… Low'} | {cat_stats['P4: Bridge/Interop']['duration']/1000:,.1f} ms total |" - - report += """ +|--------|--------|---------| +| Tracing Noise (P0) | {noise_signal} | {noise_pct:.1f}% of spans < 10us | +| Thread Utilization (P0) | {util_signal} | {utilization:.1f}% utilization | +| Heavy Monoliths (P1) | {monolith_signal} | {buckets['>100ms']} tasks > 100ms | +| Asset Pipeline (P3) | Review | {cat_stats['P3: Asset Pipeline']['duration']/1000:,.1f} ms total | +| Bridge/Interop (P4) | {p4_examine_signal} | {cat_stats['P4: Bridge/Interop']['duration']/1000:,.1f} ms total | --- -## ๐ŸŽฏ Action Items (Comprehensive P0-P4) +## Action Items (P0-P4) -""" - - lost_parallelism = int((1 - utilization/100) * 100) - if utilization < 70: - report += f"1. **[P0]** Profile lock contention to address {lost_parallelism}% lost parallelism\n" - else: - report += "1. **[P0]** Investigate task scheduling gaps for incremental gains\n" - - report += """2. **[P1]** Breakdown heavy monolith tasks (>100ms) to improve granularity -3. **[P1]** Review I/O patterns for potential batching opportunities -4. **[P3]** Optimize asset transformation pipeline hot-spots -5. **[P4]** Reduce "chatty" bridge operations if interop overhead is significant +{p0_action} +2. **[P1]** Breakdown heavy monolith tasks (>100ms) to improve granularity +3. **[P1]** Review I/O patterns for potential batching opportunities +4. **[P3]** Optimize asset transformation pipeline hot-spots +5. **[P4]** Reduce "chatty" bridge operations if interop overhead is significant --- -*Report generated by Utoopack Performance Analysis Agent on """ + datetime.now().strftime('%Y-%m-%d') + """* +*Report generated by Utoopack Performance Analysis Agent on {datetime.now().strftime('%Y-%m-%d')}* *Following: Utoopack Performance Analysis Agent Protocol* """ - - print(f"๐Ÿ’พ Writing report to {output_path}") + + print(f"Writing report to {output_path}") with open(output_path, 'w') as f: f.write(report) - - print(f"โœ… Report generated successfully!") - print(f"\n๐Ÿ“Š Quick Summary:") - print(f" Wall Time: {wall_time_ms:,.1f} ms") - print(f" Parallelism: {parallelism:.1f}x") - print(f" Thread Util: {utilization:.1f}%") - print(f" Meaningful Tasks: {meaningful_count:,}") + + print(f"Report generated successfully!") + print(f"\nQuick Summary:") + print(f" Wall Time: {wall_time_ms:,.1f} ms") + print(f" Thread Work: {thread_work_ms:,.1f} ms (de-duped)") + print(f" Parallelism: {parallelism:.1f}x") + print(f" Working Threads: {num_threads}") + print(f" Thread Util: {utilization:.1f}%") + print(f" Meaningful Tasks:{meaningful_count:,}") + + +def _load_events(trace_path): + """Load trace events. Uses streaming for large files if ijson is available.""" + size_gb = os.path.getsize(trace_path) / (1024**3) + + # Try ijson for large files + if size_gb > 0.5: + try: + import ijson + print(f" Using streaming parser (ijson) for {size_gb:.1f}GB file...") + + # Peek at first non-whitespace char to determine format + with open(trace_path, 'rb') as f: + first_char = b'' + while True: + ch = f.read(1) + if not ch: + break + if ch.strip(): + first_char = ch + break + + # JSON array [...] uses 'item', object {"traceEvents":[...]} uses 'traceEvents.item' + prefix = 'item' if first_char == b'[' else 'traceEvents.item' + print(f" Detected format: {'array' if first_char == b'[' else 'object'}, using prefix '{prefix}'") + + events = [] + with open(trace_path, 'rb') as f: + for obj in ijson.items(f, prefix): + events.append(obj) + if events: + return events + print(f" ijson returned 0 events, falling back to json.load...") + except ImportError: + print(f" ijson not available, falling back to json.load (may use significant memory)...") + except Exception as e: + print(f" ijson error: {e}, falling back to json.load...") + + with open(trace_path, 'r') as f: + data = json.load(f) + return data if isinstance(data, list) else data.get("traceEvents", []) + if __name__ == "__main__": if len(sys.argv) < 3: print("Usage: python3 analyze_trace.py [project_name]") sys.exit(1) - + trace_file = sys.argv[1] report_file = sys.argv[2] override_project_name = sys.argv[3] if len(sys.argv) > 3 else None - + if not os.path.exists(trace_file): print(f"Error: Trace file not found: {trace_file}") sys.exit(1) - + analyze_trace(trace_file, report_file, override_project_name) diff --git a/crates/pack-napi/src/pack_api/project.rs b/crates/pack-napi/src/pack_api/project.rs index c4d56ee1f3..a9be619c27 100644 --- a/crates/pack-napi/src/pack_api/project.rs +++ b/crates/pack-napi/src/pack_api/project.rs @@ -348,7 +348,7 @@ pub async fn project_new( subscriber.init(); }); } else if let Some(chrome_file) = tracing_chrome { - let mut builder = tracing_chrome::ChromeLayerBuilder::new(); + let mut builder = tracing_chrome::ChromeLayerBuilder::new().include_args(false); if chrome_file != "1" && chrome_file != "true" { builder = builder.file(chrome_file); } @@ -361,7 +361,7 @@ pub async fn project_new( TRACING_INIT.call_once(|| { tracing_subscriber::registry() - .with(EnvFilter::new("trace")) + .with(EnvFilter::new("info")) .with(chrome_layer) .init(); }); diff --git a/examples/with-antd/package.json b/examples/with-antd/package.json index 33e72b4d79..19e06c0c19 100644 --- a/examples/with-antd/package.json +++ b/examples/with-antd/package.json @@ -30,7 +30,9 @@ "@types/react-dom": "^18.2.0", "@types/styled-components": "^5.1.34", "@utoo/pack-cli": "*", - "serve": "^14.2.4" + "serve": "^14.2.4", + "less": "^4.0.0", + "less-loader": "^12.0.0" }, "repository": "git@github.com:utooland/utoo.git" } diff --git a/package-lock.json b/package-lock.json index 6951d1f1eb..f8f817a32b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,6 +88,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -127,6 +128,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -211,6 +213,8 @@ "@types/react-dom": "^18.2.0", "@types/styled-components": "^5.1.34", "@utoo/pack-cli": "*", + "less": "^4.0.0", + "less-loader": "^12.0.0", "serve": "^14.2.4" } }, @@ -322,6 +326,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -381,6 +386,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -835,6 +841,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -2758,6 +2765,7 @@ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "license": "MIT", + "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -3919,6 +3927,7 @@ "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.19.6", "@svgr/babel-preset": "^6.5.1", @@ -4274,6 +4283,7 @@ "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -4363,8 +4373,7 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/@types/webpack": { "version": "5.28.5", @@ -4673,6 +4682,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4706,7 +4716,6 @@ "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", "license": "MIT", - "peer": true, "dependencies": { "loader-utils": "^2.0.0", "regex-parser": "^2.2.11" @@ -4720,6 +4729,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4832,6 +4842,7 @@ "resolved": "https://registry.npmjs.org/antd/-/antd-5.29.3.tgz", "integrity": "sha512-3DdbGCa9tWAJGcCJ6rzR8EJFsv2CtyEbkVabZE14pfgUHfCicWCj0/QzQVLDYg8CPfQk9BH7fHCoTXHTy7MP/A==", "license": "MIT", + "peer": true, "dependencies": { "@ant-design/colors": "^7.2.1", "@ant-design/cssinjs": "^1.23.0", @@ -5747,7 +5758,6 @@ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "license": "MIT", - "peer": true, "engines": { "node": "*" } @@ -6157,6 +6167,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -6913,7 +6924,6 @@ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", "license": "MIT", - "peer": true, "dependencies": { "is-what": "^3.14.1" }, @@ -7266,6 +7276,7 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -7743,7 +7754,6 @@ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", "license": "(MPL-2.0 OR Apache-2.0)", - "peer": true, "optionalDependencies": { "@types/trusted-types": "^2.0.7" } @@ -7842,7 +7852,6 @@ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "license": "MIT", - "peer": true, "engines": { "node": ">= 4" } @@ -7901,7 +7910,6 @@ "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "prr": "~1.0.1" }, @@ -8596,7 +8604,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -9156,7 +9163,6 @@ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -9204,7 +9210,6 @@ "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", "license": "MIT", "optional": true, - "peer": true, "bin": { "image-size": "bin/image-size.js" }, @@ -9222,8 +9227,7 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/import-fresh": { "version": "3.3.1", @@ -9605,8 +9609,7 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/is-wsl": { "version": "2.2.0", @@ -9698,6 +9701,7 @@ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -9815,9 +9819,9 @@ } }, "node_modules/less-loader": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.3.0.tgz", - "integrity": "sha512-0M6+uYulvYIWs52y0LqN4+QM9TqWAohYSNTo4htE8Z7Cn3G/qQMEmktfHmyJT23k+20kU9zHH2wrfFXkxNLtVw==", + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.3.1.tgz", + "integrity": "sha512-JZZmG7gMzoDP3VGeEG8Sh6FW5wygB5jYL7Wp29FFihuRTsIBacqO3LbRPr2yStYD11riVf13selLm/CPFRDBRQ==", "license": "MIT", "peer": true, "engines": { @@ -9828,7 +9832,7 @@ "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "@rspack/core": "0.x || 1.x", + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", "less": "^3.5.0 || ^4.0.0", "webpack": "^5.0.0" }, @@ -9888,7 +9892,6 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "license": "MIT", - "peer": true, "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -9966,7 +9969,6 @@ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" @@ -9981,7 +9983,6 @@ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "license": "ISC", "optional": true, - "peer": true, "bin": { "semver": "bin/semver" } @@ -9991,7 +9992,6 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", "license": "MIT", - "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -10244,6 +10244,7 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "license": "MIT", + "peer": true, "engines": { "node": "*" } @@ -10319,7 +10320,6 @@ "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "iconv-lite": "^0.6.3", "sax": "^1.2.4" @@ -10739,7 +10739,6 @@ "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.10" } @@ -10892,7 +10891,6 @@ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=6" } @@ -11294,8 +11292,7 @@ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/public-encrypt": { "version": "4.0.3", @@ -11646,6 +11643,7 @@ "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.38.2.tgz", "integrity": "sha512-O83Oi1qPyEv31Sg+Jwvsj6pXc8uQI2BtIAkURr5lvEYHVggXJhdU/nynK8wY1gbw0qR48k731sN5ON4egRCROA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.0", "async-validator": "^4.1.0", @@ -12159,6 +12157,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -12168,6 +12167,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -12189,8 +12189,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz", "integrity": "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", @@ -12345,8 +12344,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.1.tgz", "integrity": "sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/regexpu-core": { "version": "6.4.0", @@ -12515,7 +12513,6 @@ "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", "license": "MIT", - "peer": true, "dependencies": { "adjust-sourcemap-loader": "^4.0.0", "convert-source-map": "^1.7.0", @@ -12531,8 +12528,7 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/retry": { "version": "0.13.1", @@ -12738,7 +12734,6 @@ "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", "license": "BlueOak-1.0.0", "optional": true, - "peer": true, "engines": { "node": ">=11.0.0" } @@ -13664,6 +13659,7 @@ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-imports": "^7.0.0", "@babel/traverse": "^7.4.5", @@ -13893,6 +13889,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -13959,24 +13956,6 @@ "node": ">=4" } }, - "node_modules/tailwindcss/node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, "node_modules/tapable": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", @@ -14170,6 +14149,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -14374,7 +14354,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tsyringe": { "version": "4.10.0", @@ -14553,6 +14534,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14761,6 +14743,7 @@ "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.13.2.tgz", "integrity": "sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==", "license": "MIT", + "peer": true, "dependencies": { "derive-valtio": "0.1.0", "proxy-compare": "2.6.0", @@ -14845,6 +14828,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz", "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -14894,6 +14878,7 @@ "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", @@ -15372,6 +15357,7 @@ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true, "license": "ISC", + "peer": true, "engines": { "node": ">= 6" } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ddea9ef8e9..bf8b20fbe1 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] # Same channel with turbo of next.js -channel = "nightly-2026-01-04" +channel = "nightly-2026-02-05" components = ["rustfmt", "clippy", "rust-analyzer"] profile = "minimal"