From 7071a094fb96e438721b05f096d7a1da9e12f141 Mon Sep 17 00:00:00 2001 From: elrrrrrrr Date: Fri, 22 May 2026 01:49:02 +0800 Subject: [PATCH 1/4] perf(pm): add resolver manifest provider boundary --- crates/ruborist/src/service/mod.rs | 2 + crates/ruborist/src/service/provider.rs | 82 +++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 crates/ruborist/src/service/provider.rs diff --git a/crates/ruborist/src/service/mod.rs b/crates/ruborist/src/service/mod.rs index 14723d5edd..58f41f9a9b 100644 --- a/crates/ruborist/src/service/mod.rs +++ b/crates/ruborist/src/service/mod.rs @@ -49,6 +49,7 @@ pub(crate) mod fetch; mod fs; pub(crate) mod http; pub(crate) mod manifest; +mod provider; mod registry; mod store; @@ -64,5 +65,6 @@ pub use manifest::{ FetchVersionManifestOptions, MetadataFormat, fetch_full_manifest, fetch_full_manifest_bytes, fetch_full_manifest_fresh, fetch_version_manifest, fetch_version_manifest_bytes, }; +pub use provider::{ManifestFullData, ManifestJob, ManifestJobDone, ManifestProvider}; pub use registry::UnifiedRegistry; pub use store::{ManifestStore, NoopStore}; diff --git a/crates/ruborist/src/service/provider.rs b/crates/ruborist/src/service/provider.rs new file mode 100644 index 0000000000..65609a0ed4 --- /dev/null +++ b/crates/ruborist/src/service/provider.rs @@ -0,0 +1,82 @@ +//! Manifest provider boundary for resolver drivers. +//! +//! The demand BFS loop owns per-run cache, waiters, and inflight de-duplication. +//! A provider only executes one manifest job and hides whether it satisfied the +//! job from memory, persistent storage, or the network. + +use std::sync::Arc; + +use async_trait::async_trait; + +use super::cache::VersionsInfo; +use super::manifest::MetadataFormat; +use crate::model::manifest::{CoreVersionManifest, FullManifest}; +use crate::traits::registry::RegistryClient; + +/// Full-manifest data returned by a provider job. +#[derive(Clone)] +pub enum ManifestFullData { + /// A parsed full manifest. When the original job carried a spec, the + /// provider may also return the matching version manifest extracted in the + /// same worker task so the main loop can avoid an extra extract hop. + Full { + manifest: Arc, + speculative: Option<(String, Arc)>, + }, + /// A validated versions list, usually from a 304 path. The main loop can + /// resolve a concrete version and schedule a version-manifest job. + Versions(Arc), +} + +/// Unit of work spawned by the demand BFS loop. +#[derive(Clone)] +pub enum ManifestJob { + Full { + name: String, + /// Optional range/tag from the BFS edge that caused this full-manifest + /// fetch. The provider can use it to speculatively extract the current + /// version while the full manifest bytes are already on a CPU worker. + spec: Option, + }, + Version { + name: String, + /// Cache/waiter key owned by the main loop. + spec: String, + /// Registry request spec. For npmjs 304 flows this is the resolved + /// exact version, while `spec` remains the original range key. + fetch_spec: String, + /// Metadata format for the version endpoint. Semver-capable registries + /// accept install-v1 for range/tag queries; npmjs exact-version + /// fallback requires the complete metadata format. + format: MetadataFormat, + }, + ExtractVersion { + name: String, + spec: String, + version: String, + full: Arc, + }, +} + +/// Result of one provider job. +pub enum ManifestJobDone { + Full { + name: String, + data: ManifestFullData, + }, + Version { + name: String, + spec: String, + manifest: Arc, + }, +} + +/// Lower-level manifest provider used by the demand BFS loop. +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait ManifestProvider: RegistryClient + Clone + Send + Sync + 'static { + /// Execute one manifest job. The provider owns I/O, persistence, and + /// parse/extract offloading; scheduling, waiters, and inflight + /// de-duplication stay in the BFS loop. + async fn execute_manifest_job(&self, job: ManifestJob) -> Result; +} From c3c7d7aaaf864296199973f550da0a5a686f0050 Mon Sep 17 00:00:00 2001 From: elrrrrrrr Date: Fri, 22 May 2026 06:24:32 +0800 Subject: [PATCH 2/4] fix(ci): stabilize pm view and web worker checks --- crates/pm/src/cmd/view.rs | 41 +++++++++++++++++-- .../webpackLoaders/polyfills/nodePolyFills.ts | 16 ++++---- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/crates/pm/src/cmd/view.rs b/crates/pm/src/cmd/view.rs index e5bfaad79b..8a952f4e1f 100644 --- a/crates/pm/src/cmd/view.rs +++ b/crates/pm/src/cmd/view.rs @@ -10,6 +10,11 @@ use crate::util::user_config::get_registry; /// View package information from registry, similar to npm view pub async fn view(package_spec: &str) -> Result<()> { + let registry_url = get_registry(); + view_with_registry(package_spec, ®istry_url).await +} + +async fn view_with_registry(package_spec: &str, registry_url: &str) -> Result<()> { tracing::debug!("Viewing package: {package_spec}"); // Parse package specification @@ -18,9 +23,8 @@ pub async fn view(package_spec: &str) -> Result<()> { tracing::debug!("Resolved package: {name} (spec: {version_spec})"); // Fetch full manifest directly from registry (Complete format for display, no ETag) - let registry_url = get_registry(); let (full_manifest, _etag) = - fetch_full_manifest_fresh(®istry_url, name, MetadataFormat::Complete) + fetch_full_manifest_fresh(registry_url, name, MetadataFormat::Complete) .await .map_err(|e| anyhow!("Failed to fetch package info for {}: {}", package_spec, e))?; @@ -356,12 +360,41 @@ mod tests { /// because the registry service used ETag caching. #[tokio::test] async fn test_view_twice_no_304_error() { + use mockito::Matcher; + + let mut server = mockito::Server::new_async().await; + let manifest = r#"{ + "name": "is-odd", + "description": "mock package", + "dist-tags": { "latest": "1.0.0" }, + "versions": { + "1.0.0": { + "name": "is-odd", + "version": "1.0.0", + "description": "mock package", + "dist": {} + } + } + }"#; + let mock = server + .mock("GET", "/is-odd") + .match_header("accept", "application/json") + .match_header("if-none-match", Matcher::Missing) + .with_status(200) + .with_header("content-type", "application/json") + .with_header("etag", "\"mock-etag\"") + .with_body(manifest) + .expect(2) + .create_async() + .await; + // First view - should succeed - let result1 = view("is-odd").await; + let result1 = view_with_registry("is-odd", &server.url()).await; assert!(result1.is_ok(), "First view failed: {:?}", result1.err()); // Second view - should also succeed (not fail with 304 error) - let result2 = view("is-odd").await; + let result2 = view_with_registry("is-odd", &server.url()).await; assert!(result2.is_ok(), "Second view failed: {:?}", result2.err()); + mock.assert_async().await; } } diff --git a/packages/utoo-web/src/webpackLoaders/polyfills/nodePolyFills.ts b/packages/utoo-web/src/webpackLoaders/polyfills/nodePolyFills.ts index cb0329793a..e64702b62c 100644 --- a/packages/utoo-web/src/webpackLoaders/polyfills/nodePolyFills.ts +++ b/packages/utoo-web/src/webpackLoaders/polyfills/nodePolyFills.ts @@ -3,15 +3,15 @@ import * as workerThreads from "./workerThreadsPolyfill"; const buffer = require("buffer"); self.Buffer = buffer.Buffer; -const process = require("process"); -const originalCwd = process.cwd; -process.cwd = () => { +const nodeProcess = require("process"); +const originalCwd = nodeProcess.cwd; +nodeProcess.cwd = () => { // @ts-ignore return workerThreads.workerData?.cwd || originalCwd?.() || "/"; }; -if (!process.versions) process.versions = {}; -if (!process.versions.node) process.versions.node = "24.0.0"; -self.process = process; +if (!nodeProcess.versions) nodeProcess.versions = {}; +if (!nodeProcess.versions.node) nodeProcess.versions.node = "24.0.0"; +self.process = nodeProcess; self.global = self; const path = require("path"); @@ -243,8 +243,8 @@ export default { path, "node:path": path, - process, - "node:process": process, + process: nodeProcess, + "node:process": nodeProcess, get url() { return require("url"); From 14d38c9a71de3535c8ebc9f57e7fd1336af46d24 Mon Sep 17 00:00:00 2001 From: elrrrrrrr Date: Fri, 22 May 2026 07:07:13 +0800 Subject: [PATCH 3/4] ci(pm): make defender setup best effort --- .github/workflows/pm-ci.yml | 7 ++++++- .github/workflows/pm-e2e-bench.yml | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pm-ci.yml b/.github/workflows/pm-ci.yml index aa57cc134c..a691c2c223 100644 --- a/.github/workflows/pm-ci.yml +++ b/.github/workflows/pm-ci.yml @@ -56,7 +56,12 @@ jobs: - name: Disable Windows Defender if: runner.os == 'Windows' shell: powershell - run: Set-MpPreference -DisableRealtimeMonitoring $true + run: | + try { + Set-MpPreference -DisableRealtimeMonitoring $true -ErrorAction Stop + } catch { + Write-Warning "Unable to disable Windows Defender real-time monitoring: $_" + } # Add: Configure Git longpaths on Windows - name: Configure Git (Windows) diff --git a/.github/workflows/pm-e2e-bench.yml b/.github/workflows/pm-e2e-bench.yml index c2ac3f7fe1..7505c03191 100644 --- a/.github/workflows/pm-e2e-bench.yml +++ b/.github/workflows/pm-e2e-bench.yml @@ -254,7 +254,12 @@ jobs: - uses: actions/checkout@v4 - name: Disable Windows Defender shell: powershell - run: Set-MpPreference -DisableRealtimeMonitoring $true + run: | + try { + Set-MpPreference -DisableRealtimeMonitoring $true -ErrorAction Stop + } catch { + Write-Warning "Unable to disable Windows Defender real-time monitoring: $_" + } - name: Init git submodules run: git submodule update --init --recursive --depth 1 - name: Setup Rust toolchain @@ -408,7 +413,12 @@ jobs: - uses: actions/checkout@v4 - name: Disable Windows Defender shell: powershell - run: Set-MpPreference -DisableRealtimeMonitoring $true + run: | + try { + Set-MpPreference -DisableRealtimeMonitoring $true -ErrorAction Stop + } catch { + Write-Warning "Unable to disable Windows Defender real-time monitoring: $_" + } - name: Setup node uses: actions/setup-node@v4 with: From 21ea59ba9df51b5df09e0cda2140f6dc7211b9fd Mon Sep 17 00:00:00 2001 From: elrrrrrrr Date: Fri, 22 May 2026 07:32:35 +0800 Subject: [PATCH 4/4] ci(pm): make bench cargo cache best effort --- .github/workflows/pm-e2e-bench.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/pm-e2e-bench.yml b/.github/workflows/pm-e2e-bench.yml index 7505c03191..d880765794 100644 --- a/.github/workflows/pm-e2e-bench.yml +++ b/.github/workflows/pm-e2e-bench.yml @@ -133,6 +133,7 @@ jobs: targets: x86_64-unknown-linux-gnu - name: Cache cargo uses: Swatinem/rust-cache@v2 + continue-on-error: true with: shared-key: pm-build-linux cache-bin: false @@ -200,6 +201,7 @@ jobs: targets: aarch64-apple-darwin - name: Cache cargo uses: Swatinem/rust-cache@v2 + continue-on-error: true with: shared-key: pm-build-mac-arm64 cache-bin: false @@ -232,6 +234,7 @@ jobs: run: rustup target add x86_64-apple-darwin - name: Cache cargo uses: Swatinem/rust-cache@v2 + continue-on-error: true with: shared-key: pm-build-mac-x64 cache-bin: false @@ -269,6 +272,7 @@ jobs: targets: x86_64-pc-windows-msvc - name: Cache cargo uses: Swatinem/rust-cache@v2 + continue-on-error: true with: shared-key: pm-build-windows cache-bin: false