Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/pm-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 16 additions & 2 deletions .github/workflows/pm-e2e-bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -254,7 +257,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
Expand All @@ -264,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
Expand Down Expand Up @@ -408,7 +417,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:
Expand Down
41 changes: 37 additions & 4 deletions crates/pm/src/cmd/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, &registry_url).await
}

async fn view_with_registry(package_spec: &str, registry_url: &str) -> Result<()> {
tracing::debug!("Viewing package: {package_spec}");

// Parse package specification
Expand All @@ -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(&registry_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))?;

Expand Down Expand Up @@ -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;
}
}
2 changes: 2 additions & 0 deletions crates/ruborist/src/service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub(crate) mod fetch;
mod fs;
pub(crate) mod http;
pub(crate) mod manifest;
mod provider;
mod registry;
mod store;

Expand All @@ -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};
82 changes: 82 additions & 0 deletions crates/ruborist/src/service/provider.rs
Original file line number Diff line number Diff line change
@@ -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<FullManifest>,
speculative: Option<(String, Arc<CoreVersionManifest>)>,
},
/// 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<VersionsInfo>),
}
Comment on lines +24 to +29
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using a tuple for speculative results can reduce code clarity and make it harder to extend in the future. Using a named struct explicitly defines the relationship between the spec and the manifest, improving maintainability and readability for other developers.

        speculative: Option<SpeculativeManifest>,
    },
    /// 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<VersionsInfo>),
}

/// Speculative extraction result.
#[derive(Clone)]
pub struct SpeculativeManifest {
    pub spec: String,
    pub manifest: Arc<CoreVersionManifest>,
}


/// 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<String>,
},
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<FullManifest>,
},
}

/// Result of one provider job.
pub enum ManifestJobDone {
Full {
name: String,
data: ManifestFullData,
},
Version {
name: String,
spec: String,
manifest: Arc<CoreVersionManifest>,
},
}

/// 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<ManifestJobDone, Self::Error>;
}
16 changes: 8 additions & 8 deletions packages/utoo-web/src/webpackLoaders/polyfills/nodePolyFills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -243,8 +243,8 @@ export default {
path,
"node:path": path,

process,
"node:process": process,
process: nodeProcess,
"node:process": nodeProcess,

get url() {
return require("url");
Expand Down
Loading