Skip to content

feat: support URL-scoped registry auth via npm_config_// and pnpm_config_// env vars#12338

Merged
zkochan merged 6 commits into
mainfrom
feat/url-scoped-auth-env-vars
Jun 11, 2026
Merged

feat: support URL-scoped registry auth via npm_config_// and pnpm_config_// env vars#12338
zkochan merged 6 commits into
mainfrom
feat/url-scoped-auth-env-vars

Conversation

@zkochan

@zkochan zkochan commented Jun 11, 2026

Copy link
Copy Markdown
Member

What

Adds a file-free way to configure registry authentication via environment variables:

npm_config_//registry.npmjs.org/:_authToken=<token>
pnpm_config_//registry.npmjs.org/:_authToken=<token>

Both prefixes are supported (matched case-insensitively); the key after the prefix keeps its case. When the same key is set through both, pnpm_config_ wins.

Why

Until now pnpm (v11) had no way to provide registry auth purely through the environment — it had to come from a file (.npmrc/auth.ini) or a CLI flag. npm supports npm_config_//host/:_authToken, Yarn/vlt have env-based mechanisms; this closes that gap. It's also the natural answer for users hit by the env-expansion change in repository .npmrc files (#12314).

Why it's safe

These settings are host-scoped by construction — the registry a value applies to is encoded in the variable name, which comes from the trusted environment, not the repository. So unlike a project .npmrc, a malicious repo can't redirect the credential to another host. The env layer is treated as trusted config: it sits above project/workspace .npmrc (so it overrides repo config) but below CLI options. It is included in the trusted-config set used for package-manager bootstrap; the untrusted workspace .npmrc is not.

This deliberately only covers //-prefixed (URL-scoped) keys. A bare unscoped "default token" env var is intentionally not added, because the default registry can be repointed by a literal repo config value, which would send the token to the wrong host.

Tests

Added coverage for: reading auth from npm_config_//…, from pnpm_config_//…, pnpm_config_ winning over npm_config_, and the env value overriding a project .npmrc for the same host. Full @pnpm/config.reader suite shows no new failures (the 5 failing there fail identically on main).

Docs follow-up: I'll update https://pnpm.io/npmrc once this lands.

Summary by CodeRabbit

  • New Features

    • URL-scoped registry/auth settings can be supplied via environment variables (npm_config_//… and pnpm_config_//…, matched case-insensitively); pnpm_config_… takes precedence when both set.
    • Env-derived URL-scoped values are treated as trusted, outrank project/workspace .npmrc, and remain overridable by CLI. Non-supported env credential keys are ignored.
  • Documentation

    • Changeset entry documenting behavior and minor version bumps.
  • Tests

    • Added tests covering env-based URL-scoped credentials, precedence, case-insensitivity, and robustness.

…fig_// env vars

Adds a file-free way to configure registry authentication, e.g.
  npm_config_//registry.npmjs.org/:_authToken=<token>
  pnpm_config_//registry.npmjs.org/:_authToken=<token>

These are host-scoped by construction — the registry the value applies to is
encoded in the (trusted) variable name — so they cannot be redirected to
another host by repository-controlled config. The env value is trusted: it
overrides a project/workspace .npmrc but is still overridden by CLI options.
pnpm_config_ wins over npm_config_ for the same key.
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 11, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0)

Grey Divider


Remediation recommended

1. Pacquet ignores TLS env vars 🐞 Bug ≡ Correctness
Description
NpmrcAuth::from_url_scoped_env (pacquet) only applies URL-scoped env vars that match credential
suffixes, so URL-scoped TLS settings like //host/:cafile, :certfile, and :keyfile provided via
npm_config_//… / pnpm_config_//… are silently ignored. The JS config reader imports arbitrary
//… env keys (except tokenHelper) and getNetworkConfigs actively interprets TLS keys, so pnpm
and pacquet can diverge in environments that rely on env-provided CA/client cert configuration.
Code

pacquet/crates/config/src/npmrc_auth.rs[R179-206]

+    /// Only the four credential fields (`_authToken`, `_auth`, `username`,
+    /// `_password`) are honored — the same set [`split_creds_key`] recognises.
+    /// Values are used verbatim (no `${VAR}` re-expansion): they already come
+    /// resolved from the environment.
+    pub fn from_url_scoped_env<Sys: EnvVar>() -> Self {
+        // Merge into one map keyed by the URL-scoped key so each key is applied
+        // once. `pnpm_config_` is extended last so it wins over `npm_config_`.
+        let mut npm_scoped: HashMap<String, String> = HashMap::new();
+        let mut pnpm_scoped: HashMap<String, String> = HashMap::new();
+        for (name, value) in Sys::vars() {
+            if value.is_empty() {
+                continue;
+            }
+            if let Some((is_pnpm, key)) = parse_url_scoped_env_name(&name) {
+                let target = if is_pnpm { &mut pnpm_scoped } else { &mut npm_scoped };
+                target.insert(key.to_owned(), value);
+            }
+        }
+        npm_scoped.extend(pnpm_scoped);
+
+        let mut auth = NpmrcAuth::default();
+        for (key, value) in npm_scoped {
+            if let Some((uri, suffix)) = split_creds_key(&key) {
+                let entry = auth.creds_by_uri.entry(uri.to_owned()).or_default();
+                apply_creds_field(entry, suffix, value);
+            }
+        }
+        auth
Evidence
JS reads any //… env key (dropping only :tokenHelper) and later parses TLS keys; pacquet’s env
reader explicitly limits itself to the four credential fields despite having TLS key parsing helpers
for .npmrc files.

config/reader/src/loadNpmrcFiles.ts[160-193]
config/reader/src/getNetworkConfigs.ts[31-40]
config/reader/src/getNetworkConfigs.ts[94-113]
pacquet/crates/config/src/npmrc_auth.rs[167-207]
pacquet/crates/config/src/npmrc_auth.rs[879-910]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pacquet` supports parsing URL-scoped TLS keys from `.npmrc` (e.g. `//host/:cafile`, `//host/:certfile`, `//host/:keyfile`, and inline `:ca/:cert/:key`), but the new URL-scoped env-var reader only imports credential fields. This creates a behavior gap vs pnpm’s JS config reader, which will honor URL-scoped TLS keys from the env layer.
## Issue Context
- JS path: URL-scoped env vars are merged into `rawConfig` without restricting suffixes (except `tokenHelper`), and `getNetworkConfigs()` parses TLS suffixes and loads file contents / expands `\\n`.
- Rust path: `from_url_scoped_env` only looks at `split_creds_key`, ignoring TLS keys despite having TLS parsing support elsewhere.
## Fix Focus Areas
- pacquet/crates/config/src/npmrc_auth.rs[167-207]
- pacquet/crates/config/src/npmrc_auth.rs[879-910]
- config/reader/src/loadNpmrcFiles.ts[160-193]
- config/reader/src/getNetworkConfigs.ts[31-40]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. UTF-8 slicing can panic 🐞 Bug ☼ Reliability
Description
In parse_url_scoped_env_name, byte-index string slicing (name[..prefix.len()] /
name[prefix.len()..]) can panic if an environment variable name contains non-ASCII characters such
that prefix.len() is not a UTF-8 boundary, crashing config loading.
Code

pacquet/crates/config/src/npmrc_auth.rs[R835-840]

+fn parse_url_scoped_env_name(name: &str) -> Option<(bool, &str)> {
+    for (prefix, is_pnpm) in [("pnpm_config_", true), ("npm_config_", false)] {
+        if name.len() > prefix.len() && name[..prefix.len()].eq_ignore_ascii_case(prefix) {
+            let rest = &name[prefix.len()..];
+            if rest.starts_with("//") {
+                return Some((is_pnpm, rest));
Evidence
The implementation performs direct byte-index slicing on name, which is a &str from
std::env::vars(); such slicing can panic at runtime if the index is not a UTF-8 boundary.

pacquet/crates/config/src/npmrc_auth.rs[830-845]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`parse_url_scoped_env_name()` slices a `&str` using byte indices (`name[..prefix.len()]` and `name[prefix.len()..]`). In Rust this can panic when the slice boundary is not a UTF-8 character boundary, which is possible for env var names containing non-ASCII characters.
### Issue Context
This code runs while enumerating the full process environment (`Sys::vars()`), so a single oddly-named env var can crash config resolution.
### Fix Focus Areas
- pacquet/crates/config/src/npmrc_auth.rs[835-842]
### Suggested fix
Avoid manual slicing and use safe prefix handling, e.g.:
- `if let Some(rest) = name.strip_prefix(prefix).or_else(|| name.get(..prefix.len()).filter(...)) { ... }`
- Prefer `strip_prefix` with `eq_ignore_ascii_case` by checking via `name.get(..prefix.len())` (returns `Option<&str>`), then slicing only after confirming boundaries.
One simple pattern:
- Use `name.get(..prefix.len())` for the case-insensitive compare.
- Use `name.get(prefix.len()..)` to obtain `rest` without panicking.
- Proceed only when both are `Some(...)` and `rest.starts_with("//")`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Env tokenHelper blocked ✓ Resolved 🐞 Bug ≡ Correctness
Description
readUrlScopedEnvConfig imports any //… key from npm_config_//… / pnpm_config_//… into
rawConfig, so a //…:tokenHelper env var will be included in pnpmConfig.authConfig.
getConfig() then throws TOKEN_HELPER_IN_PROJECT_CONFIG unless the key/value also exists in
userConfig, incorrectly rejecting trusted env-provided tokenHelper values (and potentially
breaking environments that already export such vars).
Code

config/reader/src/loadNpmrcFiles.ts[R160-183]

+// Matches `npm_config_//…` and `pnpm_config_//…` env var names. The prefix is
+// matched case-insensitively (as npm does), but the captured key keeps its
+// original case because URL-scoped keys are case-sensitive (e.g. `:_authToken`).
+const URL_SCOPED_ENV_RE = /^p?npm_config_(\/\/.+)$/i
+
+// Collect URL-scoped settings (keys beginning with `//host…`, such as
+// `//registry.npmjs.org/:_authToken`) from `npm_config_//…` and `pnpm_config_//…`
+// environment variables. These are host-scoped by construction — the registry
+// the value applies to is part of the variable name — so they are safe to honor
+// from the trusted environment without a config file. When the same key is set
+// through both prefixes, `pnpm_config_` wins.
+function readUrlScopedEnvConfig (env: Record<string, string | undefined>): Record<string, unknown> {
+  const npmScoped: Record<string, string> = {}
+  const pnpmScoped: Record<string, string> = {}
+  for (const envKey of Object.keys(env)) {
+    const value = env[envKey]
+    if (value == null || value === '') continue
+    const match = URL_SCOPED_ENV_RE.exec(envKey)
+    if (match == null) continue
+    const target = envKey.slice(0, 5).toLowerCase() === 'pnpm_' ? pnpmScoped : npmScoped
+    target[match[1]] = value
+  }
+  return { ...npmScoped, ...pnpmScoped }
+}
Evidence
The new env reader captures any URL-scoped key and merges it into the raw/trusted configs; because
INI config allows any key beginning with //, a //…:tokenHelper env var will flow into
pnpmConfig.authConfig. The existing tokenHelper guard only permits values that are present in
userConfig, so the env-provided value will fail that check and throw.

config/reader/src/loadNpmrcFiles.ts[116-147]
config/reader/src/loadNpmrcFiles.ts[160-183]
config/reader/src/localConfig.ts[189-199]
config/reader/src/index.ts[343-352]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
URL-scoped env vars (`npm_config_//…` / `pnpm_config_//…`) currently accept any `//…` key, including `//…:tokenHelper`. After this PR, such env-provided tokenHelper entries become part of `pnpmConfig.authConfig`, but `getConfig()` only allows tokenHelper values originating from `userConfig`, so it throws `TOKEN_HELPER_IN_PROJECT_CONFIG` even though the PR explicitly treats the env-scoped layer as trusted.
## Issue Context
- `readUrlScopedEnvConfig()` returns all `//…` keys from env and merges them into `rawConfig`/`trustedConfig`.
- `pickIniConfig()` includes all keys starting with `//`.
- The tokenHelper safety check in `getConfig()` currently compares only against `npmrcResult.userConfig`.
## Fix Focus Areas
- config/reader/src/loadNpmrcFiles.ts[160-183]
- config/reader/src/loadNpmrcFiles.ts[116-147]
- config/reader/src/index.ts[343-352]
## Suggested fix approach
Either:
1) Tighten `readUrlScopedEnvConfig()` to only admit the intended safe URL-scoped auth keys (e.g. `:_authToken`, `:_auth`, `:username`, `:_password`, `:cert`, `:key`, `:ca`, `:cafile`, etc.) and explicitly exclude `:tokenHelper`, **or**
2) Update the tokenHelper validation to treat `envScopedConfig` as a trusted source (e.g., validate tokenHelper against `trustedAuthConfig` / `npmrcResult.trustedConfig` rather than only `userConfig`, while still rejecting values that originate from the workspace `.npmrc`).
Add/adjust a test to cover `pnpm_config_//…/:tokenHelper` behavior based on the intended policy.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. Empty env override ignored 🐞 Bug ☼ Reliability
Description
readUrlScopedEnvConfig skips URL-scoped env vars whose value is '', so an explicitly-empty env
var cannot override/clear the same URL-scoped key coming from a workspace .npmrc or other layer.
This diverges from the existing pnpm_config_* env parsing (which accepts empty strings) and from
.npmrc parsing (which does not drop empty values).
Code

config/reader/src/loadNpmrcFiles.ts[R171-177]

+function readUrlScopedEnvConfig (env: Record<string, string | undefined>): Record<string, unknown> {
+  const npmScoped: Record<string, string> = {}
+  const pnpmScoped: Record<string, string> = {}
+  for (const envKey of Object.keys(env)) {
+    const value = env[envKey]
+    if (value == null || value === '') continue
+    const match = URL_SCOPED_ENV_RE.exec(envKey)
Evidence
The new env reader explicitly filters out empty strings, whereas the existing env parser only
filters out null/undefined, and .npmrc parsing keeps the parsed value without an empty-string
skip. Therefore URL-scoped env vars behave differently and cannot be used to intentionally clear a
key.

config/reader/src/loadNpmrcFiles.ts[171-177]
config/reader/src/env.ts[42-53]
config/reader/src/loadNpmrcFiles.ts[235-275]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
URL-scoped env config currently ignores empty-string values (`value === ''`). This prevents users from explicitly overriding/clearing a URL-scoped key via env, and makes the semantics inconsistent with other env parsing paths in the config reader.
## Issue Context
- `parseEnvVars()` accepts empty strings (it only skips null/undefined).
- `.npmrc` parsing stores values as-is (including empty strings).
## Fix Focus Areas
- config/reader/src/loadNpmrcFiles.ts[171-181]
- config/reader/src/env.ts[42-53]
- config/reader/test/index.ts[983-1045]
## Suggested fix approach
Remove the `value === ''` exclusion (keep only `value == null`), and add a test demonstrating the desired behavior (e.g. env `npm_config_//host/:_authToken=''` overrides a workspace `.npmrc` token to empty / disables it). If empty auth values should be rejected, implement explicit validation at the point of use rather than silently ignoring the env var.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d7c57e2a-b057-4fef-9147-71cceb32181b

📥 Commits

Reviewing files that changed from the base of the PR and between 4e97cb1 and 0466da0.

📒 Files selected for processing (2)
  • config/reader/test/index.ts
  • pacquet/crates/config/src/tests.rs
📜 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). (7)
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Lint and Test (ubuntu-latest)
  • GitHub Check: Code Coverage
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (2)
pacquet/**/*.rs

📄 CodeRabbit inference engine (pacquet/AGENTS.md)

pacquet/**/*.rs: Log emissions are part of matching pnpm — when porting a function that fires pnpm:<channel> events through globalLogger, logger.debug(...), or streamParser.write(...), mirror the call site, payload, and ordering so @pnpm/cli.default-reporter parses pacquet's NDJSON the same way
Declare a newtype wrapper for branded string types instead of collapsing the brand into a plain String or &str in Rust
If upstream TypeScript always validates before construction of a branded string, validate in the Rust wrapper too via TryFrom<String> and/or FromStr and 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 infallible From<String> constructor
If upstream TypeScript occasionally constructs a branded string without validation, expose from_str_unchecked in 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 handwriting impl blocks; use manual impl only when conversion needs custom logic
Model TypeScript string literal unions (like 'auto' | 'always' | 'never') as Rust enums 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 in CODE_STYLE_GUIDE.md — imports, modules, naming, ownership and borrowing, parameter type selection, trait bounds, pattern matching, pipe-trait, error handling, test layout, and cloning of Arc and Rc
Choose owned vs. borrowed parameters to minimize copies; widen to t...

Files:

  • pacquet/crates/config/src/tests.rs
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use Standard Style with trailing commas, prefer functions over classes, declare functions after they are used (relying on hoisting), limit functions to no more than two or three arguments, and use a single options object for functions needing more parameters
Follow import order: standard libraries first, then external dependencies (sorted alphabetically), then relative imports
Do not write comments that restate what the code already says; rename variables, split helpers, or move checks to more obvious places instead
Do not repeat documentation at call sites that already lives on the callee; update the JSDoc once and let every call site benefit
Use JSDoc for the function's contract (preconditions, postconditions, edge cases, why the function exists), not for re-narrating the function body
Do not record past implementation shape, refactor history, or removed code in comments; use git log and git blame for that information instead
Write comments only when the reason for code is non-obvious, a hidden invariant exists, a workaround for a known bug is needed, or an exception to surrounding pattern is deliberate

Files:

  • config/reader/test/index.ts
🧠 Learnings (7)
📚 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/config/src/tests.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/config/src/tests.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/config/src/tests.rs
📚 Learning: 2026-06-06T18:58:37.156Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 12243
File: pacquet/crates/package-manager/src/install_package_by_snapshot.rs:319-322
Timestamp: 2026-06-06T18:58:37.156Z
Learning: When reviewing Rust code, do not assume `matches!(expr, Pattern(_))` will move out of `expr` if `Pattern(_)` contains no by-value bindings. `matches!` desugars to a `match` that auto-borrows the scrutinee for discrimination, so even if `expr` is a non-`Copy` value behind a shared reference (e.g., `&T`), the macro should not move-out of the borrowed data purely due to `matches!`. Treat `matches!(&expr, Pattern(_))` as a readability/clarity improvement, not a correctness requirement. Only flag potential move-out-of-borrow risks when the pattern includes by-value bindings that would require moving the matched value.

Applied to files:

  • pacquet/crates/config/src/tests.rs
📚 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:

  • config/reader/test/index.ts
📚 Learning: 2026-06-05T13:47:05.929Z
Learnt from: vsumner
Repo: pnpm/pnpm PR: 12190
File: installing/deps-installer/test/install/frozenStore.ts:2-17
Timestamp: 2026-06-05T13:47:05.929Z
Learning: In the pnpm/pnpm repository, the shared Jest preset keeps `injectGlobals` at its default (`true`), so `test` and `expect` are available as Jest globals. Therefore, reviewers should not flag (or treat as TypeScript/compilation errors) missing `import { test, expect } from 'jest/globals'` when a test file uses `test`/`expect` without importing them. Importing from `jest/globals` may still be used for consistency with sibling files, but it is not required for execution in this repo unless a Jest preset is explicitly configured with `injectGlobals: false`.

Applied to files:

  • config/reader/test/index.ts
📚 Learning: 2026-06-05T13:47:26.046Z
Learnt from: vsumner
Repo: pnpm/pnpm PR: 12190
File: installing/deps-installer/src/install/index.ts:2337-2343
Timestamp: 2026-06-05T13:47:26.046Z
Learning: In the pnpm/pnpm codebase, `PnpmError` automatically prefixes `err.code` with `ERR_PNPM_` when you pass a code that does not already start with `ERR_PNPM_` (it normalizes `this.code` via `code.startsWith('ERR_PNPM_') ? code : `ERR_PNPM_${code}``). Therefore, during code review you should NOT flag `new PnpmError(...)` call sites for passing a bare error code (e.g., `new PnpmError('FROZEN_STORE_INCOMPATIBLE_WITH_PNPR', ...)`); the resulting `err.code` will still be `ERR_PNPM_FROZEN_STORE_INCOMPATIBLE_WITH_PNPR`.

Applied to files:

  • config/reader/test/index.ts
🪛 Betterleaks (1.3.1)
config/reader/test/index.ts

[high] 1107-1107: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (2)
pacquet/crates/config/src/tests.rs (1)

197-199: LGTM!

Also applies to: 446-477

config/reader/test/index.ts (1)

1081-1117: LGTM!


📝 Walkthrough

Walkthrough

This PR adds support for configuring URL-scoped registry authentication via environment variables using npm_config_//… and pnpm_config_//…. The code extracts these vars, merges them into the config/auth precedence (pnpm_ wins over npm_, env wins over project .npmrc), marks them trusted, and adds unit/tests and a changeset.

Changes

URL-scoped environment configuration

Layer / File(s) Summary
Release notes and feature documentation
.changeset/quick-registries-env-auth.md
Changeset documents npm_config_//… and pnpm_config_//… env support, precedence (pnpm_ > npm_), trusted-config treatment, and version bumps for @pnpm/config.reader and pnpm.
URL-scoped env config parsing and merge integration
config/reader/src/loadNpmrcFiles.ts
Adds URL_SCOPED_ENV_RE and readUrlScopedEnvConfig(), reads env-scoped config, inserts env-scoped layer into mergedConfig/trustedConfig/rawConfig priority chain, and documents the new env (//-scoped) layer.
Environment variable config tests (reader)
config/reader/test/index.ts
Jest tests that verify reading :_authToken from npm_config_//… and pnpm_config_//…, pnpm_ precedence, case-insensitive prefix matching, excluding :tokenHelper, env override of project .npmrc tokens, CLI precedence, and non-token credential fields.
Env enumeration and auth assembly (pacquet)
pacquet/crates/env-replace/src/lib.rs, pacquet/crates/config/src/api.rs, pacquet/crates/config/src/lib.rs, pacquet/crates/config/src/npmrc_auth.rs
Extend EnvVar with vars() and implement in production; add NpmrcAuth::from_url_scoped_env and parse_url_scoped_env_name; integrate env-scoped source into Config::current merge order so URL-scoped env creds are high-priority.
Unit tests (pacquet)
pacquet/crates/config/src/npmrc_auth/tests.rs, pacquet/crates/config/src/tests.rs
Test helpers and unit/end-to-end tests for NpmrcAuth::from_url_scoped_env covering npm_/pnpm_ sourcing, precedence, ignoring non-URL-scoped vars, empty-value handling, non-ASCII safety, and full-config precedence behavior.

Sequence Diagram(s)

sequenceDiagram
  participant loadNpmrcConfig as loadNpmrcConfig
  participant EnvProvider as EnvVar::vars()
  participant Parser as readUrlScopedEnvConfig
  participant Merger as mergedConfig/trustedConfig/rawConfig
  loadNpmrcConfig->>EnvProvider: request env variables
  EnvProvider->>Parser: provide (name,value) list
  Parser->>Parser: match npm_config_//… / pnpm_config_//…, filter, merge pnpm_ over npm_
  Parser-->>loadNpmrcConfig: envScopedConfig
  loadNpmrcConfig->>Merger: prepend env-scoped source into merge chain
  Merger->>Merger: fold env-scoped before project/auth.ini/user sources
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • pnpm/pnpm#11730: Related refactor of the Host/EnvVar seam that this PR extends with EnvVar::vars and URL-scoped env parsing.

Poem

🐰 I sniff the env where tokens hide,
pnpm_ hops ahead with bounding stride,
Keys parsed neat from names that start with //,
Trusted and high, they leap past .npmrc,
A carrot cheer for smooth registry glide.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding support for URL-scoped registry authentication via npm_config_// and pnpm_config_// environment variables.
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 feat/url-scoped-auth-env-vars

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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.

❤️ Share

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

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Support URL-scoped registry auth via npm_config_// and pnpm_config_//
✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

Walkthroughs

Description
• Support URL-scoped registry auth via npm_config_//… and pnpm_config_//… env vars
• Merge env-scoped credentials as trusted config above workspace .npmrc but below CLI
• Add tests for env parsing, precedence, and workspace override behavior
Diagram
graph TD
  A["loadNpmrcConfig"] --> M["merge priority"] --> O["mergedConfig + trustedConfig"]
  W["workspace .npmrc"] --> M
  U["user ~/.npmrc"] --> M
  I["pnpm auth.ini"] --> M
  E["env (//-scoped)"] --> M
  C["CLI options"] --> M

  subgraph Legend
    direction LR
    _proc["Function"] ~~~ _file["Config file"] ~~~ _env["Environment"]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Honor all `npm_config_*`/`pnpm_config_*` env vars (not just `//`-scoped)
  • ➕ Matches npm’s broader env-to-config surface area
  • ➕ Would enable file-free defaults beyond auth (e.g. registry, proxies)
  • ➖ Reintroduces the core security risk: unscoped auth could be redirected by repo-controlled registry settings
  • ➖ Much larger compatibility surface area; higher chance of surprising behavior/regressions
2. Add a single unscoped token env var (e.g. `PNPM_AUTH_TOKEN`)
  • ➕ Simple UX for common cases
  • ➕ Works even without URL-scoped keys knowledge
  • ➖ Unsafe if the default registry can be repointed by workspace .npmrc or other untrusted config
  • ➖ Harder to reason about which host receives credentials
3. Delegate parsing to an npm config/env library
  • ➕ Less custom parsing logic to maintain
  • ➕ Potentially closer behavioral parity with npm
  • ➖ Additional dependency/complexity for a small, security-sensitive feature
  • ➖ Still needs pnpm-specific precedence/trust rules (workspace vs env vs CLI)

Recommendation: The PR’s approach (only //-scoped env keys, merged above workspace .npmrc and included in trustedConfig) is the best tradeoff: it enables file-free auth while keeping credentials host-bound by construction and avoiding redirect attacks. The broader/unscoped alternatives add UX convenience but significantly increase credential exfiltration risk.

Grey Divider

File Changes

Enhancement (1)
loadNpmrcFiles.ts Merge URL-scoped env config into npmrc load pipeline +39/-4

Merge URL-scoped env config into npmrc load pipeline

• Adds parsing of 'npm_config_//…' and 'pnpm_config_//…' environment variables into a URL-scoped config object, preserving key case while matching prefixes case-insensitively. Integrates this env-scoped layer into 'mergedConfig', 'rawConfig', and 'trustedConfig' with precedence between workspace '.npmrc' and CLI options, and defines 'pnpm_config_' as the winner when both are set.

config/reader/src/loadNpmrcFiles.ts


Tests (1)
index.ts Test env-scoped auth parsing and precedence rules +64/-0

Test env-scoped auth parsing and precedence rules

• Adds tests covering reading URL-scoped auth from both 'npm_config_//…' and 'pnpm_config_//…', the precedence of 'pnpm_config_' over 'npm_config_', and env-scoped auth overriding a workspace '.npmrc' token for the same host.

config/reader/test/index.ts


Documentation (1)
quick-registries-env-auth.md Add changeset for URL-scoped auth env vars +13/-0

Add changeset for URL-scoped auth env vars

• Introduces a changeset announcing minor bumps for '@pnpm/config.reader' and 'pnpm'. Documents the new 'npm_config_//…' and 'pnpm_config_//…' env var support, including precedence and trust rationale.

.changeset/quick-registries-env-auth.md


Grey Divider

Qodo Logo

@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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
config/reader/test/index.ts (1)

983-1045: ⚡ Quick win

Add mixed/upper-case prefix coverage for the new env-key parsing contract.

These tests validate lowercase npm_config_// and pnpm_config_//, but not the case-insensitive prefix behavior promised by this feature. Please add at least one test using NPM_CONFIG_//... and/or PnPm_Config_//... (including precedence) to lock that contract.

🤖 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 `@config/reader/test/index.ts` around lines 983 - 1045, Add tests mirroring the
existing URL-scoped env-var cases but using mixed/upper-case prefixes to assert
case-insensitive parsing: create a test that calls getConfig (after
prepareEmpty) with env containing 'NPM_CONFIG_//env-test.example/:_authToken'
(or 'PnPm_Config_//env-test.example/:_authToken') and assert
expect(config.authConfig['//env-test.example/:_authToken']).toBe('...'); also
add a precedence test where a mixed-case 'pnpm' style key overrides a lowercase
'npm_config_//' key (use the same pattern as the existing "pnpm_config_// takes
precedence..." test), referencing getConfig, prepareEmpty and config.authConfig
to locate where to add them.
🤖 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 @.changeset/quick-registries-env-auth.md:
- Around line 8-11: The fenced code block that contains the lines
"npm_config_//registry.npmjs.org/:_authToken=<token>" and
"pnpm_config_//registry.npmjs.org/:_authToken=<token>" is missing a language
hint and triggers MD040; fix it by changing the opening fence to include a
language identifier (e.g., use ```text) so the block becomes a labeled fenced
code block and the lint warning is resolved.

---

Nitpick comments:
In `@config/reader/test/index.ts`:
- Around line 983-1045: Add tests mirroring the existing URL-scoped env-var
cases but using mixed/upper-case prefixes to assert case-insensitive parsing:
create a test that calls getConfig (after prepareEmpty) with env containing
'NPM_CONFIG_//env-test.example/:_authToken' (or
'PnPm_Config_//env-test.example/:_authToken') and assert
expect(config.authConfig['//env-test.example/:_authToken']).toBe('...'); also
add a precedence test where a mixed-case 'pnpm' style key overrides a lowercase
'npm_config_//' key (use the same pattern as the existing "pnpm_config_// takes
precedence..." test), referencing getConfig, prepareEmpty and config.authConfig
to locate where to add them.
🪄 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: 290f0230-a56d-42dd-81f4-fd640d4e2982

📥 Commits

Reviewing files that changed from the base of the PR and between 84bb4b1 and 8c394d0.

📒 Files selected for processing (3)
  • .changeset/quick-registries-env-auth.md
  • config/reader/src/loadNpmrcFiles.ts
  • config/reader/test/index.ts
📜 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). (1)
  • GitHub Check: ubuntu-latest / Node.js 24 / Test
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use Standard Style with trailing commas, prefer functions over classes, declare functions after they are used (relying on hoisting), limit functions to no more than two or three arguments, and use a single options object for functions needing more parameters
Follow import order: standard libraries first, then external dependencies (sorted alphabetically), then relative imports
Do not write comments that restate what the code already says; rename variables, split helpers, or move checks to more obvious places instead
Do not repeat documentation at call sites that already lives on the callee; update the JSDoc once and let every call site benefit
Use JSDoc for the function's contract (preconditions, postconditions, edge cases, why the function exists), not for re-narrating the function body
Do not record past implementation shape, refactor history, or removed code in comments; use git log and git blame for that information instead
Write comments only when the reason for code is non-obvious, a hidden invariant exists, a workaround for a known bug is needed, or an exception to surrounding pattern is deliberate

Files:

  • config/reader/test/index.ts
  • config/reader/src/loadNpmrcFiles.ts
🧠 Learnings (4)
📚 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/quick-registries-env-auth.md
📚 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:

  • config/reader/test/index.ts
  • config/reader/src/loadNpmrcFiles.ts
📚 Learning: 2026-06-05T13:47:05.929Z
Learnt from: vsumner
Repo: pnpm/pnpm PR: 12190
File: installing/deps-installer/test/install/frozenStore.ts:2-17
Timestamp: 2026-06-05T13:47:05.929Z
Learning: In the pnpm/pnpm repository, the shared Jest preset keeps `injectGlobals` at its default (`true`), so `test` and `expect` are available as Jest globals. Therefore, reviewers should not flag (or treat as TypeScript/compilation errors) missing `import { test, expect } from 'jest/globals'` when a test file uses `test`/`expect` without importing them. Importing from `jest/globals` may still be used for consistency with sibling files, but it is not required for execution in this repo unless a Jest preset is explicitly configured with `injectGlobals: false`.

Applied to files:

  • config/reader/test/index.ts
📚 Learning: 2026-06-05T13:47:26.046Z
Learnt from: vsumner
Repo: pnpm/pnpm PR: 12190
File: installing/deps-installer/src/install/index.ts:2337-2343
Timestamp: 2026-06-05T13:47:26.046Z
Learning: In the pnpm/pnpm codebase, `PnpmError` automatically prefixes `err.code` with `ERR_PNPM_` when you pass a code that does not already start with `ERR_PNPM_` (it normalizes `this.code` via `code.startsWith('ERR_PNPM_') ? code : `ERR_PNPM_${code}``). Therefore, during code review you should NOT flag `new PnpmError(...)` call sites for passing a bare error code (e.g., `new PnpmError('FROZEN_STORE_INCOMPATIBLE_WITH_PNPR', ...)`); the resulting `err.code` will still be `ERR_PNPM_FROZEN_STORE_INCOMPATIBLE_WITH_PNPR`.

Applied to files:

  • config/reader/test/index.ts
  • config/reader/src/loadNpmrcFiles.ts
🪛 markdownlint-cli2 (0.22.1)
.changeset/quick-registries-env-auth.md

[warning] 8-8: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 OpenGrep (1.22.0)
config/reader/src/loadNpmrcFiles.ts

[ERROR] 177-177: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🔇 Additional comments (1)
config/reader/src/loadNpmrcFiles.ts (1)

16-17: LGTM!

Also applies to: 88-93, 117-130, 145-145, 160-183

Comment thread .changeset/quick-registries-env-auth.md Outdated
zkochan added 2 commits June 11, 2026 22:18
… pnpm_config_// env vars

Pacquet parity for the same feature on the JS side: read URL-scoped registry
credentials from npm_config_//… and pnpm_config_//… environment variables
(e.g. npm_config_//registry.npmjs.org/:_authToken=<token>).

These are trusted (sourced from the environment, not the repository) and
host-scoped by construction, so they sit at the top of the .npmrc precedence
chain — above the project .npmrc. pnpm_config_ wins over npm_config_ for the
same key. Adds an EnvVar::vars() enumeration capability (default empty, so
existing fakes keep compiling; production providers override it).
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 11, 2026

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit a5454c1

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.01      5.4±0.24ms   799.5 KB/sec    1.00      5.4±0.17ms   807.6 KB/sec

@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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
pacquet/crates/config/src/npmrc_auth/tests.rs (1)

1067-1122: ⚡ Quick win

Add a mixed-case prefix regression.

These new tests only exercise lowercase npm_config_ / pnpm_config_, but this feature's contract is case-insensitive prefix matching. One NPM_CONFIG_//... or mixed-case PnPm_Config_//... case would keep that guarantee locked in. Based on the PR objectives, case-insensitive prefix matching is part of the intended behavior.

🤖 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/config/src/npmrc_auth/tests.rs` around lines 1067 - 1122, The
tests only cover lowercase prefixes but the prefix matching must be
case-insensitive; add new tests that set mixed-case and upper-case env keys and
assert NpmrcAuth::from_url_scoped_env::<Env>() treats
"NPM_CONFIG_//registry.npmjs.org/:_authToken" and a mixed
"PnPm_Config_//registry.npmjs.org/:_authToken" the same as their lowercase
equivalents. Add at least: (1) a test that uses
"NPM_CONFIG_//registry.npmjs.org/:_authToken" and expects the auth token to be
read, and (2) a precedence test that sets both mixed-case pnpm and npm prefixed
vars (e.g., "PnPm_Config_..." and "npm_CONFIG_...") and asserts the
pnpm-prefixed value wins, mirroring the existing url_scoped_env_reads_* and
url_scoped_env_pnpm_prefix_wins_over_npm tests but using mixed-case keys to
ensure from_url_scoped_env handles prefixes case-insensitively.
🤖 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 `@pacquet/crates/config/src/npmrc_auth.rs`:
- Around line 835-844: parse_url_scoped_env_name can panic on non-ASCII env var
names due to direct byte-slicing (name[..prefix.len()]) — replace those slices
with boundary-checked reads using name.get(..prefix.len()) and
name.get(prefix.len()..) so you only call eq_ignore_ascii_case and starts_with
on valid &str slices; update the loop in parse_url_scoped_env_name to first
obtain the prefix slice via get(..prefix.len()) and the remainder via
get(prefix.len()..) and only then perform the case-insensitive prefix check and
rest.starts_with("//"), returning Some((is_pnpm, rest)) with the safe rest slice
when matched.

In `@pacquet/crates/env-replace/src/lib.rs`:
- Around line 74-75: Update the env-var collectors to use std::env::vars_os()
and skip non-UTF-8 entries: replace uses of std::env::vars().collect() (e.g.,
the vars() function in SystemEnv in pacquet/crates/env-replace/src/lib.rs and
Host::vars in pacquet/crates/config/src/api.rs) with an iterator over
std::env::vars_os() and collect only pairs where both key and value can be
converted to String (use into_string().ok() or equivalent), preserving iteration
order and returning Vec<(String,String)> as before.

---

Nitpick comments:
In `@pacquet/crates/config/src/npmrc_auth/tests.rs`:
- Around line 1067-1122: The tests only cover lowercase prefixes but the prefix
matching must be case-insensitive; add new tests that set mixed-case and
upper-case env keys and assert NpmrcAuth::from_url_scoped_env::<Env>() treats
"NPM_CONFIG_//registry.npmjs.org/:_authToken" and a mixed
"PnPm_Config_//registry.npmjs.org/:_authToken" the same as their lowercase
equivalents. Add at least: (1) a test that uses
"NPM_CONFIG_//registry.npmjs.org/:_authToken" and expects the auth token to be
read, and (2) a precedence test that sets both mixed-case pnpm and npm prefixed
vars (e.g., "PnPm_Config_..." and "npm_CONFIG_...") and asserts the
pnpm-prefixed value wins, mirroring the existing url_scoped_env_reads_* and
url_scoped_env_pnpm_prefix_wins_over_npm tests but using mixed-case keys to
ensure from_url_scoped_env handles prefixes case-insensitively.
🪄 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: 33894fd0-6058-4653-8a09-6bfcd9501570

📥 Commits

Reviewing files that changed from the base of the PR and between 8c394d0 and a5454c1.

📒 Files selected for processing (5)
  • pacquet/crates/config/src/api.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/npmrc_auth.rs
  • pacquet/crates/config/src/npmrc_auth/tests.rs
  • pacquet/crates/env-replace/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). (10)
  • GitHub Check: Lint and Test (ubuntu-latest)
  • GitHub Check: Dylint
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Format
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Code Coverage
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (1)
pacquet/**/*.rs

📄 CodeRabbit inference engine (pacquet/AGENTS.md)

pacquet/**/*.rs: Log emissions are part of matching pnpm — when porting a function that fires pnpm:<channel> events through globalLogger, logger.debug(...), or streamParser.write(...), mirror the call site, payload, and ordering so @pnpm/cli.default-reporter parses pacquet's NDJSON the same way
Declare a newtype wrapper for branded string types instead of collapsing the brand into a plain String or &str in Rust
If upstream TypeScript always validates before construction of a branded string, validate in the Rust wrapper too via TryFrom<String> and/or FromStr and 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 infallible From<String> constructor
If upstream TypeScript occasionally constructs a branded string without validation, expose from_str_unchecked in 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 handwriting impl blocks; use manual impl only when conversion needs custom logic
Model TypeScript string literal unions (like 'auto' | 'always' | 'never') as Rust enums 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 in CODE_STYLE_GUIDE.md — imports, modules, naming, ownership and borrowing, parameter type selection, trait bounds, pattern matching, pipe-trait, error handling, test layout, and cloning of Arc and Rc
Choose owned vs. borrowed parameters to minimize copies; widen to t...

Files:

  • pacquet/crates/env-replace/src/lib.rs
  • pacquet/crates/config/src/api.rs
  • pacquet/crates/config/src/npmrc_auth/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/npmrc_auth.rs
🧠 Learnings (4)
📚 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/env-replace/src/lib.rs
  • pacquet/crates/config/src/api.rs
  • pacquet/crates/config/src/npmrc_auth/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/npmrc_auth.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/env-replace/src/lib.rs
  • pacquet/crates/config/src/api.rs
  • pacquet/crates/config/src/npmrc_auth/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/npmrc_auth.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/env-replace/src/lib.rs
  • pacquet/crates/config/src/api.rs
  • pacquet/crates/config/src/npmrc_auth/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/npmrc_auth.rs
📚 Learning: 2026-06-06T18:58:37.156Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 12243
File: pacquet/crates/package-manager/src/install_package_by_snapshot.rs:319-322
Timestamp: 2026-06-06T18:58:37.156Z
Learning: When reviewing Rust code, do not assume `matches!(expr, Pattern(_))` will move out of `expr` if `Pattern(_)` contains no by-value bindings. `matches!` desugars to a `match` that auto-borrows the scrutinee for discrimination, so even if `expr` is a non-`Copy` value behind a shared reference (e.g., `&T`), the macro should not move-out of the borrowed data purely due to `matches!`. Treat `matches!(&expr, Pattern(_))` as a readability/clarity improvement, not a correctness requirement. Only flag potential move-out-of-borrow risks when the pattern includes by-value bindings that would require moving the matched value.

Applied to files:

  • pacquet/crates/env-replace/src/lib.rs
  • pacquet/crates/config/src/api.rs
  • pacquet/crates/config/src/npmrc_auth/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/npmrc_auth.rs

Comment thread pacquet/crates/config/src/npmrc_auth.rs
Comment thread pacquet/crates/env-replace/src/lib.rs Outdated
@codecov-commenter

codecov-commenter commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.36170% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.74%. Comparing base (84bb4b1) to head (0466da0).

Files with missing lines Patch % Lines
pacquet/crates/env-replace/src/lib.rs 37.50% 5 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #12338   +/-   ##
=======================================
  Coverage   87.74%   87.74%           
=======================================
  Files         291      291           
  Lines       36061    36107   +46     
=======================================
+ Hits        31642    31683   +41     
- Misses       4419     4424    +5     

☔ View full report in Codecov by Harness.
📢 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.

…ve tests

Address review feedback on #12338:
- A `//host/:tokenHelper` env var would land in authConfig but trip the
  TOKEN_HELPER_IN_PROJECT_CONFIG guard (which only trusts the user .npmrc),
  incorrectly failing. tokenHelper names an executable, so it is now excluded
  from the env-scoped layer entirely.
- Add tests for case-insensitive prefix matching and the tokenHelper exclusion.
- Add a 'text' language hint to the changeset's fenced block (MD040).
@zkochan

zkochan commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

Thanks both — addressed in 9127925:

Qodo #1 — env tokenHelper blocked (valid). A //host/:tokenHelper env var would land in authConfig but trip the TOKEN_HELPER_IN_PROJECT_CONFIG guard (which only trusts the user .npmrc), failing incorrectly. Since tokenHelper names an executable pnpm runs, the env-scoped reader now excludes :tokenHelper entirely (it must come from a user-level config file). Added a regression test. The pacquet side was already safe here — it only admits the four credential fields.

Qodo #2 — empty env value ignored (working as intended). Treating an empty env var as unset matches both npm's own npm_config_* handling (npm-conf's if (!env[x]) return) and pnpm's existing readEnvVar (value !== '') / env_overlay's !value.is_empty(). Honoring "" as an explicit clear would diverge from those, so I kept the skip and documented it in a comment rather than change it. Happy to revisit if you'd prefer the clear-semantics.

CodeRabbit — case-insensitive coverage (valid). Added a test using NPM_CONFIG_//… (upper) and PnPm_Config_//… (mixed), including precedence, to lock the contract.

CodeRabbit — changeset MD040 (valid). Added a text language hint to the fenced block.

Full @pnpm/config.reader suite: 130 passed, same 5 pre-existing failures as main (no new failures).

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 11, 2026

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 9127925

Address CodeRabbit review on the pacquet env-auth code:
- EnvVar::vars() used std::env::vars(), which panics if any env var name or
  value is not valid UTF-8. Iterate vars_os() and skip non-UTF-8 entries,
  matching var()'s .ok() behavior. (SystemEnv and Host.)
- parse_url_scoped_env_name sliced with name[..prefix.len()], which panics
  when the byte index lands inside a multi-byte char. Use boundary-checked
  name.get(..) instead.
- Add a regression test with non-ASCII env var names.
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 11, 2026

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 4e97cb1

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

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

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 4.131 ± 0.105 4.022 4.328 1.76 ± 0.09
pacquet@main 4.174 ± 0.130 4.012 4.325 1.78 ± 0.10
pnpr@HEAD 2.343 ± 0.108 2.146 2.458 1.00
pnpr@main 2.382 ± 0.146 2.185 2.598 1.02 ± 0.08
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 4.130588901179999,
      "stddev": 0.1053829406687776,
      "median": 4.08981175058,
      "user": 3.565938799999999,
      "system": 3.44015994,
      "min": 4.0219887195799995,
      "max": 4.32765551058,
      "times": [
        4.10408457958,
        4.18917116358,
        4.0219887195799995,
        4.06462956558,
        4.13375457658,
        4.02819100758,
        4.32765551058,
        4.07474873658,
        4.07553892158,
        4.28612623058
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 4.174030940980001,
      "stddev": 0.12987779575576672,
      "median": 4.13898296608,
      "user": 3.6263727,
      "system": 3.43482214,
      "min": 4.0115576615799995,
      "max": 4.32522622958,
      "times": [
        4.07237080058,
        4.07189878758,
        4.31790834058,
        4.0115576615799995,
        4.31135231858,
        4.04674393358,
        4.32522622958,
        4.19531470258,
        4.30528540558,
        4.08265122958
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 2.3431021888799997,
      "stddev": 0.10826497482764484,
      "median": 2.39062348758,
      "user": 2.5955169,
      "system": 2.96846584,
      "min": 2.14573106258,
      "max": 2.45813758058,
      "times": [
        2.45813758058,
        2.39698379758,
        2.45274198758,
        2.39197506458,
        2.26705321958,
        2.3892719105799998,
        2.28319552358,
        2.21416392058,
        2.14573106258,
        2.43176782158
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 2.3817968283799997,
      "stddev": 0.1459930408671415,
      "median": 2.37788541308,
      "user": 2.6471484999999997,
      "system": 3.01217284,
      "min": 2.18478720358,
      "max": 2.59800127258,
      "times": [
        2.18478720358,
        2.54044370458,
        2.44859528858,
        2.30717553758,
        2.51044142958,
        2.45338275758,
        2.59800127258,
        2.2192166535799998,
        2.3044179425799998,
        2.25150649358
      ]
    }
  ]
}

Scenario: Isolated linker: fresh restore, hot cache + hot store

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 658.3 ± 12.9 641.0 685.0 1.00
pacquet@main 697.4 ± 120.0 636.8 1036.2 1.06 ± 0.18
pnpr@HEAD 779.9 ± 14.7 756.4 805.5 1.18 ± 0.03
pnpr@main 791.1 ± 67.0 745.9 975.2 1.20 ± 0.10
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.6582940033600002,
      "stddev": 0.012890361823739488,
      "median": 0.6553054782600001,
      "user": 0.37269924,
      "system": 1.32237182,
      "min": 0.6410253877600001,
      "max": 0.68502047476,
      "times": [
        0.6534539957600001,
        0.64602976976,
        0.66838583076,
        0.64927187876,
        0.6410253877600001,
        0.6525563737600001,
        0.6571569607600001,
        0.6657877787600001,
        0.6642515827600001,
        0.68502047476
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.6974134722600001,
      "stddev": 0.11996947665554031,
      "median": 0.6625083457600001,
      "user": 0.37659844000000003,
      "system": 1.3334202199999996,
      "min": 0.6368415577600001,
      "max": 1.03621422476,
      "times": [
        0.66168131976,
        0.66333537176,
        0.6368415577600001,
        0.65518459776,
        0.64300165176,
        0.6509351827600001,
        0.66898990276,
        0.66781543876,
        1.03621422476,
        0.69013547476
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 0.77987758516,
      "stddev": 0.014669094404958902,
      "median": 0.78001117076,
      "user": 0.38156924,
      "system": 1.35235802,
      "min": 0.75636594576,
      "max": 0.80554290876,
      "times": [
        0.80554290876,
        0.7907356617600001,
        0.75636594576,
        0.78309162876,
        0.7897739437600001,
        0.77693071276,
        0.7680603237600001,
        0.77140693276,
        0.76671389276,
        0.79015390076
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 0.79107522986,
      "stddev": 0.06700543417876474,
      "median": 0.76656350626,
      "user": 0.39003154000000007,
      "system": 1.3350501199999996,
      "min": 0.74593229776,
      "max": 0.97516134976,
      "times": [
        0.75039619976,
        0.74593229776,
        0.7999572677600001,
        0.79081119176,
        0.76420579176,
        0.76505910876,
        0.7625437917600001,
        0.78861739576,
        0.97516134976,
        0.7680679037600001
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 3.785 ± 0.045 3.711 3.856 1.72 ± 0.11
pacquet@main 3.806 ± 0.044 3.734 3.856 1.73 ± 0.11
pnpr@HEAD 2.253 ± 0.136 2.061 2.411 1.02 ± 0.09
pnpr@main 2.202 ± 0.138 2.011 2.396 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 3.7852603801399995,
      "stddev": 0.04525276696228089,
      "median": 3.7778403748400002,
      "user": 3.4475365599999996,
      "system": 3.33075706,
      "min": 3.71122334534,
      "max": 3.85587891034,
      "times": [
        3.80822054634,
        3.84085414234,
        3.73332979834,
        3.77788045534,
        3.77007080834,
        3.85587891034,
        3.76334383234,
        3.71122334534,
        3.77780029434,
        3.81400166834
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 3.80552445794,
      "stddev": 0.04387349096880973,
      "median": 3.82359659634,
      "user": 3.5067879599999996,
      "system": 3.3468189599999993,
      "min": 3.73393808434,
      "max": 3.8556830933399997,
      "times": [
        3.73393808434,
        3.76037308634,
        3.8537684513399997,
        3.83583068634,
        3.82603132934,
        3.78211940334,
        3.75521736434,
        3.8556830933399997,
        3.82116186334,
        3.8311212173399998
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 2.2525341112399997,
      "stddev": 0.13585249037144428,
      "median": 2.32225879734,
      "user": 2.40095796,
      "system": 2.8867610599999995,
      "min": 2.06080946234,
      "max": 2.41142109634,
      "times": [
        2.38411070034,
        2.15713195934,
        2.34153007334,
        2.33785660534,
        2.10709783734,
        2.30666098934,
        2.0776868453399997,
        2.06080946234,
        2.34103554334,
        2.41142109634
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 2.20219619364,
      "stddev": 0.13751761030210283,
      "median": 2.16803269034,
      "user": 2.472585959999999,
      "system": 2.88570766,
      "min": 2.01126496934,
      "max": 2.39642071334,
      "times": [
        2.35588851334,
        2.15283897134,
        2.01126496934,
        2.05300799034,
        2.39642071334,
        2.18322640934,
        2.1259243733399997,
        2.34128019034,
        2.09573835434,
        2.30637145134
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, hot cache + hot store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 1.071 ± 0.011 1.048 1.085 1.59 ± 0.03
pacquet@main 1.103 ± 0.083 1.057 1.337 1.63 ± 0.13
pnpr@HEAD 0.696 ± 0.099 0.653 0.975 1.03 ± 0.15
pnpr@main 0.675 ± 0.011 0.659 0.698 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 1.07083381184,
      "stddev": 0.010641203856715315,
      "median": 1.0721501682399999,
      "user": 1.06593962,
      "system": 1.6556435599999997,
      "min": 1.04840370124,
      "max": 1.08522101924,
      "times": [
        1.07829328024,
        1.0678262122400002,
        1.06131854324,
        1.07109474824,
        1.04840370124,
        1.08227740624,
        1.07320558824,
        1.08522101924,
        1.06746211624,
        1.07323550324
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 1.1034239839400002,
      "stddev": 0.08322217513391185,
      "median": 1.0771819362400001,
      "user": 1.0608153200000001,
      "system": 1.6871061599999997,
      "min": 1.05714382224,
      "max": 1.33711851624,
      "times": [
        1.05714382224,
        1.07427405924,
        1.07457004124,
        1.0797938312400002,
        1.33711851624,
        1.0904741502400002,
        1.07053030124,
        1.10490068224,
        1.08271846024,
        1.0627159752400002
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 0.69600797804,
      "stddev": 0.09872392906877565,
      "median": 0.66176556324,
      "user": 0.33490722,
      "system": 1.27325966,
      "min": 0.65335626624,
      "max": 0.97461761824,
      "times": [
        0.65335626624,
        0.66044306124,
        0.65809251124,
        0.66891959524,
        0.65533570124,
        0.66089415324,
        0.97461761824,
        0.69860980424,
        0.66263697324,
        0.66717409624
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 0.67526041354,
      "stddev": 0.010671476988292773,
      "median": 0.6742150577399999,
      "user": 0.32920282,
      "system": 1.2823783599999998,
      "min": 0.65879630124,
      "max": 0.69831514424,
      "times": [
        0.67352057624,
        0.68348348624,
        0.66934676624,
        0.66813581424,
        0.65879630124,
        0.68040893224,
        0.67656189024,
        0.67490953924,
        0.66912568524,
        0.69831514424
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, cold cache + hot store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.560 ± 0.026 2.523 2.613 3.84 ± 0.05
pacquet@main 2.568 ± 0.028 2.529 2.629 3.85 ± 0.06
pnpr@HEAD 0.682 ± 0.009 0.668 0.696 1.02 ± 0.02
pnpr@main 0.667 ± 0.007 0.658 0.679 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.5600268728600004,
      "stddev": 0.02609693109761762,
      "median": 2.55996085426,
      "user": 1.5167469999999998,
      "system": 1.9176853199999997,
      "min": 2.5231453567599997,
      "max": 2.61272739576,
      "times": [
        2.58118495476,
        2.5632999427599996,
        2.55662176576,
        2.55121746676,
        2.55007802376,
        2.5231453567599997,
        2.52539352376,
        2.56618632676,
        2.61272739576,
        2.57041397176
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.5679410856600002,
      "stddev": 0.02832696422156358,
      "median": 2.56552279876,
      "user": 1.512592,
      "system": 1.9402213199999998,
      "min": 2.52902538976,
      "max": 2.62896743476,
      "times": [
        2.57830624976,
        2.52902538976,
        2.5590550687599998,
        2.5422947157599998,
        2.57008767476,
        2.56095792276,
        2.54358463376,
        2.58213524076,
        2.62896743476,
        2.58499652576
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 0.6818892742600001,
      "stddev": 0.00863581435463962,
      "median": 0.68300417726,
      "user": 0.3337909,
      "system": 1.28869792,
      "min": 0.66753215676,
      "max": 0.69626820276,
      "times": [
        0.68538185276,
        0.66753215676,
        0.69626820276,
        0.6806265017600001,
        0.6745384827600001,
        0.67212678876,
        0.68929459676,
        0.68632490576,
        0.68056182176,
        0.68623743276
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 0.6671809394599999,
      "stddev": 0.006597235479413103,
      "median": 0.6662347877600001,
      "user": 0.33625109999999997,
      "system": 1.26244432,
      "min": 0.65793061476,
      "max": 0.67869347576,
      "times": [
        0.66985794876,
        0.66732681276,
        0.67869347576,
        0.66262586276,
        0.67471221376,
        0.67147836476,
        0.65946436676,
        0.65793061476,
        0.6651427627600001,
        0.66457697176
      ]
    }
  ]
}

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12338
Testbedpacquet
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
milliseconds (ms)
(Result Δ%)
Upper Boundary
milliseconds (ms)
(Limit %)
isolated-linker.fresh-install.cold-cache.cold-store📈 view plot
🚷 view threshold
3,785.26 ms
(-58.10%)Baseline: 9,033.40 ms
10,840.08 ms
(34.92%)
isolated-linker.fresh-install.cold-cache.hot-store📈 view plot
🚷 view threshold
2,560.03 ms
(-47.92%)Baseline: 4,915.73 ms
5,898.87 ms
(43.40%)
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
🚷 view threshold
1,070.83 ms
(-23.19%)Baseline: 1,394.19 ms
1,673.03 ms
(64.01%)
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
🚷 view threshold
4,130.59 ms
(-58.52%)Baseline: 9,957.83 ms
11,949.40 ms
(34.57%)
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
🚷 view threshold
658.29 ms
(+0.30%)Baseline: 656.30 ms
787.56 ms
(83.59%)
🐰 View full continuous benchmarking report in Bencher

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12338
Testbedpnpr

⚠️ 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-thresholds flag.

Click to view all benchmark results
BenchmarkLatencymilliseconds (ms)
isolated-linker.fresh-install.cold-cache.cold-store📈 view plot
⚠️ NO THRESHOLD
2,252.53 ms
isolated-linker.fresh-install.cold-cache.hot-store📈 view plot
⚠️ NO THRESHOLD
681.89 ms
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
⚠️ NO THRESHOLD
696.01 ms
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
⚠️ NO THRESHOLD
2,343.10 ms
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
⚠️ NO THRESHOLD
779.88 ms
🐰 View full continuous benchmarking report in Bencher

Fill the coverage gaps in the URL-scoped env-auth feature:
- JS: assert a CLI-provided //host/:_authToken still beats the same env var
  (workspace < env < CLI), and that non-token cred fields work while a
  non-URL-scoped env key is ignored.
- pacquet: add end-to-end tests through the full config load — that a
  npm_config_//… var is honored and outranks a project .npmrc token for the
  same host, and that the prefix is matched case-insensitively. FakeEnv now
  enumerates via vars() so the env-scoped reader sees the fixture.
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 11, 2026

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 0466da0

@zkochan zkochan merged commit 615c669 into main Jun 11, 2026
27 of 28 checks passed
@zkochan zkochan deleted the feat/url-scoped-auth-env-vars branch June 11, 2026 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants