Conversation
Add a server-side resolve-only mode so `--lockfile-only` (and the `lockfileOnly` setting) is honored when a pnprServer / agent is configured: resolve and write the lockfile, fetch nothing, link nothing. The pnpr server skips the tarball fetch and file diff when the request sets `lockfileOnly`, returning just the lockfile. The pnpr client and the TypeScript agent client forward the flag and ignore any file/index lines an older server still streams, keeping the store untouched. The pacquet CLI no longer errors and stops after writing the lockfile. Closes #12146
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📜 Recent 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). (9)
🧰 Additional context used📓 Path-based instructions (1)**/*.{ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (1)📚 Learning: 2026-05-14T09:04:00.133ZApplied to files:
🔇 Additional comments (1)
📝 WalkthroughWalkthroughThis PR implements ChangesLockfile-only install across pnpr, pacquet, and pnpm agent
Sequence Diagram(s)sequenceDiagram
participant Caller
participant FetchFromPnpmRegistry
participant AgentServer
participant StreamHandler
Caller->>FetchFromPnpmRegistry: opts.lockfileOnly=true
FetchFromPnpmRegistry->>AgentServer: {lockfileOnly: true, ...}
AgentServer->>StreamHandler: resolve packages, stream L/D/I lines
StreamHandler->>StreamHandler: if lockfileOnly: skip D (digest)
StreamHandler->>StreamHandler: if lockfileOnly: skip I (index)
StreamHandler->>FetchFromPnpmRegistry: return L (lockfile) only
FetchFromPnpmRegistry->>Caller: lockfile, empty indexEntries
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
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 |
Micro-Benchmark ResultsLinux |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #12148 +/- ##
=======================================
Coverage 87.47% 87.48%
=======================================
Files 262 262
Lines 29903 29917 +14
=======================================
+ Hits 26158 26172 +14
Misses 3745 3745 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Review Summary by QodoSupport
WalkthroughsDescription• Support --lockfile-only flag on pnpr/agent install path • Server skips tarball fetch and file diff when lockfileOnly is set • Client returns lockfile immediately, skips materialization • Backward compatible: older servers still work, client ignores D/I lines Diagramflowchart LR
A["Client requests install<br/>with lockfileOnly flag"] -->|sends request| B["pnpr server"]
B -->|lockfileOnly=true| C["Resolve dependencies"]
C -->|skip fetch & diff| D["Return lockfile only<br/>no D/I lines"]
D -->|receives lockfile| E["Write pnpm-lock.yaml"]
E -->|stop here| F["Skip materialization<br/>no node_modules"]
B -->|lockfileOnly=false| G["Fetch tarballs<br/>compute diff"]
G -->|stream D/I lines| H["Download files<br/>link node_modules"]
File Changes1. pacquet/crates/cli/src/cli_args/install.rs
|
There was a problem hiding this comment.
Pull request overview
This PR adds faithful --lockfile-only / lockfileOnly support to the pnpr/agent “install via server” fast path, matching pnpm’s behavior: resolve and write pnpm-lock.yaml, but fetch nothing into the store and link nothing into node_modules.
Changes:
- Adds a
lockfileOnlyrequest flag to the Rust pnpr server and uses it to return only the finalLNDJSON line (noD/Istreaming). - Updates Rust and TypeScript clients to forward
lockfileOnlyand defensively avoid any store materialization (including ignoringD/Ilines client-side). - Adds integration/e2e tests covering “lockfile resolved and written, store untouched, node_modules absent” across Rust and TS paths, plus a changeset.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pnpr/crates/pnpr/src/install_accelerator/protocol.rs | Extends /v1/install request schema with lockfileOnly (camelCase) for resolve-only mode. |
| pnpr/crates/pnpr/src/install_accelerator.rs | Implements server-side resolve-only flow by skipping tarball fetch + diff computation and returning only the lockfile line. |
| pacquet/crates/pnpr-client/src/lib.rs | Forwards lockfileOnly and adds a client-side backstop to return early without touching the store. |
| pacquet/crates/pnpr-client/tests/integration.rs | Adds an integration test asserting lockfile-only resolves without writing files/index entries. |
| pacquet/crates/cli/src/cli_args/install.rs | Threads --lockfile-only through the pnpr path and stops after writing the lockfile. |
| pacquet/crates/cli/tests/pnpr_install.rs | Adds an e2e test asserting lockfile-only writes lockfile and does not create node_modules or populate the client store. |
| agent/client/src/fetchFromPnpmRegistry.ts | Adds lockfileOnly option and ignores D/I lines to keep the client store untouched against older servers. |
| agent/server/test/integration.ts | Adds an integration test verifying lockfile-only does not write CAFS files or index entries client-side. |
| installing/deps-installer/src/install/index.ts | Forwards lockfileOnly to the agent client and returns early after writing the lockfile (skipping headless install). |
| .changeset/pnpr-lockfile-only.md | Publishes release notes/versions for the agent client and deps-installer changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
pacquet/crates/cli/src/cli_args/install.rs (1)
502-518:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAlways persist the resolved lockfile before the lockfile-only early return.
Line 502 still gates
save_to_path()onstate.config.lockfile, but Lines 516-518 return immediately for--lockfile-only. If lockfile writing is disabled in config, this path resolves and exits without creatingpnpm-lock.yaml, which breaks the flag’s resolve-and-write contract.Suggested fix
- if state.config.lockfile { + if state.config.lockfile || link.lockfile_only { let lockfile_dir = state.manifest.path().parent().expect("manifest path always has a parent dir"); outcome .lockfile .save_to_path(&lockfile_dir.join(Lockfile::FILE_NAME)) .map_err(|err| miette::miette!("{err}")) .wrap_err("writing the pnpr-resolved lockfile")?; }Based on learnings: only match pnpm’s behavior exactly.
🤖 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 `@pacquet/crates/cli/src/cli_args/install.rs` around lines 502 - 518, The current logic only calls outcome.lockfile.save_to_path when state.config.lockfile is true, but returns early on link.lockfile_only before writing when the config disables lockfile, violating the --lockfile-only contract; change the control flow so that when link.lockfile_only is set you always persist the resolved lockfile (call outcome.lockfile.save_to_path(&lockfile_dir.join(Lockfile::FILE_NAME)) and wrap_err as before) regardless of state.config.lockfile, then return Ok(()); keep the original behavior for non‑lockfile‑only runs by still gating save_to_path on state.config.lockfile.pacquet/crates/pnpr-client/src/lib.rs (1)
198-224:⚠️ Potential issue | 🟠 Major | ⚡ Quick winSkip opening the local store in resolve-only mode.
Line 198 still reads
index.dbto buildstoreIntegritieseven whenopts.lockfile_onlyis set. That breaks the lockfile-only “don’t touch the local store” contract and adds unnecessary local I/O on the resolve-only path.Suggested fix
- let store_keys = read_store_keys(opts.store_dir); - let store_integrities = integrities_from_keys(&store_keys); - let present: HashSet<&str> = store_keys.iter().map(String::as_str).collect(); + let store_keys = (!opts.lockfile_only).then(|| read_store_keys(opts.store_dir)); + let store_integrities = + store_keys.as_deref().map(integrities_from_keys).unwrap_or_default(); @@ let parsed = parse_install_response(&ndjson)?; @@ if opts.lockfile_only { return Ok(InstallOutcome { lockfile: parsed.lockfile, stats: parsed.stats, files_written: 0, index_entries_written: 0, }); } + + let present: HashSet<&str> = store_keys + .as_ref() + .map(|keys| keys.iter().map(String::as_str).collect()) + .unwrap_or_default();🤖 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 `@pacquet/crates/pnpr-client/src/lib.rs` around lines 198 - 224, The code always calls read_store_keys and integrities_from_keys to populate storeIntegrities even when opts.lockfile_only is true; change it so that when opts.lockfile_only is set you do NOT call read_store_keys/integrities_from_keys (avoid opening index.db) and instead set store_integrities to an empty/neutral value (e.g., empty vec/map) used in the JSON request; locate the calls to read_store_keys and integrities_from_keys and the variable store_integrities created before building the serde_json::json! request and short-circuit that logic based on opts.lockfile_only.installing/deps-installer/src/install/index.ts (1)
2409-2412:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAvoid logging downloads that won't happen.
This message still reports
${agentStats.filesToDownload} files to download, but theopts.lockfileOnlybranch below skips fetching entirely. With the unchanged TS agent server, that count can be non-zero here, so the user-facing summary becomes misleading.Proposed fix
logger.info({ - message: `Resolved ${agentStats.totalPackages} packages: ${agentStats.alreadyInStore} cached, ${agentStats.filesToDownload} files to download`, + message: opts.lockfileOnly + ? `Resolved ${agentStats.totalPackages} packages and wrote the lockfile` + : `Resolved ${agentStats.totalPackages} packages: ${agentStats.alreadyInStore} cached, ${agentStats.filesToDownload} files to download`, prefix: rootDir, })🤖 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 `@installing/deps-installer/src/install/index.ts` around lines 2409 - 2412, The log message using logger.info currently reports agentStats.filesToDownload even when opts.lockfileOnly short-circuits fetching; update the message to reflect the lockfile-only mode by setting the displayed files-to-download to 0 when opts.lockfileOnly is true (or compute a local variable like effectiveFilesToDownload = opts.lockfileOnly ? 0 : agentStats.filesToDownload) and use that in the logger.info call (same call site that references agentStats.totalPackages, agentStats.alreadyInStore, and prefix rootDir) so the user-facing summary isn't misleading.
🤖 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.
Inline comments:
In `@installing/deps-installer/src/install/index.ts`:
- Around line 2417-2425: The early-return for opts.lockfileOnly exits before
observing the fileDownloads promise returned by fetchFromPnpmRegistry(), risking
unhandled rejections; before returning in the lockfile-only branch, explicitly
consume the fileDownloads promise (e.g., await it or await
fileDownloads.catch(() => { /* propagate or log */ })) so any rejection is
observed, then proceed to return updatedManifest/lockfile/etc.; target the
branch guarded by opts.lockfileOnly and the fileDownloads variable created by
fetchFromPnpmRegistry().
---
Outside diff comments:
In `@installing/deps-installer/src/install/index.ts`:
- Around line 2409-2412: The log message using logger.info currently reports
agentStats.filesToDownload even when opts.lockfileOnly short-circuits fetching;
update the message to reflect the lockfile-only mode by setting the displayed
files-to-download to 0 when opts.lockfileOnly is true (or compute a local
variable like effectiveFilesToDownload = opts.lockfileOnly ? 0 :
agentStats.filesToDownload) and use that in the logger.info call (same call site
that references agentStats.totalPackages, agentStats.alreadyInStore, and prefix
rootDir) so the user-facing summary isn't misleading.
In `@pacquet/crates/cli/src/cli_args/install.rs`:
- Around line 502-518: The current logic only calls
outcome.lockfile.save_to_path when state.config.lockfile is true, but returns
early on link.lockfile_only before writing when the config disables lockfile,
violating the --lockfile-only contract; change the control flow so that when
link.lockfile_only is set you always persist the resolved lockfile (call
outcome.lockfile.save_to_path(&lockfile_dir.join(Lockfile::FILE_NAME)) and
wrap_err as before) regardless of state.config.lockfile, then return Ok(());
keep the original behavior for non‑lockfile‑only runs by still gating
save_to_path on state.config.lockfile.
In `@pacquet/crates/pnpr-client/src/lib.rs`:
- Around line 198-224: The code always calls read_store_keys and
integrities_from_keys to populate storeIntegrities even when opts.lockfile_only
is true; change it so that when opts.lockfile_only is set you do NOT call
read_store_keys/integrities_from_keys (avoid opening index.db) and instead set
store_integrities to an empty/neutral value (e.g., empty vec/map) used in the
JSON request; locate the calls to read_store_keys and integrities_from_keys and
the variable store_integrities created before building the serde_json::json!
request and short-circuit that logic based on opts.lockfile_only.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: adf444a7-70d0-412d-80ca-40c281444360
📒 Files selected for processing (10)
.changeset/pnpr-lockfile-only.mdagent/client/src/fetchFromPnpmRegistry.tsagent/server/test/integration.tsinstalling/deps-installer/src/install/index.tspacquet/crates/cli/src/cli_args/install.rspacquet/crates/cli/tests/pnpr_install.rspacquet/crates/pnpr-client/src/lib.rspacquet/crates/pnpr-client/tests/integration.rspnpr/crates/pnpr/src/install_accelerator.rspnpr/crates/pnpr/src/install_accelerator/protocol.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). (4)
- GitHub Check: Agent
- GitHub Check: ubuntu-latest / Node.js 24 / Test
- GitHub Check: Run benchmark on ubuntu-latest
- GitHub Check: Lint and Test (windows-latest)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Follow Standard Style with trailing commas, preferring functions over classes, and declaring functions after they are used (relying on hoisting)
Use a single options object instead of multiple parameters when a function needs more than two or three arguments
Follow Import Order: Standard libraries first, then external dependencies (alphabetically), then relative imports
Write self-documenting code where function names, parameters, and types explain what a function does without requiring prose comments
Do not write comments that restate what the code already says; refactor via renaming, splitting helpers, or restructuring instead
Do not repeat documentation at call sites that already exists in JSDoc on the callee; update JSDoc once for all call sites to benefit
Use JSDoc only for a function's contract (preconditions, postconditions, edge cases, why the function exists), not for re-narrating the body
Do not record past implementation shape, refactor history, or 'the previous code did X' framing in code; use git log and git blame instead
Write comments only when: the reason for code is non-obvious (hidden invariant, workaround for known bug, deliberate exception), or the right name doesn't fit (temporary technical constraint)
Files:
agent/server/test/integration.tsagent/client/src/fetchFromPnpmRegistry.tsinstalling/deps-installer/src/install/index.ts
pacquet/**/*.rs
📄 CodeRabbit inference engine (pacquet/AGENTS.md)
pacquet/**/*.rs: Log emissions are part of matching pnpm — when porting a function that firespnpm:<channel>events throughglobalLogger,logger.debug(...), orstreamParser.write(...), mirror the call site, payload, and ordering so@pnpm/cli.default-reporterparses pacquet's NDJSON the same way
Declare a newtype wrapper for branded string types instead of collapsing the brand into a plainStringor&strin Rust
If upstream TypeScript always validates before construction of a branded string, validate in the Rust wrapper too viaTryFrom<String>and/orFromStrand do not provide an infallible public constructor
If upstream TypeScript never validates a branded string, just brand for type-safety in Rust by exposing an infallibleFrom<String>constructor
If upstream TypeScript occasionally constructs a branded string without validation, exposefrom_str_uncheckedin Rust as an escape hatch alongside the validating constructor
Match upstream serde behavior for branded strings crossing JSON, YAML, or INI boundaries by using#[serde(try_from = "String")]for deserialization and#[serde(into = "String")]for serialization
Derive simple conversions for branded strings using#[derive(derive_more::From)]and#[derive(derive_more::Into)]instead of handwritingimplblocks; use manualimplonly when conversion needs custom logic
Model TypeScript string literal unions (like'auto' | 'always' | 'never') as Rustenums instead of newtype wrappers, since the set of valid values is closed
Treat TypeScript string template literal types (like`${string}@${string}`) the same as branded string types in Rust, using a newtype wrapper with validation
Follow the code style guide inCODE_STYLE_GUIDE.md— imports, modules, naming, ownership and borrowing, parameter type selection, trait bounds, pattern matching,pipe-trait, error handling, test layout, and cloning ofArcandRc
Choose owned vs. borrowed parameters to minimize copies; widen to t...
Files:
pacquet/crates/cli/tests/pnpr_install.rspacquet/crates/pnpr-client/tests/integration.rspacquet/crates/pnpr-client/src/lib.rspacquet/crates/cli/src/cli_args/install.rs
pacquet/**/tests/**/*.rs
📄 CodeRabbit inference engine (pacquet/AGENTS.md)
pacquet/**/tests/**/*.rs: Prefer real fixtures over dependency-injection seams — usetempfile::TempDir, the mocked registry, or integration tests spawning the actual binary for happy paths and most error paths; use the DI seam only for filesystem error kinds, deterministic time, shared process-global state, or external-service happy paths
Follow test-logging guidance — log before non-assert_eq!assertions,dbg!complex structures, skip logging for simple scalarassert_eq!
Tests must not be tolerant of missing build/runtime environment by silently returning early when a tool isn't found — let the test panic when a required tool is absent
Prefer#[cfg_attr(target_os = "windows", ignore = "...")](or matching#[cfg(unix)]gates) over runtime probe-and-skip helpers for platform-locked tools
Port relevant pnpm tests to Rust tests whenever they translate when porting behavior from pnpm
Use snapshot tests withinstaand carefully review diffs when intentional changes alter snapshots; accept withcargo insta reviewonly after careful review
Tests that need the mocked registry should startpnprthroughpacquet-testing-utils;cargo test/cargo nextest runshould not require a separatejust registry-mock launchstep
Files:
pacquet/crates/cli/tests/pnpr_install.rspacquet/crates/pnpr-client/tests/integration.rs
pnpr/**/pnpr/**/*.rs
📄 CodeRabbit inference engine (pnpr/AGENTS.md)
pnpr/**/pnpr/**/*.rs: Follow the pacquet code-style guide (../pacquet/CODE_STYLE_GUIDE.md) for Rust-level conventions including imports, naming, ownership, and error handling
Follow the pacquet contributing guide (../pacquet/CONTRIBUTING.md) for test layout and Rust conventions
Files:
pnpr/crates/pnpr/src/install_accelerator/protocol.rspnpr/crates/pnpr/src/install_accelerator.rs
🧠 Learnings (5)
📚 Learning: 2026-05-14T09:04:00.133Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11622
File: resolving/npm-resolver/test/publishedBy.test.ts:350-354
Timestamp: 2026-05-14T09:04:00.133Z
Learning: In the pnpm/pnpm repository, ESLint is the authoritative style linter. Do not raise review findings for missing trailing commas in multiline function calls (e.g., `fs.writeFileSync(...)`) when this repo’s ESLint configuration does not report them and lint passes. Prefer deferring to the ESLint results for this specific trailing-comma rule rather than enforcing it manually in code review.
Applied to files:
agent/server/test/integration.tsagent/client/src/fetchFromPnpmRegistry.tsinstalling/deps-installer/src/install/index.ts
📚 Learning: 2026-05-20T19:40:55.051Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11774
File: pacquet/crates/resolving-deps-resolver/src/resolve_peers.rs:0-0
Timestamp: 2026-05-20T19:40:55.051Z
Learning: In the pacquet Rust code, ensure the semver implementation uses the `node-semver` crate (not `nodejs-semver`). `node-semver`’s public API does not include a `satisfies_with_prerelease`-style method; prerelease-tolerant matching should be implemented inline by first calling `Range::satisfies`, and when it rejects a prerelease version, retry matching against a stripped `MAJOR.MINOR.PATCH` base of the prerelease version.
Applied to files:
pacquet/crates/cli/tests/pnpr_install.rspacquet/crates/pnpr-client/tests/integration.rspacquet/crates/pnpr-client/src/lib.rspacquet/crates/cli/src/cli_args/install.rs
📚 Learning: 2026-05-22T00:08:44.646Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11837
File: pacquet/crates/resolving-npm-resolver/src/pick_package.rs:33-51
Timestamp: 2026-05-22T00:08:44.646Z
Learning: In the pnpm/pnpm repo’s pacquet Rust crates, do not flag Unicode ellipsis characters (U+2026, `…`) in Rust doc comments (`///` / `/** */`) as a lint violation. The pacquet crate’s `dylint.toml` only enables `perfectionist::derive_ordering`, and the Dylint `unicode-ellipsis` rule is not enabled for this project—so `…` in doc comments is an intentional, repo-consistent style.
Applied to files:
pacquet/crates/cli/tests/pnpr_install.rspacquet/crates/pnpr-client/tests/integration.rspacquet/crates/pnpr-client/src/lib.rspacquet/crates/cli/src/cli_args/install.rs
📚 Learning: 2026-05-20T23:07:58.444Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11784
File: pacquet/crates/resolving-deps-resolver/src/hoist_peers.rs:120-133
Timestamp: 2026-05-20T23:07:58.444Z
Learning: When reviewing code in this pacquet Rust port, follow the upstream pnpm compatibility rule: only match pnpm’s behavior exactly. Do not propose review changes that intentionally deviate from pnpm’s documented/observed behavior, even if pnpm appears buggy. If you identify a real bug in pnpm behavior, the review should prioritize fixing it upstream in pnpm first, and avoid implementing a pnpm-behavior workaround here unless the same fix has already landed upstream.
Applied to files:
pacquet/crates/cli/tests/pnpr_install.rspacquet/crates/pnpr-client/tests/integration.rspacquet/crates/pnpr-client/src/lib.rspacquet/crates/cli/src/cli_args/install.rs
📚 Learning: 2026-05-26T21:01:06.666Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11966
File: .changeset/require-tarball-integrity.md:6-6
Timestamp: 2026-05-26T21:01:06.666Z
Learning: In pnpm lockfile-related release notes/docs (especially changeset markdown), preserve URL hostnames exactly as they appear in pnpm-lock.yaml tarball resolution entries—keep hosts like `codeload.github.com`, `bitbucket.org`, and `gitlab.com` in lowercase. Do not “correct” them to title-case/preserve brand capitalization (e.g., LanguageTool rules like `GITHUB` capitalization) because these are literal URL fragments, not platform brand names.
Applied to files:
.changeset/pnpr-lockfile-only.md
🔇 Additional comments (4)
.changeset/pnpr-lockfile-only.md (1)
1-7: LGTM!pnpr/crates/pnpr/src/install_accelerator/protocol.rs (1)
69-76: LGTM!pnpr/crates/pnpr/src/install_accelerator.rs (1)
213-246: LGTM!pacquet/crates/pnpr-client/tests/integration.rs (1)
69-69: LGTM!Also applies to: 116-145
Integrated-Benchmark Report (Linux)Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.06747756236,
"stddev": 0.06490745653052755,
"median": 2.0580135992599997,
"user": 2.66473662,
"system": 3.2600389199999995,
"min": 1.98319618926,
"max": 2.18066553226,
"times": [
1.98319618926,
2.01410593426,
2.14817842026,
2.0579081002599997,
2.0581190982599997,
2.07925413626,
2.1178257392599997,
2.03831078126,
1.99721169226,
2.18066553226
]
},
{
"command": "pacquet@main",
"mean": 2.00716057136,
"stddev": 0.0718304372009241,
"median": 1.98174401426,
"user": 2.6547695199999994,
"system": 3.22360922,
"min": 1.94326843326,
"max": 2.14933264126,
"times": [
2.03828279126,
1.94385794226,
1.94326843326,
1.9740576652600001,
2.00794579126,
1.96656887326,
1.98943036326,
2.11040965626,
2.14933264126,
1.94845155626
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.661841911,
"stddev": 0.01387617410992863,
"median": 0.6609357601,
"user": 0.3726969400000001,
"system": 1.3336962199999998,
"min": 0.6426706506000001,
"max": 0.6866095536,
"times": [
0.6866095536,
0.6426706506000001,
0.6762948226,
0.6468306386,
0.6646435866,
0.6574204456,
0.6731658686,
0.6516231276,
0.6644510746,
0.6547093416
]
},
{
"command": "pacquet@main",
"mean": 0.6686322732000001,
"stddev": 0.04022184846490968,
"median": 0.6627268941,
"user": 0.34213844,
"system": 1.3283613199999997,
"min": 0.6399780446,
"max": 0.7778274106,
"times": [
0.7778274106,
0.6720127216,
0.6617160636,
0.6671729856,
0.6402048866,
0.6534939726,
0.6399780446,
0.6637377246,
0.6423206806,
0.6678582416
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.34448027048,
"stddev": 0.025178531862936532,
"median": 2.34418526548,
"user": 3.8256716799999992,
"system": 3.0224516199999996,
"min": 2.29695881098,
"max": 2.37791400498,
"times": [
2.35306427798,
2.37791400498,
2.29695881098,
2.35790787498,
2.33138348298,
2.35967726398,
2.3767403059800003,
2.32776698898,
2.33530625298,
2.32808344098
]
},
{
"command": "pacquet@main",
"mean": 2.2939859944800007,
"stddev": 0.02556010807404355,
"median": 2.30148381548,
"user": 3.76840148,
"system": 3.0120445200000003,
"min": 2.2558643539800003,
"max": 2.3418468159800003,
"times": [
2.28307019398,
2.30789961298,
2.3008499819800003,
2.3418468159800003,
2.3059484569800004,
2.27952958498,
2.30416345298,
2.3021176489800004,
2.2558643539800003,
2.25856984198
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.53692370192,
"stddev": 0.013353699459997067,
"median": 1.53596450532,
"user": 1.74770008,
"system": 1.84746406,
"min": 1.51930593682,
"max": 1.55525909982,
"times": [
1.53407630782,
1.52022605582,
1.5236021088199998,
1.54815966982,
1.55525909982,
1.51930593682,
1.5435682088199998,
1.5327836468199998,
1.55440328182,
1.53785270282
]
},
{
"command": "pacquet@main",
"mean": 1.5729258402200001,
"stddev": 0.02698350147567614,
"median": 1.56934932832,
"user": 1.7033368799999997,
"system": 1.90980676,
"min": 1.54295348982,
"max": 1.6333049128200001,
"times": [
1.54295348982,
1.56715763382,
1.6333049128200001,
1.55539710982,
1.57900796482,
1.57689264782,
1.56120182382,
1.57154102282,
1.59799501682,
1.54380677982
]
}
]
} |
|
| Branch | pr/12148 |
| 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 | 2,344.48 ms(-0.33%)Baseline: 2,352.36 ms | 2,822.83 ms (83.05%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,536.92 ms(+0.65%)Baseline: 1,526.98 ms | 1,832.38 ms (83.88%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 2,067.48 ms(-0.06%)Baseline: 2,068.66 ms | 2,482.39 ms (83.29%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 661.84 ms(+1.06%)Baseline: 654.88 ms | 785.85 ms (84.22%) |
…ct minimumReleaseAge unit doc Address PR review: avoid a possible unhandled rejection if the NDJSON stream errors after the L frame in lockfile-only mode, and fix the `minimumReleaseAge` JSDoc (minutes, not seconds) to match the resolver and the Rust client.
What
Lift the restriction tracked by #12146:
pacquet install --lockfile-only(and thelockfileOnlysetting) is now honored when apnprServer/ agent is configured. Previously pacquet errored rather than silently fetching + linking (added in #12144). The faithful pnpm behavior — resolve + writepnpm-lock.yaml, fetch nothing, link nothing — is now supported via a server-side resolve-only mode plus a defensive client.How
This implements both options the issue outlined: a server resolve-only mode and a client that skips materialization.
Rust pnpr / pacquet (the issue's explicit scope)
pnprserver (install_accelerator.rs,protocol.rs): newlockfileOnlyrequest field. When set, the handler resolves and returns only theLline — skippingfetch_uncached(tarball fetch) andcompute_diff(file diff), so noD/Ilines are streamed.pacquet-pnpr-client(lib.rs): newlockfile_onlyoption, forwarded to/v1/install. As a backstop, when set the client returns the lockfile immediately and never touches the local store — holding the guarantee even against a server that predates resolve-only.pacquet-cli(install.rs): removed the error; threads the flag through and returns right after writing the lockfile, skipping the materializationInstall.TypeScript
@pnpm/agent.*client (client-side only, per maintainer guidance — the agent server is intentionally untouched)@pnpm/agent.client(fetchFromPnpmRegistry.ts): newlockfileOnlyoption, sent to the server; ignores anyD/Ilines so the store stays untouched even though the current TS agent server still streams them.@pnpm/installing.deps-installer(installFromPnpmRegistry): forwardslockfileOnlyand returns after writing the lockfile, skipping the headless install.Tests
pacquet-pnpr-clientintegration testlockfile_only_resolves_without_fetching_files(lockfile resolved,files_written == 0,index_entries_written == 0, client store index empty);pacquet-clie2e testinstall_via_pnpr_lockfile_only_writes_lockfile_without_linking(lockfile written, nonode_modules, no client store). All 219 tests inpnpr+pacquet-pnpr-clientpass.agent/serverintegration test asserting resolution happens butindexEntriesis empty and no CAFSfiles/dir is written — the server streamedDlines that the client correctly ignored. All 5 integration tests pass.Notes
Written by an agent (Claude Code, claude-opus-4-8).
Summary by CodeRabbit