Skip to content
This repository was archived by the owner on Jul 21, 2026. It is now read-only.

feat(lockfile): support npm-alias importer dependency versions#524

Merged
zkochan merged 1 commit into
mainfrom
fix-yaml-parse
May 14, 2026
Merged

feat(lockfile): support npm-alias importer dependency versions#524
zkochan merged 1 commit into
mainfrom
fix-yaml-parse

Conversation

@zkochan

@zkochan zkochan commented May 14, 2026

Copy link
Copy Markdown
Member

Summary

  • Add an Alias(PkgNameVerPeer) variant to ImporterDepVersion so importer version: fields written as <name>@<version> (e.g. version: '@zkochan/js-yaml@0.0.11') parse correctly. Previously these lockfiles failed with Failed to parse importer dependency version.
  • Introduce ImporterDepVersion::resolved_key(importer_key) and route every consumer through it — the snapshot lookup, skipped check, reachability BFS, runtime exclusion, build-sequence root walk, hoisted-dep-graph builder, and both hoist passes. For aliases the resolved key is the alias's own (name, suffix), not (importer-map key, version).
  • For aliased direct deps, the on-disk symlink at <modules_dir>/<importer-key> now points to <slot>/node_modules/<alias-real-name> and the pnpm:root added event reports realName as the resolved package name.

Upstream reference: refToRelative in pnpm/pnpm@8a80235c7b/deps/path/src/index.ts#L96-L110.

Test plan

  • cargo nextest run -p pacquet-lockfile resolved_dependency (11 tests including new alias parse + serde round-trip + resolved_key cases)
  • cargo nextest run -p pacquet-lockfile -p pacquet-package-manager -p pacquet-real-hoist (395 tests)
  • just ready (1237 tests, lint clean)
  • taplo format --check and just dylint clean
  • Built release binary and verified pacquet now parses the previously-failing pnpm v11 monorepo lockfile (the original --frozen-lockfile error is gone; a separate runtime-specifier check now fires, unrelated to this PR)

Written by an agent (Claude Code, claude-opus-4-7).

Summary by CodeRabbit

Release Notes

  • New Features
    • Added comprehensive support for npm-alias dependencies in package resolution and lockfile management
    • Improved consistency in how aliased, regular, and linked dependencies are resolved across installation workflows

Review Change Stack

…sions

pnpm writes importer dependency `version:` fields in three shapes:
bare semver-with-peer (`4.0.0`), `link:<path>`, or — when a specifier
(typically `catalog:`) resolves to a different package name — the full
npm-alias `<name>@<version>`. The third shape is what
`refToRelative` recognises with the same leading-`@` / `@` before
`(`/`:` test that `SnapshotDepRef` already uses, and which pnpm v11
emits for entries like:

    js-yaml:
      specifier: 'catalog:'
      version: '@zkochan/js-yaml@0.0.11'

Pacquet's `ImporterDepVersion` only modelled `Regular` and `Link`, so
deserialising a lockfile with an aliased catalog dep failed with
"Failed to parse importer dependency version".

Add an `Alias(PkgNameVerPeer)` variant and a `resolved_key` helper
that returns the correct snapshot-map key for each shape — the
importer-map key paired with the version for `Regular`, the alias's
own `(name, suffix)` for `Alias`, and `None` for `Link`. Every site
that previously built a key from `as_regular().map(|v| PkgNameVerPeer::new(name, v))`
now goes through `resolved_key`, so aliased deps reach the snapshot,
the skipped-set, the reachability BFS, the build-sequence root walk,
the runtime exclusion check, and both hoist passes correctly.

For symlink targets, an aliased dep links the importer-key name to
`<slot>/node_modules/<alias-real-name>` (the resolved package's true
name inside its slot), matching pnpm's `linkDirectDeps`. The
`pnpm:root added` event now reports `realName` as the resolved
package name for aliases, where before it always echoed the
importer-map key.

Upstream reference: `refToRelative` in
`pnpm/pnpm@8a80235c7b/deps/path/src/index.ts:96-110`.
Copilot AI review requested due to automatic review settings May 14, 2026 12:10
@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The PR extends ImporterDepVersion with a new Alias variant representing npm-alias dependencies, adds parsing and serialization support, and refactors all downstream code that derives snapshot package keys to use the unified resolved_key() method instead of as_regular() plus manual construction.

Changes

npm-alias support in ImporterDepVersion and dependency key resolution

Layer / File(s) Summary
ImporterDepVersion enum with Alias variant and core methods
crates/lockfile/src/resolved_dependency.rs
ImporterDepVersion gains Alias(PkgNameVerPeer) variant. New as_alias() method and updated as_regular(), as_link_target(), resolved_key(), ver_peer() methods expose the alias distinction and compute snapshot keys correctly for all three dependency shapes.
Parsing, serialization, and type conversions
crates/lockfile/src/resolved_dependency.rs
FromStr implementation detects alias-shaped strings and parses them into Alias variant with alias-specific error handling. Serialization, Display, and From<PkgNameVerPeer> conversion updated to roundtrip Alias values correctly.
Test coverage for alias parsing and behavior
crates/lockfile/src/resolved_dependency/tests.rs
New tests validate parsing of scoped/unscoped aliases and aliases with peer suffixes, YAML roundtrip for aliased dependencies, and resolved_key() behavior across all three ImporterDepVersion variants.
Refactor dependency filtering to use resolved_key
crates/package-manager/src/build_sequence.rs, crates/package-manager/src/current_lockfile.rs, crates/package-manager/src/install_frozen_lockfile.rs
Dependency key computation in filtering, reachability, and runtime-snapshot-skipping paths refactored from as_regular() + manual construction to unified resolved_key() method, ensuring correct handling for all dependency shapes.
Refactor hoisted graph construction to use resolved_key
crates/package-manager/src/hoist.rs, crates/package-manager/src/hoisted_dep_graph.rs, crates/real-hoist/src/lib.rs
Graph construction key derivation refactored to resolved_key(). Documentation clarified that aliased importer deps now route through snapshot-dep resolution while link: deps remain skipped.
Direct dependency symlink setup with alias resolution
crates/package-manager/src/symlink_direct_dependencies.rs
Symlink filtering refactored to use resolved_key(). New Alias branch routes alias symlinks through layout.slot_dir(alias) targeting the resolved real package name. Event payloads now emit correct version and real_name for aliases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • pnpm/pacquet#438: Describes the need to handle npm-alias dependencies in the hoisted node-linker, which this PR implements by introducing ImporterDepVersion::Alias and updating all dependency key resolution paths.

Possibly related PRs

  • pnpm/pacquet#452: Both PRs update real-hoist to use resolved_key() for deriving snapshot lookup keys, directly overlapping at the same code site in collect_importer_deps.
  • pnpm/pacquet#495: Both PRs refactor filter_lockfile_for_current in current_lockfile.rs to compute PackageKey via resolved_key() for correct npm-alias snapshot mapping.
  • pnpm/pacquet#506: Both PRs modify the --no-runtime runtime snapshot filtering logic in install_frozen_lockfile.rs to use resolved_key() for candidate key computation.

Poem

🐰 A rabbit hops through alias trees,
Each dep now knows its rightful key—
No more forcing versions to regular form,
Resolved keys make the hotline norm!
Hoist and filter, symlink with care,
npm-aliases now handled everywhere! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(lockfile): support npm-alias importer dependency versions' is concise and directly describes the primary change—adding support for npm-alias shapes in importer dependency versions.
Description check ✅ Passed The PR description covers all required sections: a comprehensive summary of changes, linked upstream reference, and test plan evidence. However, the 'Linked issue' section required by the template is missing.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-yaml-parse

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@github-actions

Copy link
Copy Markdown

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.02      7.7±0.17ms   566.7 KB/sec    1.00      7.5±0.62ms   576.8 KB/sec

@codecov

codecov Bot commented May 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 67.21311% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.06%. Comparing base (110df76) to head (91e1d70).

Files with missing lines Patch % Lines
crates/lockfile/src/resolved_dependency.rs 63.41% 15 Missing ⚠️
...package-manager/src/symlink_direct_dependencies.rs 58.33% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #524      +/-   ##
==========================================
- Coverage   89.17%   89.06%   -0.11%     
==========================================
  Files         125      125              
  Lines       14220    14251      +31     
==========================================
+ Hits        12680    12693      +13     
- Misses       1540     1558      +18     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/package-manager/src/install_frozen_lockfile.rs (1)

461-477: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Look up runtime metadata by the peer-stripped key.

packages is keyed by the metadata key, not the full snapshot key. With the current lookup, a runtime dep that carries a peer suffix will miss its metadata row here and slip past --no-runtime.

Suggested fix
-                        if let Some(meta) = pkgs.get(&key)
+                        let metadata_key = key.without_peer();
+                        if let Some(meta) = pkgs.get(&metadata_key)
                             && matches!(
                                 &meta.resolution,
                                 pacquet_lockfile::LockfileResolution::Binary(_)
                                     | pacquet_lockfile::LockfileResolution::Variations(_),
                             )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/package-manager/src/install_frozen_lockfile.rs` around lines 461 -
477, The code is looking up package metadata in pkgs using the full snapshot key
(key) which may include peer suffixes, so pkgs.get(&key) can miss entries and
runtime deps bypass --no-runtime; instead compute a peer-stripped key (e.g.
derive meta_key by removing the peer/peer suffix from key) and use
pkgs.get(&meta_key) when checking meta in the if-let, while still using the
original snapshot key for skipped.add_optional_excluded(key) and the subsequent
logic; update the lookup around spec.version.resolved_key(alias) / key /
pkgs.get(...) accordingly to use the peer-stripped key for metadata lookup.
🧹 Nitpick comments (1)
crates/lockfile/src/resolved_dependency.rs (1)

157-175: ⚡ Quick win

Extract the alias-shape check into one shared helper.

looks_like_alias() duplicates the same discriminator SnapshotDepRef already uses. Keeping two copies of the dep-path classifier invites parser drift: the next edge-case fix can land in one path and silently desync the other.

As per coding guidelines, "Before implementing any non-trivial helper function, search the workspace for existing functions or utilities that do the same or similar thing" and "If logic you need already exists in another crate but is not exported, refactor it into a shared crate or move it to a utility crate rather than copy-pasting."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/lockfile/src/resolved_dependency.rs` around lines 157 - 175, Duplicate
alias-shape logic in looks_like_alias should be refactored into a single shared
helper used by both looks_like_alias and SnapshotDepRef; create a new utility
function (e.g., dep_path::is_alias or crate::utils::looks_like_alias_shared)
that implements the current classifier (leading '@' or an '@' before any '(' or
':'), export it from a common module accessible to both crates, replace the
local looks_like_alias and the duplicated classifier in SnapshotDepRef to call
the shared helper, and remove the duplicated code while preserving existing
behavior and tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/package-manager/src/install_frozen_lockfile.rs`:
- Around line 461-477: The code is looking up package metadata in pkgs using the
full snapshot key (key) which may include peer suffixes, so pkgs.get(&key) can
miss entries and runtime deps bypass --no-runtime; instead compute a
peer-stripped key (e.g. derive meta_key by removing the peer/peer suffix from
key) and use pkgs.get(&meta_key) when checking meta in the if-let, while still
using the original snapshot key for skipped.add_optional_excluded(key) and the
subsequent logic; update the lookup around spec.version.resolved_key(alias) /
key / pkgs.get(...) accordingly to use the peer-stripped key for metadata
lookup.

---

Nitpick comments:
In `@crates/lockfile/src/resolved_dependency.rs`:
- Around line 157-175: Duplicate alias-shape logic in looks_like_alias should be
refactored into a single shared helper used by both looks_like_alias and
SnapshotDepRef; create a new utility function (e.g., dep_path::is_alias or
crate::utils::looks_like_alias_shared) that implements the current classifier
(leading '@' or an '@' before any '(' or ':'), export it from a common module
accessible to both crates, replace the local looks_like_alias and the duplicated
classifier in SnapshotDepRef to call the shared helper, and remove the
duplicated code while preserving existing behavior and tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 98369aef-57be-4952-aa76-0d69868a636e

📥 Commits

Reviewing files that changed from the base of the PR and between 110df76 and 91e1d70.

📒 Files selected for processing (9)
  • crates/lockfile/src/resolved_dependency.rs
  • crates/lockfile/src/resolved_dependency/tests.rs
  • crates/package-manager/src/build_sequence.rs
  • crates/package-manager/src/current_lockfile.rs
  • crates/package-manager/src/hoist.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/real-hoist/src/lib.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: copilot-pull-request-reviewer
  • GitHub Check: Code Coverage
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Lint and Test (ubuntu-latest)
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Run benchmark on ubuntu-latest
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Preserve existing method chains and pipe-trait chains; do not break them into intermediate let bindings unless there is a concrete justification such as a compilation failure, borrow checker rejection, meaningful performance improvement, or other technical necessity. Refactoring for style alone is not sufficient justification.
Choose owned vs. borrowed parameters to minimize copies; prefer borrowed types (&Path over &PathBuf, &str over &String) when it does not force extra copies.
Prefer Arc::clone(&x) and Rc::clone(&x) over x.clone() for reference-counted types to make the cost visible at the call site.
Do not use star imports inside module bodies. Write use super::{Foo, bar} instead of use super::*; for any glob whose target is a module you control. External-crate preludes (e.g., use rayon::prelude::*;) and root-of-module re-exports (e.g., pub use submodule::*; in lib.rs) are exceptions.
Follow Rust API Guidelines for naming, as documented in https://rust-lang.github.io/api-guidelines/naming.html.
Declare a newtype wrapper for any branded string type being ported from TypeScript pnpm. Do not collapse the brand into a plain String or &str; give the type its own struct so misuse is a type error.
When porting branded string types where upstream TypeScript always validates before construction, validate in the Rust port too. Construct the wrapper only via TryFrom<String> and/or FromStr; do not provide an infallible public constructor that takes an arbitrary string.
For branded string types where upstream TypeScript never validates (used purely for type-safety to prevent confusion between string slots), expose an infallible From<String> and From<&str> constructor in the Rust wrapper.
When upstream TypeScript occasionally constructs a branded type without validation (via bare as assertion), add a from_str_unchecked (or similarly named) constructor on the Rust side. Keep the validating constructor as well; `from_str_u...

Files:

  • crates/package-manager/src/current_lockfile.rs
  • crates/lockfile/src/resolved_dependency/tests.rs
  • crates/real-hoist/src/lib.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/build_sequence.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/hoist.rs
  • crates/lockfile/src/resolved_dependency.rs
🧠 Learnings (5)
📚 Learning: 2026-05-07T23:19:08.272Z
Learnt from: KSXGitHub
Repo: pnpm/pacquet PR: 401
File: tasks/integrated-benchmark/src/work_env.rs:343-344
Timestamp: 2026-05-07T23:19:08.272Z
Learning: When reviewing Rust code in pnpm/pacquet for deprecated API usage, do not automatically treat `serde_saphyr::to_string` as deprecated. In `serde-saphyr` v0.0.25, `serde_saphyr::to_string` has no `#[deprecated]` attribute (the `#[deprecated]` later in `serde-saphyr-0.0.25/src/lib.rs` applies to a different function). Only flag `serde_saphyr::to_string` as deprecated if the resolved dependency version’s source shows `#[deprecated]` on that specific function.

Applied to files:

  • crates/package-manager/src/current_lockfile.rs
  • crates/lockfile/src/resolved_dependency/tests.rs
  • crates/real-hoist/src/lib.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/build_sequence.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/hoist.rs
  • crates/lockfile/src/resolved_dependency.rs
📚 Learning: 2026-05-13T22:52:32.579Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 506
File: crates/package-manager/src/link_bins.rs:238-241
Timestamp: 2026-05-13T22:52:32.579Z
Learning: In Rust code using `matches!` (or `match`) on a borrowed value (e.g., `meta: &PackageMetadata` and a field access like `meta.resolution`), it is safe to use wildcard/OR patterns such as `LockfileResolution::Binary(_) | LockfileResolution::Variations(_)` when the pattern does not bind the inner payload by value. `_` wildcard patterns (and similar discriminant-only patterns) should not move non-`Copy` data; they only test the variant and avoid ownership transfer. Do not flag these patterns as move-out-of-borrow/ownership compile errors. Only patterns that bind the inner value by value (e.g., `LockfileResolution::Binary(b)`) would require ownership and may trigger move errors when matching on data behind a borrow.

Applied to files:

  • crates/package-manager/src/current_lockfile.rs
  • crates/lockfile/src/resolved_dependency/tests.rs
  • crates/real-hoist/src/lib.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/build_sequence.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/hoist.rs
  • crates/lockfile/src/resolved_dependency.rs
📚 Learning: 2026-05-13T19:22:48.951Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 478
File: crates/package-manager/src/hoisted_dep_graph.rs:51-55
Timestamp: 2026-05-13T19:22:48.951Z
Learning: When reviewing this Rust codebase, avoid introducing/using a newtype like `PkgIdWithPatchHash` in only one module (e.g., `hoisted_dep_graph.rs`) if other related pacquet modules still represent the upstream pnpm “pkg id with patch hash” as a plain `String` (e.g., `virtual_store_layout`). For type consistency, either keep `pkg_id_with_patch_hash` as `String` here, or require a workspace-wide sweep that defines/extracts the `PkgIdWithPatchHash` newtype once and updates all call sites together (otherwise defer the refactor to a coordinated follow-up PR).

Applied to files:

  • crates/package-manager/src/current_lockfile.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/build_sequence.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/hoist.rs
📚 Learning: 2026-05-13T20:09:22.171Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 486
File: crates/package-manager/src/hoisted_dep_graph.rs:472-476
Timestamp: 2026-05-13T20:09:22.171Z
Learning: In pnpm/pacquet, when generating serialized `hoisted_locations` strings (used for `.modules.yaml`), normalize path separators to forward slashes (e.g., replace `\\` with `/`). This preserves cross-platform portability: the output must not use OS-native separators because upstream pnpm’s `path.relative` can return platform-specific separators, which would break consistency with pnpm/pacquet’s other serialized formats on Windows.

Applied to files:

  • crates/package-manager/src/current_lockfile.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/build_sequence.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/hoist.rs
📚 Learning: 2026-05-01T10:01:33.766Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 349
File: crates/reporter/src/tests.rs:121-121
Timestamp: 2026-05-01T10:01:33.766Z
Learning: In Rust test code, follow the repo’s CODE_STYLE_GUIDE test-logging rule: add logging (e.g., `eprintln!`/`eprintln!(...)`) so that useful diagnostic values are printed when a test fails, unless the assertion is `assert_eq!` (where the differing values are already included). Concretely, if you use assertions like `assert!`, `assert_ne!`, etc., ensure the test logs the relevant actual/expected values (or context) before/around the assertion so failures can be diagnosed without rerunning.

Applied to files:

  • crates/lockfile/src/resolved_dependency/tests.rs
🔇 Additional comments (3)
crates/package-manager/src/symlink_direct_dependencies.rs (3)

286-290: LGTM!

Also applies to: 384-388


432-442: LGTM!


493-509: LGTM!

@github-actions

Copy link
Copy Markdown

Integrated-Benchmark Report (Linux)

Scenario: Frozen Lockfile

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.104 ± 0.078 1.987 2.203 1.00
pacquet@main 2.116 ± 0.060 1.998 2.223 1.01 ± 0.05
pnpm 5.253 ± 0.066 5.130 5.334 2.50 ± 0.10
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.10383335702,
      "stddev": 0.07768764951656557,
      "median": 2.09595632002,
      "user": 2.71021158,
      "system": 2.1258466,
      "min": 1.98747116452,
      "max": 2.20310305752,
      "times": [
        2.1835280295199997,
        2.0630377105199997,
        2.04975676652,
        2.04435337452,
        2.12887492952,
        1.98747116452,
        2.19401812352,
        2.0312261165199996,
        2.20310305752,
        2.1529642975199996
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.11565507312,
      "stddev": 0.05963161149050938,
      "median": 2.12772454852,
      "user": 2.67356298,
      "system": 2.1114237,
      "min": 1.99849250452,
      "max": 2.22307523952,
      "times": [
        2.1419699845199998,
        1.99849250452,
        2.0508251545199996,
        2.22307523952,
        2.0995375845199997,
        2.12595055752,
        2.10850671452,
        2.1294985395199997,
        2.14678570752,
        2.13190874452
      ]
    },
    {
      "command": "pnpm",
      "mean": 5.25272633352,
      "stddev": 0.0657257778730102,
      "median": 5.26612816602,
      "user": 8.42044498,
      "system": 2.7222306,
      "min": 5.12955710452,
      "max": 5.33374910552,
      "times": [
        5.31404574352,
        5.211223324520001,
        5.29599789552,
        5.25631920152,
        5.24635003852,
        5.29661958352,
        5.16746420752,
        5.12955710452,
        5.33374910552,
        5.27593713052
      ]
    }
  ]
}

Scenario: Frozen Lockfile (Hot Cache)

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 512.9 ± 28.4 491.8 589.6 1.00
pacquet@main 521.2 ± 53.0 494.9 669.4 1.02 ± 0.12
pnpm 2181.6 ± 67.4 2103.7 2323.1 4.25 ± 0.27
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.51291967514,
      "stddev": 0.028443161145883866,
      "median": 0.5064603167399999,
      "user": 0.36409388,
      "system": 0.93864176,
      "min": 0.49177933974000004,
      "max": 0.58963413674,
      "times": [
        0.58963413674,
        0.51543580774,
        0.51339810674,
        0.5032491247399999,
        0.49815909974000006,
        0.49295029474,
        0.51646506074,
        0.49177933974000004,
        0.50967150874,
        0.49845427174000007
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.5211954433400001,
      "stddev": 0.053047773474671656,
      "median": 0.5011180747399999,
      "user": 0.35974078,
      "system": 0.9525933600000002,
      "min": 0.49492100774000003,
      "max": 0.66940975374,
      "times": [
        0.66940975374,
        0.49492100774000003,
        0.51070865574,
        0.51331359374,
        0.50026366474,
        0.49672461974000004,
        0.49753743674000006,
        0.49917050974000005,
        0.50197248474,
        0.52793270674
      ]
    },
    {
      "command": "pnpm",
      "mean": 2.1816456790400003,
      "stddev": 0.06735624930631078,
      "median": 2.18097251274,
      "user": 2.7463425799999994,
      "system": 1.2909600599999997,
      "min": 2.10373171074,
      "max": 2.32308117874,
      "times": [
        2.32308117874,
        2.10373171074,
        2.12023365774,
        2.23174467074,
        2.11917943474,
        2.15667987874,
        2.20655642074,
        2.13836199174,
        2.21162269974,
        2.20526514674
      ]
    }
  ]
}

@zkochan
zkochan merged commit 4138c70 into main May 14, 2026
13 of 17 checks passed
@zkochan
zkochan deleted the fix-yaml-parse branch May 14, 2026 12:31
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants