fix: preserve node runtime version prefix#12444
Conversation
📝 WalkthroughWalkthroughReplaces a hardcoded ChangesRuntime version prefix normalization
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoPreserve Node runtime version prefix when resolving runtime specs WalkthroughsDescription• Preserve ^/~ prefix when normalizing runtime: to a concrete Node version. • Apply the same normalization behavior in pacquet’s Rust resolver. • Add resolver tests and publish patch changesets for affected packages. Diagramgraph TD
A["WantedDependency"] --> B["Runtime resolver"] --> C["Parse channel/spec"] --> D{{"Node mirror index.json"}} --> E["Resolved version"] --> F["Normalize prefix (^/~)"] --> G["normalizedBareSpecifier"]
High-Level AssessmentThe following are alternative approaches to this PR: 1. Use semver range parsing to infer operator
2. Persist the desired range operator separately in lockfile metadata
Recommendation: The PR’s approach (preserve ^/~ by inspecting the current or previous runtime specifier, and keep prereleases exact) is the best low-risk fix: it restores expected semantics during runtime updates without expanding the spec surface area or changing lockfile formats. The semver-parsing alternative is attractive for future robustness but isn’t necessary for the reported prefix-regression and would increase risk. File ChangesBug fix (2)
Tests (2)
Other (1)
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
engine/runtime/node-resolver/test/resolveNodeRuntime.test.ts (1)
22-27: ⚡ Quick winAdd one prerelease normalization case for TS parity with Rust tests.
The new helper has an explicit prerelease passthrough branch, but this table doesn’t assert it on the TypeScript side. Adding a case like
runtime:^22 -> runtime:22.0.0-rc.0keeps pnpm/pacquet behavior checks symmetric and reduces regression risk.As per coding guidelines, “Keep pnpm (TypeScript) and pacquet (Rust) in sync … and add/update tests on both sides when applicable.”
🤖 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 `@engine/runtime/node-resolver/test/resolveNodeRuntime.test.ts` around lines 22 - 27, The test table in resolveNodeRuntime() test is missing a test case to assert the prerelease passthrough branch that exists in the helper implementation. Add a new test case to the test.each array that exercises prerelease normalization, such as a case that converts a runtime specifier like 'runtime:^22' with a previous specifier like 'runtime:22.0.0-rc.0' to verify the prerelease is preserved or normalized correctly. This ensures the TypeScript tests maintain parity with the Rust implementation and reduces regression risk when both implementations handle prerelease versions.Source: Coding guidelines
🤖 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.
Nitpick comments:
In `@engine/runtime/node-resolver/test/resolveNodeRuntime.test.ts`:
- Around line 22-27: The test table in resolveNodeRuntime() test is missing a
test case to assert the prerelease passthrough branch that exists in the helper
implementation. Add a new test case to the test.each array that exercises
prerelease normalization, such as a case that converts a runtime specifier like
'runtime:^22' with a previous specifier like 'runtime:22.0.0-rc.0' to verify the
prerelease is preserved or normalized correctly. This ensures the TypeScript
tests maintain parity with the Rust implementation and reduces regression risk
when both implementations handle prerelease versions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 4e8044db-2966-45cd-8ecd-04976605e474
📒 Files selected for processing (5)
.changeset/runtime-node-prefix.mdengine/runtime/node-resolver/src/index.tsengine/runtime/node-resolver/test/resolveNodeRuntime.test.tspacquet/crates/engine-runtime-node-resolver/src/node_resolver.rspacquet/crates/engine-runtime-node-resolver/src/node_resolver/tests.rs
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #12444 +/- ##
==========================================
- Coverage 88.00% 88.00% -0.01%
==========================================
Files 308 308
Lines 41395 41418 +23
==========================================
+ Hits 36431 36451 +20
- Misses 4964 4967 +3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Micro-Benchmark ResultsLinux |
Integrated-Benchmark Report (Linux)Each scenario reports direct installs and pnpr installs. Bencher consumes pacquet@HEAD and pnpr@HEAD. Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 4.12204743086,
"stddev": 0.16948799725773891,
"median": 4.03602751816,
"user": 3.87801706,
"system": 3.4525886,
"min": 3.99956174716,
"max": 4.49662909316,
"times": [
4.3539897311599995,
4.03325372816,
4.0214141861599995,
4.06495806216,
4.49662909316,
4.164621171159999,
4.02788581216,
4.03880130816,
3.99956174716,
4.019359469159999
]
},
{
"command": "pacquet@main",
"mean": 4.132607178359999,
"stddev": 0.12953962622719317,
"median": 4.059935040159999,
"user": 3.8680458599999996,
"system": 3.4400785,
"min": 4.0105474461599995,
"max": 4.29318770916,
"times": [
4.0357036081599995,
4.25182142316,
4.022197708159999,
4.01441695816,
4.28921681416,
4.08225351416,
4.29318770916,
4.0105474461599995,
4.03761656616,
4.289110036159999
]
},
{
"command": "pnpr@HEAD",
"mean": 2.1016443210600007,
"stddev": 0.07982615228632427,
"median": 2.0838319701600003,
"user": 2.6164102599999994,
"system": 2.9679127000000003,
"min": 1.99917084616,
"max": 2.20478614316,
"times": [
2.1804678771600003,
2.0068618431600003,
2.04065795516,
2.04922012016,
2.08038920816,
1.99917084616,
2.16990361516,
2.1977108701600003,
2.20478614316,
2.08727473216
]
},
{
"command": "pnpr@main",
"mean": 2.0825418263600004,
"stddev": 0.1378302820854578,
"median": 2.0545998586600005,
"user": 2.62797876,
"system": 2.9505222,
"min": 1.95247342816,
"max": 2.3497159561600003,
"times": [
2.3497159561600003,
2.0635239101600003,
2.0845943951600003,
2.0456758071600003,
2.06488931116,
1.9934657251599999,
2.00149037516,
2.30896938416,
1.95247342816,
1.96061997116
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.6163195486600002,
"stddev": 0.015173125801200988,
"median": 0.6097027688600001,
"user": 0.36093464,
"system": 1.31436178,
"min": 0.6000699513600001,
"max": 0.6364159113600001,
"times": [
0.60319172336,
0.63176813436,
0.63332466036,
0.63267312436,
0.6097085843600001,
0.6364159113600001,
0.6018939213600001,
0.60445252236,
0.6096969533600001,
0.6000699513600001
]
},
{
"command": "pacquet@main",
"mean": 0.61758483886,
"stddev": 0.009648677334539855,
"median": 0.6178849003600001,
"user": 0.37285363999999993,
"system": 1.30302028,
"min": 0.6020240453600001,
"max": 0.63208984936,
"times": [
0.60647864336,
0.6191997963600001,
0.61122592836,
0.61384574136,
0.62175811636,
0.6165700043600001,
0.6020240453600001,
0.63042876636,
0.63208984936,
0.62222749736
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6629789096600002,
"stddev": 0.014826409533653374,
"median": 0.66631209686,
"user": 0.37927214,
"system": 1.3310437799999997,
"min": 0.63662776036,
"max": 0.6814256323600001,
"times": [
0.6814256323600001,
0.66478821736,
0.6754773903600001,
0.67568172636,
0.63662776036,
0.66783597636,
0.6733659333600001,
0.6579433073600001,
0.6440568093600001,
0.6525863433600001
]
},
{
"command": "pnpr@main",
"mean": 0.6696020959600001,
"stddev": 0.020322669726845197,
"median": 0.67253923136,
"user": 0.39083284,
"system": 1.3265997799999998,
"min": 0.6417239703600001,
"max": 0.7045950333600001,
"times": [
0.7045950333600001,
0.6511344683600001,
0.67375484636,
0.67132361636,
0.6759681993600001,
0.6952854913600001,
0.6417239703600001,
0.66182139636,
0.6746447693600001,
0.6457691683600001
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 4.21211688732,
"stddev": 0.028391970441932347,
"median": 4.2070024856199995,
"user": 3.67236374,
"system": 3.3327390799999996,
"min": 4.17544303212,
"max": 4.26020259212,
"times": [
4.23947858912,
4.17544303212,
4.2188587561199995,
4.1920992221199995,
4.2432578441199995,
4.195146215119999,
4.21922848212,
4.18827874812,
4.26020259212,
4.18917539212
]
},
{
"command": "pacquet@main",
"mean": 4.208414186120001,
"stddev": 0.02921535052583037,
"median": 4.19388478462,
"user": 3.6868436399999993,
"system": 3.30312968,
"min": 4.175276763119999,
"max": 4.25660310512,
"times": [
4.21754749312,
4.235761904119999,
4.1871444771199995,
4.2489921301199995,
4.184186739119999,
4.1965255911199995,
4.175276763119999,
4.19085968012,
4.25660310512,
4.19124397812
]
},
{
"command": "pnpr@HEAD",
"mean": 2.17757369992,
"stddev": 0.11307927680209601,
"median": 2.1311549576199997,
"user": 2.42973574,
"system": 2.88738358,
"min": 2.04081754912,
"max": 2.36203977512,
"times": [
2.3584270951199997,
2.09906941012,
2.04081754912,
2.1011619851199996,
2.24420528312,
2.21169334412,
2.36203977512,
2.09601264212,
2.10794741312,
2.1543625021199997
]
},
{
"command": "pnpr@main",
"mean": 2.1790679729199995,
"stddev": 0.12835543664836827,
"median": 2.14701199062,
"user": 2.43645024,
"system": 2.843428479999999,
"min": 1.9938774841200002,
"max": 2.3916514961199997,
"times": [
2.28784469312,
2.1715882451199997,
2.08567594012,
2.12243573612,
2.35434588512,
2.3916514961199997,
2.1128724591199997,
1.9938774841200002,
2.08034352412,
2.1900442661199997
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.31739445652,
"stddev": 0.019057621064902327,
"median": 1.31473462532,
"user": 1.3205543599999998,
"system": 1.69584692,
"min": 1.29076706182,
"max": 1.3495580548200001,
"times": [
1.30483131882,
1.32410962482,
1.29076706182,
1.31343131182,
1.3495580548200001,
1.34669368282,
1.3195835548200001,
1.31603793882,
1.31178796682,
1.29714404982
]
},
{
"command": "pacquet@main",
"mean": 1.35626702932,
"stddev": 0.09385739133442006,
"median": 1.33255700032,
"user": 1.3220448599999999,
"system": 1.7115994199999995,
"min": 1.29099974682,
"max": 1.61009342982,
"times": [
1.29983000782,
1.61009342982,
1.38736589682,
1.34013523582,
1.3564454348200001,
1.3007226088200001,
1.33027579182,
1.29099974682,
1.3348382088200001,
1.31196393182
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6689695928200001,
"stddev": 0.08758975144157133,
"median": 0.6376567038200001,
"user": 0.3225808600000001,
"system": 1.28666462,
"min": 0.6268820538200001,
"max": 0.9107714848200001,
"times": [
0.6271210048200001,
0.6371350898200001,
0.6268820538200001,
0.6414312178200001,
0.6300203778200001,
0.9107714848200001,
0.7004524178200001,
0.6361492158200001,
0.6381783178200001,
0.6415547478200001
]
},
{
"command": "pnpr@main",
"mean": 0.64802231312,
"stddev": 0.044657290276050816,
"median": 0.6336884118200001,
"user": 0.33132945999999996,
"system": 1.2604000199999998,
"min": 0.6259186478200001,
"max": 0.7742288998200001,
"times": [
0.6406799378200001,
0.63075985582,
0.6323687708200001,
0.64166382982,
0.6327892248200001,
0.6393853758200001,
0.62784098982,
0.63458759882,
0.6259186478200001,
0.7742288998200001
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.9564880736,
"stddev": 0.032017363728516296,
"median": 2.9474773718,
"user": 1.7493315200000001,
"system": 1.9322313599999998,
"min": 2.9206335463,
"max": 3.0324774163,
"times": [
2.9386819353,
2.9560926163000003,
2.9437856743,
2.9489704893,
2.9748275733,
2.9755750753,
2.9206335463,
2.9459842543,
3.0324774163,
2.9278521553
]
},
{
"command": "pacquet@main",
"mean": 2.943877979,
"stddev": 0.02438211882829861,
"median": 2.9464362558,
"user": 1.72714512,
"system": 1.9447617599999998,
"min": 2.9028207333,
"max": 2.9942409273,
"times": [
2.9426092283000003,
2.9532018733000003,
2.9518629923,
2.9028207333,
2.9470908393,
2.9141195233,
2.9373534673,
2.9496985333000003,
2.9457816723,
2.9942409273
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6396687833000001,
"stddev": 0.0138759898635695,
"median": 0.6362522513,
"user": 0.32105471999999996,
"system": 1.2828138599999996,
"min": 0.6274865333,
"max": 0.6702573473,
"times": [
0.6702573473,
0.6274865333,
0.6282670993,
0.6575603143000001,
0.6309661953,
0.6339670643,
0.6303094583000001,
0.6387755643,
0.6385374383,
0.6405608183
]
},
{
"command": "pnpr@main",
"mean": 0.6280583775,
"stddev": 0.008764671770217015,
"median": 0.6253685978,
"user": 0.32329952,
"system": 1.26702396,
"min": 0.6195035353,
"max": 0.6446416243,
"times": [
0.6366054683,
0.6446416243,
0.6215873893,
0.6195035353,
0.6250159943,
0.6271498763000001,
0.6197613473,
0.6221182963,
0.6384790423000001,
0.6257212013
]
}
]
} |
|
| Branch | pr/12444 |
| Testbed | pacquet |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 4,212.12 ms(+1.33%)Baseline: 4,156.84 ms | 4,988.21 ms (84.44%) |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot 🚷 view threshold | 2,956.49 ms(-0.86%)Baseline: 2,982.13 ms | 3,578.55 ms (82.62%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,317.39 ms(+1.08%)Baseline: 1,303.33 ms | 1,563.99 ms (84.23%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 4,122.05 ms(+4.78%)Baseline: 3,933.93 ms | 4,720.71 ms (87.32%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 616.32 ms(-0.24%)Baseline: 617.81 ms | 741.37 ms (83.13%) |
|
| Branch | pr/12444 |
| Testbed | pnpr |
⚠️ WARNING: No Threshold found!Without a Threshold, no Alerts will ever be generated.
Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the--ci-only-thresholdsflag.
Click to view all benchmark results
| Benchmark | Latency | milliseconds (ms) |
|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot | 2,177.57 ms |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot | 639.67 ms |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot | 668.97 ms |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot | 2,101.64 ms |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot | 662.98 ms |
Summary
node@runtime:<range>so exact specs do not become caret ranges during runtime updates.@pnpm/engine.runtime.node-resolverandpnpm.Tests
pnpm --filter @pnpm/engine.runtime.node-resolver test resolveNodeRuntime.test.tscargo fmt --check --package pacquet-engine-runtime-node-resolvercargo test -p pacquet-engine-runtime-node-resolver normalized_runtime_spec_preserves_version_prefixcargo fmt --all -- --check,cargo clippy --all-targets --workspace -- -D warnings,RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --workspace --all-features,RUSTFLAGS="-D warnings" cargo dylint --all -- --all-targets --workspace,taplo format --checkRelated: #12441
Written by an agent (Codex, GPT-5).
Summary by CodeRabbit
Bug Fixes
^,~, andruntime:formats) when resolving versions to concrete implementations, ensuring version constraints remain properly maintained across all resolution scenarios and dependencies.Tests