mirror of
https://github.com/pnpm/pnpm.git
synced 2026-10-09 23:02:09 -04:00
1e82e001cd5316fa2ab792a7c19bdeb0c498084b
167
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1e82e001cd | chore(release): 11.7.0 (#12414) | ||
|
|
681b593eb2 |
fix: support scope-specific registry auth tokens (#12392)
pnpm can now use different auth tokens for different package scopes, even when those scopes use the same registry URL. Previously, auth was selected only by registry URL. If `@org-a` and `@org-b` both used `https://npm.pkg.github.com/`, they had to share the same token. This caused problems for registries that issue tokens per organization or per scope. Configure a scope-specific token by adding the package scope after the registry URL in the auth key: ```ini @org-a:registry=https://npm.pkg.github.com/ @org-b:registry=https://npm.pkg.github.com/ //npm.pkg.github.com/:@org-a:_authToken=${ORG_A_TOKEN} //npm.pkg.github.com/:@org-b:_authToken=${ORG_B_TOKEN} //npm.pkg.github.com/:_authToken=${FALLBACK_TOKEN} ``` `pnpm login --registry=https://npm.pkg.github.com --scope=@org-a` writes the token to the same scope-specific auth key. When installing or publishing `@org-a/*`, pnpm uses `ORG_A_TOKEN`. For `@org-b/*`, pnpm uses `ORG_B_TOKEN`. Packages without a matching scope continue to use the registry-wide fallback token. |
||
|
|
53b105416f |
chore(release): 11.6.0 (#12336)
* chore(release): 11.6.0 * docs: update CHANGELOG.md --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> |
||
|
|
b7195db5c8 | chore(release): 11.5.3 (#12305) | ||
|
|
4b4d38361c | chore(release): 11.5.2 (#12207) | ||
|
|
5192edf40e |
feat(pnpr): forward credentials and add per-user access grants for external private registries (#12184) (#12189)
Closes #12184 (part 2). #12181 shipped the per-caller access gate on `POST /v1/install`, which authorizes every served package against pnpr's own `packages:` policy — the complete answer **while pnpr fetches anonymously**. This PR adds the remaining piece: forwarding the caller's per-registry credentials so the accelerator can resolve/fetch **external private** content as the caller, and gating that content per user against the registry that actually owns it. ## Credential forwarding (issue steps 1–2) - **Wire:** `POST /v1/install` gains an `authHeaders` body map (`{ "//host/path/": "Bearer …" }`, the shape `AuthHeaders::from_map` consumes / `getAuthHeadersFromCreds` produces) plus an HTTP `Authorization` header. The body map carries the *upstream* registry tokens; the header identifies the caller to pnpr's own gate and keys the grant table. - **pacquet plumbing:** a request-scoped `Arc<AuthHeaders>` is threaded via a new `Install.auth_override` field and an `auth_override` param on `build_resolution_verifiers`, so resolution/verification run as the caller **without** baking per-user auth into the interned `&'static Config` (which would leak one config per user). - **Server:** `handle_install` builds the per-request `AuthHeaders` and threads it through resolve, verify, and `fetch_uncached` (which now returns the freshly-fetched set). - **Clients:** pacquet `pnpr-client` and `@pnpm/pnpr.client` send `registry` / `namedRegistries` / `authHeaders` + `Authorization`; the TS path sources them from the caller's registry credentials via `@pnpm/network.auth-header` (`getAuthHeadersFromCreds` is newly re-exported). `@pnpm/worker` is unchanged — downloads happen server-side. - **Credential scope:** both clients forward the caller's *full* credential map, not a subset scoped to the declared registries. The registries a dependency graph touches aren't knowable up front — a transitive package can be scope-routed to another registry or pinned to a tarball URL on a host that's in `.npmrc` but isn't a declared registry — so pnpr attaches the right token per fetched URL exactly as a local install does. These are package-fetch credentials going to the very service the caller configured to fetch its packages. ## Per-user grant table (issue steps 3–4) Externally-resolved private content carries no pnpr policy, so the store's possession of the bytes must not authorize a user the upstream never cleared. A served package is dispatched by **whether a forwarded credential was used to fetch it**: - **No forwarded cred → pnpr-as-authority:** the existing local `packages:` policy check, unchanged. - **Forwarded cred → upstream-as-authority:** gated against a persistent `(user, name@version)` grant table (SQLite, modeled on `VerdictCache`). Freshly fetched this request ⇒ record + allow (the upstream just accepted the token). Cache hit with a standing grant ⇒ allow, no upstream trip. Cache hit, no grant ⇒ re-verify against the owning registry with the caller's credential — record on success; **clear-on-discovery** (purge the user's grants for the package) + deny on `401`/`403`. TTL is the `installAccelerator.grantTtl` config knob (default: permanent). ## Public vs private (no per-user gating for public packages) A forwarded credential matching a registry doesn't mean a package is *private* — in a mixed proxy (one registry serving a company's private packages **and** public ones), the token matches everything, and gating public content per user would cost a grant row and a re-verify round trip per user for bytes anyone may read. So before the per-user path, a not-yet-classified cache hit is probed **anonymously**: a `2xx` classifies the package public in a global set (no user pays for it again, no grant, no further round trip); a `401`/`403` means it's genuinely private and falls through to the grant / re-verify path above. Public packages thus cost **one anonymous probe across the whole fleet**, not one per user. ## Tests - pnpr: grant-table + public-set mechanics, regime dispatch, the upstream-authorization paths (fresh-fetch, granted cache hit, private re-verify-and-record, denied-clears-grants, public-classified-once-then-free), and forwarded-cred-routes-around-local-policy. - pacquet `pnpr-client`: a test asserting `authHeaders` + `Authorization` travel on the wire. - Full suites green: `pnpr` (237), `pacquet-package-manager` (389), `pacquet-pnpr-client` (12), `pacquet-network`/`config` (325); clippy `-D warnings`, `cargo fmt`, rustdoc `-D warnings --document-private-items`, `typos`, and the TS compile all clean. ## Scoped follow-ups (not in this PR) - Clear-on-discovery fires at the re-verify hook only. A `401`/`403` during the cold resolve aborts the request anyway (nothing is served); threading the offending package out of the deep resolve error to also clear stale grants for *future* requests needs structured auth errors. - Per-scope external registries route via the default registry, since pacquet doesn't yet surface `@scope:registry` routing in `collect_packages`. |
||
|
|
60a1eeca28 |
fix(cli): avoid Windows crash after network requests (#12121)
* fix: avoid Windows crash after network requests * fix: destroy dispatchers before Windows error exits * test: cover destroyDispatchers and document its shutdown contract Add a JSDoc to destroyDispatchers() explaining it is for process shutdown, a unit test verifying it destroys the global and cached dispatchers, and bump @pnpm/network.fetch to minor for the new public export. * fix(network.fetch): destroy the active global dispatcher on shutdown Include getGlobalDispatcher() so destroyDispatchers() also closes the currently-active dispatcher if it was replaced via setGlobalDispatcher(). --------- Co-authored-by: Zoltan Kochan <z@kochan.io> |
||
|
|
b741d91e67 | chore(release): 11.5.0 (#12068) | ||
|
|
2cadfb5d3d |
refactor: replace enquirer with @inquirer/prompts (#11942)
Replaces the unmaintained `enquirer` package with `@inquirer/prompts` for all interactive CLI prompts. Fixes the `update -i` scrolling overflow bug where long choice lists were clipped in the terminal. Fixes #6643 ## User-facing changes - **`pnpm update -i` / `pnpm update -i --latest`**: Scrolling now works correctly when many packages are available; the new library uses visual-line-aware pagination via `usePagination` - **`pnpm audit --fix -i`**: Same scrolling fix for vulnerability selection - **`pnpm approve-builds`**: Interactive build approval prompts updated - **`pnpm patch`**: Version selection and "apply to all" prompts updated - **`pnpm patch-remove`**: Patch removal selection updated - **`pnpm publish`**: Branch confirmation prompt updated - **`pnpm login`**: Credential prompts updated - **`pnpm run` / `pnpm exec`** (with `verifyDepsBeforeRun=prompt`): Confirmation prompt updated ## Internal changes - `OtpEnquirer` DI interface changed from `{ prompt }` to `{ input }` - `LoginEnquirer` DI interface changed from `{ prompt }` to `{ input, password }` - `enquirer` removed from catalog and all 8 package.json files - `@inquirer/prompts` v8.4.3 added to catalog and all 8 package.json files - Removed `OtpPromptOptions` and `OtpPromptResponse` exports from `@pnpm/network.web-auth` (no longer needed) --------- Co-authored-by: Zoltan Kochan <z@kochan.io> |
||
|
|
b1fa2d5979 |
fix(dist-tag): support npm's web-based 2FA flow (#11998)
* fix(dist-tag): open the browser for npm web 2FA when --otp is absent
Without `--otp`, `pnpm dist-tag add` (and `rm`) failed against
npmjs.org with `[ERR_PNPM_UNAUTHORIZED] You must be logged in to set
dist-tag … "You must provide a one-time pass. Upgrade your client to
npm@latest in order to use 2FA."` — the browser never opened. The
fallback "upgrade your client" message is what npmjs.org returns when
the client doesn't announce `npm-auth-type: web`; without that header
the server skips the web challenge and tells the user to install a
newer npm. `--otp=<6-digit code>` already worked because the OTP went
out in `npm-otp` directly.
Send `npm-auth-type: web` on dist-tag writes when no `--otp` is
given, surface 401 responses carrying `authUrl`/`doneUrl` (or the
legacy "one-time pass" text) as `SyntheticOtpError`, and wrap the
call in the existing `withOtpHandling` helper (already used by
`pnpm publish`), which opens the browser, polls the done URL, and
retries with the resulting token as `npm-otp` while keeping
`npm-auth-type: web` in place.
Drive-by cleanup in `@pnpm/network.fetch`: the abbreviated-metadata
`Accept` header is no longer attached to non-GET requests, matching
`npm-registry-fetch`'s behavior.
* fix(dist-tag): default authType to 'web'; inherit network config in OTP context
Two review fixes:
- `setDistTag` documented `authType` as defaulting to `'web'` but only
sent the `npm-auth-type` header when the field was explicitly passed.
Always send the header, defaulting to `'web'`.
- `OTP_CONTEXT` used a module-level `createFetchFromRegistry({})`, so
the `withOtpHandling` doneUrl poll ignored the command's proxy / TLS
/ `configByUri` config. Build the OTP context per call from the
command's `opts` instead.
Also rename `toOtpOrUnauthorizedError` → `parseAuthError` and drop the
spurious `async` (the body string is already awaited at the call site).
|
||
|
|
72d997cc34 | chore(release): 11.4.0 (#11989) | ||
|
|
a23956e3ab |
fix(config/reader): pin unscoped per-registry settings to their source's registry at load time (#11953)
* fix(config/reader): drop user-level default auth when workspace overrides registry
When a workspace `.npmrc` overrides `registry=` to a different value than the
user's `~/.npmrc` or `~/.config/pnpm/auth.ini` would have set, do not bind
unscoped/default credentials (`_authToken`, `_auth`, `username`/`_password`)
from the user-level config to the workspace-selected registry. The previous
behavior leaked user-trusted credentials to whatever registry an untrusted
workspace `.npmrc` pointed at. Reported by JUNYI LIU.
* chore(cspell): allow JUNYI in changeset and tests
* fix(config/reader): also defend when pnpm-workspace.yaml overrides registry
Move the rebind defense to after all config layers (CLI, env vars,
pnpm-workspace.yaml, .npmrc) have settled. Compare the final resolved
default registry against what the user-level config alone would produce,
and skip the check entirely if the user requested a registry via CLI/env
themselves.
* feat(config/reader): deprecate unscoped authentication credentials
Emit a per-file warning whenever an .npmrc or auth.ini contains an
unscoped auth value (_authToken, _auth, username, _password,
tokenHelper). URL-scoped tokens have been npm's recommended pattern
since npm@9, and unscoped credentials are slated for removal in a
future major. The warning fires independently of whether the rebind
defense rejects the credentials, so users see the deprecation even when
their setup happens to be safe today.
* refactor(config/reader): rescope unscoped credentials at load time instead of detecting rebinds post-merge
Each .npmrc / auth.ini / CLI source's unscoped credential keys
(_authToken, _auth, username, _password, tokenHelper) are rewritten to
their URL-scoped equivalent during load, using the same source's
registry= value (or the npmjs default if it declares none). A later
layer overriding registry= can no longer rebind a credential to its own
registry — the credential is already pinned to the URL its author
intended.
This removes the post-merge source-tracking defense and replaces it
with the simpler per-source normalization. Each rescope emits a
deprecation warning so users migrate to writing the URL-scoped form
directly.
* refactor(network/auth-header): drop empty-string default-registry slot
After load-time rescoping, no source can populate configByUri[''] —
every credential is either URL-scoped from the start or rewritten to
the URL-scoped form during the .npmrc / auth.ini / CLI parse. The
runtime fallback that re-keyed configByUri[''] onto the merged default
registry, and the publish-side fallback that read it, are both dead
code.
Removed:
- empty-string handling in getAuthHeadersFromCreds, including its
defaultRegistry parameter
- defaultRegistry parameter from createGetAuthHeaderByURI
- the corresponding dedicated unit test
- the configByUri['']?.creds fallback in publishPackedPkg.ts
- empty-key assertions in config/reader tests
Updated all ~16 call sites of createGetAuthHeaderByURI to drop the now
unused second argument.
* feat(config/reader): extend per-source rescoping to client TLS cert/key
The same trust-boundary issue that affected unscoped credentials applies
to client TLS settings: an unscoped cert=/key= would be presented to
whatever registry the merged config settles on, even if a later layer
(workspace .npmrc, pnpm-workspace.yaml, CLI flag) overrode it. The
existing rescope helper now also rewrites unscoped `cert` and `key`
to their URL-scoped form, pinning them to the registry their author
named in the same source.
`ca`/`cafile` are intentionally left unscoped: they're trust anchors,
not credentials, and corporate MITM-proxy setups depend on them
applying to every HTTPS request. The default-registry override can't
weaponize an unscoped CA — the attacker would need a cert signed by it.
`certfile`/`keyfile` (file-path variants) are not rescoped either:
`certfile` isn't read unscoped by pnpm today (asymmetric vs. `keyfile`
in NPM_AUTH_SETTINGS), and supporting only one of them would be
confusing. Users wanting the path form can write it URL-scoped
directly.
* chore(config/reader): remove dead unscoped `keyfile` allowlist entry
`keyfile` was listed in NPM_AUTH_SETTINGS so unscoped `keyfile=<path>`
passed the .npmrc filter and ended up in authConfig — but nothing in
the codebase ever read it from there. The dispatcher uses `opts.key`
(inline PEM) and `configByUri[host].tls.key` (URL-scoped path/inline
content), neither of which is populated from unscoped `keyfile=`.
`certfile` was already absent from the allowlist for the same reason,
so this also removes the asymmetry between the two file-path variants.
URL-scoped `//host/:certfile=...` and `//host/:keyfile=...` continue
to work via `tryParseSslKey` and are unaffected.
* test(network/auth-header): drop test for removed default-registry slot
This test exercised the configByUri[''] re-keying path that was
removed in the rescope-at-load refactor. With createGetAuthHeaderByURI
no longer accepting a defaultRegistry parameter and unscoped
credentials no longer reaching the merged config, the scenario the
test described is structurally unreachable.
* fix(config/reader): handle empty/invalid registry value in rescope
Two CI fixes:
1. When a source's `registry=` resolves to an empty string (e.g. an
unresolved `${ENV_VAR}` placeholder), `new URL(...)` inside
`nerfDart` throws. Guard the call with try/catch: drop the
unscoped per-registry keys (a bare token has nowhere safe to bind)
and emit a warning naming the offending source.
2. Update `.npmrc does not load pnpm settings` to expect the rescoped
form of unscoped `_authToken`/`username` in `authConfig` — they
now appear as `//registry.npmjs.org/:_authToken` etc. since the
test's .npmrc declares no `registry=` of its own.
* chore(cspell): allow "rescoping"
* test(installing/deps-installer): drop "legacy way" auth test
This test passed credentials via the configByUri[''] empty-string slot,
which the auth-header layer re-keyed to the merged default registry at
request time. That slot was removed in the rescope-at-load refactor —
credentials are now always URL-scoped before they reach configByUri,
so the empty-key entry is unreachable from any code path.
The scenario the test covered (basicAuth via username/password) is
already exercised by the existing "installing a package that need
authentication, using password" test using the URL-scoped form.
|
||
|
|
0fb723323f | chore(release): 11.2.0 (#11764) | ||
|
|
cd80b2c8ae | chore(release): 11.1.3 (#11717) | ||
|
|
8a80235c7b | chore(release): 11.1.2 | ||
|
|
18a464f5b4 |
fix(network): strip sec-fetch-* headers to fix Azure DevOps Artifacts 400 errors (#11602)
* fix(network): strip sec-fetch-* headers to fix Azure DevOps Artifacts 400 errors undici's fetch() automatically adds sec-fetch-* headers (e.g. sec-fetch-mode: cors) per the Fetch spec. Azure DevOps Artifacts interprets these as browser requests and returns HTTP 400 for uncached upstream packages. Since pnpm is a CLI tool, these headers serve no purpose. Adds a stripSecFetchHeaders interceptor applied to all dispatchers (global, proxy, and non-proxy) via undici's compose() API. Fixes #11572 * refactor: fix header types and function placement in stripSecFetchHeaders - Widen header type from Record<string, string> to Record<string, string | string[] | undefined> to match Dispatcher.DispatchOptions - Move stripSecFetchHeaders below its first use, relying on function hoisting per codebase conventions * refactor(network.fetch): handle iterable header form and tidy test `Dispatcher.dispatch` accepts headers as a Map/web-Headers iterable in addition to the flat string[] and plain object forms. The previous object branch routed iterables through Object.entries, which would silently drop every header for Map-like inputs. Detect Symbol.iterator and consume the iterator directly when present. Also drop the underscore prefix on the test's `req` parameter since it is used. --------- Co-authored-by: Zoltan Kochan <z@kochan.io> |
||
|
|
732312f49e | chore(release): 11.1.0 | ||
|
|
20e7affbe2 |
fix: publish web-auth honors proxy when polling doneUrl (#11581)
- `pnpm publish` failed to complete the web-based authentication flow when an HTTP/HTTPS proxy was configured. `libnpmpublish` (used for the initial publish request) routes through the proxy, but the subsequent `doneUrl` polling went through `@pnpm/network.fetch` without forwarding any proxy/TLS settings. The registry rejected the poll with `403` because the source IP differed from the initial request, so publish hung on the QR-code prompt forever. - Adds `createDispatchedFetch(opts)` to `@pnpm/network.fetch` — a curried `fetchWithDispatcher` that pre-binds proxy / TLS / local-address / `configByUri`-derived client certificates. `publishPackedPkg` uses it to build an `OtpContext` whose `fetch` honors the same network configuration as the publish request. - `extractTlsConfigs` is now performed automatically inside `createDispatchedFetch` (and hoisted out of the per-request loop in `createFetchFromRegistry`), so callers only have to pass `configByUri` once. Fixes #11561. |
||
|
|
6ef34b7a11 | chore(release): 11.0.3 | ||
|
|
184ce26f3f |
docs: fix package names in README files (#11409)
* docs: fix package names in README files * docs: update links to point to npmx.dev |
||
|
|
4d7cd56ccc |
chore: upgrade @typescript/native-preview to 7.0.0-dev.20260421.2 (#11332)
* chore: upgrade @typescript/native-preview to 7.0.0-dev.20260421.2
- Add explicit `types: ["node"]` to the shared tsconfig because tsgo
20260421 no longer auto-acquires `@types/*` from `node_modules`.
- Refactor test files to explicitly import jest globals (`describe`,
`it`, `test`, `expect`, `beforeEach`, etc.) from `@jest/globals`
instead of relying on `@types/jest` ambient declarations. Under the
new tsgo build, `import { jest } from '@jest/globals'` shadows the
ambient `jest` namespace, breaking `@types/jest`'s `declare var
describe: jest.Describe;` globals.
- Add `@jest/globals` to each package's devDependencies where tests
now import from it, and add `@types/node` to packages that need it
but were relying on hoisted resolution.
- Replace `fail()` calls with `throw new Error(...)` since `fail` is
no longer globally available.
* chore: fix remaining tsgo type-strictness errors
- Strip `as <PnpmType>` casts on objects passed to toMatchObject /
toStrictEqual / toEqual; @jest/globals rejects the typed objects
(which include AsymmetricMatchers) vs. the repo-specific type.
- Type `jest.fn<...>()` explicitly where the mock's signature matters
for toHaveBeenCalledWith.
- Replace `beforeEach(() => X)` with `beforeEach(() => { X })` so the
return value is void, as the stricter jest typing requires.
- Use `expect.objectContaining({...})` in one place where the full
expected object triggered stricter type resolution.
- Cast `prompt.mock.calls` arg through `as unknown as Record<...>[]`
for patch.test.ts's nested-array matchers.
- Fix off-by-one `<reference path>` in pnpm/test/getConfig.test.ts
that only surfaced now.
- Move `@jest/globals` from devDependencies to dependencies in the
two `__utils__` packages that import it from `src/`.
- Clean up unused imports from the @jest/globals migration.
* chore: address Copilot review on #11332
- Move misplaced `@jest/globals` imports to the top import block in
checkEngine, run.ts, and workspace/root-finder tests where the
script dropped them below executable code.
- Replace `try { await x(); throw new Error('should have thrown') } catch`
in bins/linker, lockfile/fs, and resolving/local-resolver tests with
`await expect(x()).rejects.toMatchObject({...})`. The old pattern
swallowed an unrelated `throw` if the under-test call silently
succeeded, which would fail on the catch-block assertion with a
misleading message.
|
||
|
|
aa93759d9b |
chore(release): drop eslint from lib prepublishOnly (#11320)
Library packages had `prepublishOnly: pn compile`, which expands to `tsgo --build && pn lint --fix`. During `pn release` that runs eslint against ~150 packages for no benefit — the code has already been linted in CI and the release flow's upfront compile has already built dist/. Switch lib prepublishOnly to a bare `tsgo --build` so the safety-net compile stays but the per-package eslint cost is gone. |
||
|
|
53668a42b8 |
fix: support explicit versions and --no-commit-hooks / --no-git-tag-version in pnpm version (#11299)
* fix: support explicit versions and --no-commit-hooks / --no-git-tag-version in `pnpm version` Registers options under their canonical names so nopt correctly parses the `--no-*` variants, accepts an explicit semver argument alongside bump types, and creates a git commit + annotated tag for the bump (honoring `--no-git-tag-version`, `--no-commit-hooks`, `--sign-git-tag`, `--message`, and `--tag-version-prefix`). Also fixes `--no-git-checks` which was parsed incorrectly. Closes #11271. * chore: add gpgsign and newversion to cspell; set gpg sign config in version test * fix: address Copilot review feedback on pnpm version * fix: skip git commit and tag in recursive version runs * fix: address Copilot review feedback on pnpm version |
||
|
|
f7c23231a9 | chore(release): 11.0.0-rc.1 | ||
|
|
2410cf4092 |
feat: add pnpm docs command and home alias (#11244)
* feat: add pnpm docs command and home alias * chore: update manifests * fix: address review comments for pnpm docs command - Remove 'docs' and 'home' from NOT_IMPLEMENTED_COMMANDS to prevent not-implemented handlers from overriding the real implementations - Change fallback URL from npmjs.com to npmx.dev - Remove bugs URL as a docs fallback (it's an issue tracker, not docs) - Fix ESM mock in tests: use jest.unstable_mockModule instead of jest.mock * refactor: use open package for browser opening in promptBrowserOpen Replace the hand-rolled platform-specific execFile logic with the open npm package, which handles WSL, Docker-in-WSL, and Windows edge cases better. This removes the execFile dependency injection from promptBrowserOpen, OtpContext, LoginContext, and SharedContext. * fix: address copilot review comments - docs: use www.npmjs.com fallback (not npmx.dev) and validate homepage URL is http(s) before opening - promptBrowserOpen: validate authUrl protocol before passing to open(), and guard against synchronous throws from open() * fix: restore npmx.dev fallback for pnpm docs * chore: expand changeset with web-auth refactor impact --------- Co-authored-by: Zoltan Kochan <z@kochan.io> |
||
|
|
06d6c2d405 | chore(release): 11.0.0-rc.0 | ||
|
|
d401ff2500 |
feat: add native pnpm unpublish command (#11128)
- Implements the `pnpm unpublish` command natively instead of passing through to npm - Supports unpublishing specific versions or version ranges using semver - Supports unpublishing entire packages with `--force` flag (with protection against accidental unpublish) - Supports OTP authentication via `--otp` flag - Supports custom registry via `--registry` flag - Reuses existing data structures from the deprecate command ## Usage ```bash # Unpublish a specific version pnpm unpublish my-package@1.0.0 # Unpublish multiple versions matching a range pnpm unpublish my-package@">1.0.0 <2.0.0" # Unpublish entire package (requires --force) pnpm unpublish my-package --force # With custom registry pnpm unpublish my-package --registry https://my-registry.com ``` ## Changes - Added `unpublish.ts` command in `releasing/plugin-commands-publishing/` - Removed unpublish from npm pass-through list in `pnpm.ts` - Added required dependencies: `@pnpm/fetch`, `semver`, `@types/semver` Follows npm unpublish behavior and is aligned with the existing `pnpm deprecate` implementation. --------- Co-authored-by: Zoltan Kochan <z@kochan.io> |
||
|
|
705e3fe3cc |
fix(auth): enable shell for .bat/.cmd token helpers on Windows
The refactor in
|
||
|
|
45a6cb6b2a |
refactor(auth): unify auth/SSL into structured configByUri (#11201)
Replaces the dual `authConfig` (raw .npmrc) + `authInfos` (parsed auth) + `sslConfigs` (parsed SSL) pattern with a single structured `configByUri: Record<string, RegistryConfig>` field on Config.
### New types (`@pnpm/types`)
- **`RegistryConfig`** — per-registry config: `{ creds?: Creds, tls?: TlsConfig }`
- **`Creds`** — auth credentials: `{ authToken?, basicAuth?, tokenHelper? }`
- **`TlsConfig`** — TLS config: `{ cert?, key?, ca? }`
### Key changes
- Rewrite `createGetAuthHeaderByURI` to accept `Record<string, RegistryConfig>` instead of raw .npmrc key-value pairs
- Eliminate duplicate auth parsing between `getAuthHeadersFromConfig` and `getNetworkConfigs`
- Remove `authConfig` from the install pipeline (`StrictInstallOptions`, `HeadlessOptions`), replaced by `configByUri`
- Remove `sslConfigs` from Config — SSL fields now live in `configByUri[uri].tls`
- Remove `authConfig['registry']` mutation in `extendInstallOptions` (default registry now passed directly to `createGetAuthHeaderByURI`)
- `authConfig` remains on Config only for raw .npmrc access (config commands, error reporting, config inheritance)
### Security
- tokenHelper in project .npmrc now throws instead of being silently stripped
- tokenHelper execution uses `shell: false` to prevent shell metacharacter injection
- Basic auth uses `Buffer.from().toString('base64')` instead of `btoa()` for Unicode safety
- Dispatcher only creates custom agents when entries actually have TLS fields
|
||
|
|
de3dc74439 |
feat(auth): keyboard event to open browser (#11148)
* feat(auth): add "Press ENTER to open in browser" during web authentication During web-based authentication (login, publish), users can now press ENTER to open the authentication URL in their default browser. The background token polling continues uninterrupted, so users who prefer to authenticate on their phone can still do so without pressing anything. The implementation uses Node's readline module (not raw mode), so Ctrl+C and Ctrl+Z continue to work normally. It is fully error-tolerant: if the keyboard listener or browser opening fails, a warning is printed and the polling continues. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix(auth): inject readline and execFile directly, not wrapper functions Address review feedback: - Remove defaultListenForEnter and defaultOpenBrowser wrapper functions - Inject readline module and execFile function directly via context - DEFAULT_CONTEXT now references modules directly (no closures) - Use switch for platform detection, default = no browser prompt - Rename pollWithBrowserOpen → offerToOpenBrowser (clearer name) - Add platform-specific tests (darwin, win32, linux, freebsd) - Use PassThrough streams for stdin mocks in tests https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix(auth): fix CI type errors in test mocks - Type jest.fn() mocks for readline.createInterface properly - Use PassThrough streams for stdin mocks in releasing/commands tests https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * refactor(auth): use generic Stdin parameter to eliminate PassThrough in tests Per review feedback, add a generic Stdin type parameter to context interfaces. This ties process.stdin and readline.createInterface together through the same type, so tests can use simple { isTTY: true } mocks instead of requiring PassThrough streams. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix(auth): propagate Stdin generic to releasing/commands OtpContext The OtpContext in releasing/commands extends BaseOtpContext from web-auth. Now that BaseOtpContext is generic, the local OtpContext and publishWithOtpHandling must also be generic so tests can use simple stdin mocks without PassThrough. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix: sort imports in releasing/commands otp.ts https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * refactor(auth): use .bind() for readline injection instead of generics Per review feedback, revert the generic Stdin approach and instead use readline.createInterface.bind(null, { input: process.stdin }) as the injectable dependency. This avoids generics proliferation while keeping the context clean — no arrow functions or closures in DEFAULT_CONTEXT. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * feat(publish): add "Press ENTER to open in browser" during publish OTP Wire up createReadlineInterface and execFile in the publish SHARED_CONTEXT so that pnpm publish also offers to open the browser during web-based OTP authentication. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix(auth): improve browser-open prompt message Change "Press ENTER to open in browser..." to "Press ENTER to open the URL in your browser." The old message implied the user should press Enter. The new wording presents it as an available action, not an instruction — users can also scan the QR code or copy-paste the URL. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * style: remove unnecessary arrow wrapper around createMockReadlineInterface https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * docs: explain why Enter keypress is fire-and-forget, not awaited Add a comment explaining that only pollPromise is awaited — the Enter listener is intentionally not part of a Promise.all. This prevents a future refactor from reintroducing the npm bug where authentication blocks until Enter is pressed, even when the user authenticates on another device. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * docs: add permalink to npm's Promise.all bug in comment Link to the specific npm-profile commit (d1a48be4259) so the comment remains accurate even if npm fixes the bug in the future. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix: correct line numbers in npm-profile permalink (L85-L98) https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * style: apply review suggestion for npm-profile permalink format https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * style: remove duplicate line in npm-profile comment https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix: shadow global process instead of renaming to proc Destructure as `process` (not `proc`) so the global `process` is shadowed, preventing accidental direct access to it. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix: merge process fields in test mock contexts Restructure createMockContext to merge process fields instead of replacing the entire object. Tests that only need to override platform or stdin no longer need to redundantly provide the other. Also adds a test for undefined platform (default: case). https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix: use Omit+Partial for process overrides in test mock contexts The process field spread `...overrides?.process` merges at runtime but TypeScript still requires all fields in the override type. Fix by typing the process override as Partial via Omit<..., 'process'> & { process?: Partial<...> }. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * refactor: extract a type alias * refactor: extract MockContextOverrides type alias in remaining tests https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * refactor(auth): extract process types, use NodeJS.Platform, clean up tests - Extract OfferToOpenBrowserProcess interface from inline process type - Extract LoginProcess interface from inline process type in LoginContext - Use NodeJS.Platform instead of string for platform fields (prevents typos) - Rename simulateEnter → simulateEnterKeypress (clarify it's the key) - Convert single-return functions to arrow expressions in tests - Update test descriptions to say "Enter key" / "Enter keypress" https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * refactor(auth): rename offerToOpenBrowser → promptBrowserOpen Per review feedback, "offer to open browser" was mouthful. Renamed function, file, and all associated types (OfferToOpenBrowser* → PromptBrowserOpen*). https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * docs: drop "IMPORTANT" * refactor(auth): extract OtpProcess interface from inline process type https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix(auth): validate authUrl before passing to execFile On Windows, cmd.exe re-parses execFile arguments with full shell grammar, so metacharacters (&, |, ^, etc.) in the URL would be interpreted as operators. Validate that authUrl is a well-formed http(s) URL before passing it to the platform browser command. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * test(auth): add regression test for URLs with query parameters on win32 Verifies that URLs containing & and other query string characters are passed through to execFile as-is on the win32 platform. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix(auth): escape cmd.exe metacharacters in Windows browser open URL On Windows, cmd.exe re-parses execFile arguments and treats & | < > ^ % as operators. Escape these with ^ so query strings in auth URLs (e.g. ?token=abc&redirect=...) are not split by cmd.exe. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix(auth): use canonicalized URL and expand cmd.exe escape set - Use parsedUrl.href (canonicalized by new URL()) instead of the raw authUrl string, ensuring percent-encoding of spaces and special chars. - Expand cmd.exe metacharacter escaping to include () and ! in addition to & | < > ^ %, covering grouping operators and delayed expansion. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * docs(auth): document Windows browser-opening edge cases Explain why cmd /c start is used instead of ShellExecuteW (not callable from Node.js without a native addon), why alternatives like explorer.exe, rundll32, and PowerShell are unreliable, and note that a Rust/N-API addon could replace this in the future. https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW * fix: fix cspell errors in Windows browser-open comment Reword to avoid unknown words "rundll" and "metacharacter". https://claude.ai/code/session_01UtDnjrNQ2Cc3GLAPR8BrrW --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
421d120972 |
perf: use If-Modified-Since for conditional metadata fetches (#11161)
Before fetching package metadata from the registry, stat the local cache file and send its mtime as an If-Modified-Since header. If the registry returns 304 Not Modified, read the local cache instead of downloading the full response body. This saves bandwidth and latency for packages whose metadata hasn't changed since the last fetch. Registries that don't support If-Modified-Since simply return 200 as before, so there is no behavior change for unsupported registries. |
||
|
|
6b3d87a4ca |
perf: optimize undici connection settings and tarball buffering (#11151)
- Enable Happy Eyeballs (`autoSelectFamily`) for faster dual-stack (IPv4/IPv6) connection establishment - Increase keep-alive timeouts (30s idle, 10min max) to reduce connection churn during install - Set optimized global dispatcher so requests without custom options still benefit - Pre-allocate `SharedArrayBuffer` for tarball downloads when `Content-Length` is known, avoiding intermediate chunk array and double-copy |
||
|
|
6c480a4375 |
perf: replace node-fetch with undici (#10537)
Replace node-fetch with native undici for HTTP requests throughout pnpm. Key changes: - Replace node-fetch with undici's fetch() and dispatcher system - Replace @pnpm/network.agent with a new dispatcher module in @pnpm/network.fetch - Cache dispatchers via LRU cache keyed by connection parameters - Handle proxies via undici ProxyAgent instead of http/https-proxy-agent - Convert test mocking from nock to undici MockAgent where applicable - Add minimatch@9 override to fix ESM incompatibility with brace-expansion |
||
|
|
d6b8e281b6 | chore: use pn instead of pnpm (#11124) | ||
|
|
d4a1d734b6 |
feat: pnpm login (#11094)
* refactor: extract web auth QR code and polling into @pnpm/network.web-auth Extract generateQrCode() and pollForWebAuthToken() from releasing/commands into a new shared package so that both `pnpm publish` and the upcoming `pnpm login` can reuse the web-based authentication flow with QR code display and doneUrl polling. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * feat: implement `pnpm login` command Add `pnpm login` (and `pnpm adduser` alias) for authenticating with npm registries. The command: - Tries web-based login first (POST /-/v1/login), displaying a QR code and polling for the token using @pnpm/network.web-auth - Falls back to classic username/password/email login (PUT /-/user/ org.couchdb.user:<username>) when web login is not supported (404/405) - Saves the received auth token to the user's global rc file Also fixes a tsgo build issue in releasing/commands where OtpWebAuthFetchOptions was used as a local type alias but was only available as a re-exported name. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * fix: resolve spellcheck issues in login test https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * fix: correct alphabetical ordering for meta-updater https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * chore: add meta-updater generated tsconfig files https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * fix: add explicit return type to prompt mock for tsgo compatibility https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * fix: use @pnpm/network.fetch instead of globalThis.fetch Switch from globalThis.fetch to fetchWithAgent from @pnpm/network.fetch so that pnpm login respects proxy settings (httpProxy/httpsProxy/noProxy), custom SSL certificates (ca/cert/key), strictSsl, and retry configuration. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor: improve login fetch types and use URL constructor - Type LoginContext.fetch using WebAuthFetchOptions/WebAuthFetchResponse from @pnpm/network.web-auth, extended with text() and wider method - Replace regex-based URL construction with new URL() constructor - Remove redundant LoginFetchInit type https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor: match publish pattern for dependency injection - Static DEFAULT_CONTEXT constant instead of createDefaultContext factory - context = DEFAULT_CONTEXT default parameter instead of context?: Partial - Destructure context in function signatures for natural calling - Use plain fetch from @pnpm/network.fetch (like SHARED_CONTEXT in publish) - Context contains only side-effect functions and modules, not config https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor: use typeof fetch instead of custom fetch types Remove LoginFetchOptions and LoginFetchResponse. Type LoginContext.fetch as typeof fetch from @pnpm/network.fetch directly, eliminating all casts. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * fix: remove placeholder username from login success message Web login doesn't return a username, so just report the registry. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor: use tempDir from @pnpm/prepare instead of manual tmp dirs https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * chore: update tsconfig references for @pnpm/prepare https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor: inject readSettings/writeSettings for fully pure tests Add readSettings and writeSettings to LoginContext so tests need no filesystem side effects. Remove @pnpm/prepare devDependency. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor: remove DEFAULT_CONTEXT from tests, use pure test context Tests now construct their own TEST_CONTEXT with all no-op mocks, eliminating any reliance on real side-effectful functions. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * test: use distinct opts per test, assert URLs and config paths Each test now uses a different registry and configDir to verify URL construction, config key generation, and save path are correct for non-default options. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * test: throw on unexpected mock calls instead of silent fallbacks All mock functions in TEST_CONTEXT now throw on unexpected calls, ensuring tests fail loudly if the code makes unanticipated side effects. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * test: use IANA-reserved example.com domains in test URLs Replace custom.registry.io and private.reg.co with example.com and example.org (RFC 2606 reserved) to prevent domain squatting risks. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * test: use deterministic Date mock instead of native Date https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * test: assert globalInfo calls, throw on unexpected ones Default globalInfo in TEST_CONTEXT now throws. Each test overrides it to capture messages and asserts the expected output. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * fix: use inferred type for fetch url parameter in tests Drop explicit `string` annotation so the parameter matches the `RequestInfo` type expected by the fetch signature. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * fix: resolve type errors in login test mock fetch Use mockResponse helper with `as any` cast to satisfy the Response type, and String(url) for RequestInfo-to-string conversion. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * chore: add tsconfig.lint.tsbuildinfo to .gitignore https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor: replace typeof fetch with explicit LoginFetchResponse/LoginFetchOptions types Derive the fetch signature from actual call-site usage instead of coupling to the concrete @pnpm/network.fetch type. This lets test mocks return plain objects without casts. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * chore: gitignore generated pn/pnpx/pnx artifacts These files are created by setup.js during preinstall and should not be tracked. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor: remove unnecessary backwards-compat aliases from otp.ts Remove Otp-prefixed re-exports (OtpWebAuthFetchOptions, OtpWebAuthFetchResponse, OtpWebAuthTimeoutError) that only existed as backwards-compatibility shims. Update the test to import directly from @pnpm/network.web-auth. Restore the named OtpDate interface that was unnecessarily inlined. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * test(web-auth): add comprehensive unit tests for @pnpm/network.web-auth Add dependency-injected unit tests covering: - WebAuthTimeoutError: properties, code, hint, message - generateQrCode: basic output and input differentiation - pollForWebAuthToken: happy path, fetch argument passing, Retry-After handling (valid, non-finite, null, sub-interval, capped to remaining timeout, timeout during retry wait), error recovery (fetch throws, non-ok response, json parse error, missing token, empty token, multiple consecutive errors), custom timeout, poll interval timing All tests use fake Date.now() and setTimeout — no real timers or side effects. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * fix(web-auth): fix TS2339 compile errors in test assertions Replace `.catch((e: WebAuthTimeoutError) => e)` pattern with `rejects.toMatchObject()` to avoid `string | WebAuthTimeoutError` union type issue when accessing `.timeout` property. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * feat(web-auth,login): extract shared OTP handling and add OTP support to login - Create `withOtpHandling<T>()` in `@pnpm/network.web-auth` that wraps any operation with EOTP challenge detection, web auth flow, and classic OTP prompting. - Refactor `publishWithOtpHandling` to delegate to the shared function. - Add OTP handling to `pnpm login`'s classic (CouchDB) login flow: detects 401 + `www-authenticate: otp` header and retries with the OTP code (or web auth token) in the `npm-otp` header. - Remove overly strict `this: this` constraints from WebAuthFetchResponse interfaces to improve cross-package type compatibility. - Add 13 unit tests for `withOtpHandling` (classic + webauth flows). - Add 4 login OTP tests (classic OTP, webauth OTP, non-401, non-otp 401). https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * fix(login): use word-boundary regex for URL assertion in test Replace `m.includes(url)` with a regex that checks the URL is bounded by whitespace or string boundaries, addressing the CodeQL "incomplete URL substring sanitization" finding. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor(login): use toContainEqual + stringMatching for URL assertion Replace manual `.some()` with Jest's `toContainEqual(expect.stringMatching(...))` for better error messages on failure. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor(web-auth): use expect.any(String) instead of typeof check https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor(web-auth): consolidate multi-property assertions Use toMatchObject and toEqual instead of separate per-property expects. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * docs: explain why npm-auth-type header is sent unconditionally https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor: remove unused re-exports and add missing test coverage Remove dead re-exports of OtpHandlingPromptOptions and OtpHandlingPromptResponse from releasing/commands/src/publish/otp.ts. Add tests for: - LOGIN_MISSING_CREDENTIALS (empty username in classic login) - LOGIN_NO_TOKEN (registry returns success without token) - LOGIN_INVALID_RESPONSE (web login returns incomplete response) - isWebLoginNotSupported with 405 status code https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor(login): rename readSettings/writeSettings to safeReadIniFile/writeIniFile Use the actual function names in the LoginContext interface instead of abstract names, matching the implementations they wrap. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor(otp): remove unnecessary re-exports from otp.ts OtpNonInteractiveError, OtpSecondChallengeError, and OtpHandlingEnquirer were re-exported only for the test file, which can import them directly from @pnpm/network.web-auth. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor(otp): remove unused SHARED_CONTEXT re-export All consumers already import SHARED_CONTEXT directly from ./utils/shared-context.js, making this re-export dead code. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor(login): extract LoginDate and LoginEnquirer interfaces Extract named interfaces for the Date and enquirer members of LoginContext instead of inlining their types. https://claude.ai/code/session_01YHYqGAAmZ1a9XMWoV7nG4S * refactor: stop renaming Claude Code Web didn't rename them thoroughly, so I had to do it myself * docs: correct the lines Why did Claude Code Web misaligned? * refactor: strictly type `LoginFetchOptions.headers` * docs: remove redundant comments * refactor: inline `npm-otp` * refactor: inline `headers` * feat: add `WebLoginError.responseText` * refactor: rename `statusCode` into `httpStatus` * refactor(login): extract ClassicLoginError subclass from PnpmError Extract the LOGIN_FAILED error into a dedicated ClassicLoginError class with httpStatus and responseText properties, matching the WebLoginError pattern. https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * refactor: remove unnecessary import * docs(changeset): correct a changeset * docs(changeset): re-add `releasing.commands` * refactor(web-auth): split monolithic test file into per-module files Split index.test.ts into four files matching the source structure: - WebAuthTimeoutError.test.ts - generateQrCode.test.ts - pollForWebAuthToken.test.ts - withOtpHandling.test.ts https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * refactor: remove unnecessary `as const` * refactor: remove unnecessary `as const` * chore: undo Claude's BS * refactor: extract `LoginEnquirerOptions` * refactor: move types closer to their usesites * refactor: remove simple type alias * fix: type errors * refactor(login): inject readIniFile instead of safeReadIniFile in context The context object should only contain external dependencies. safeReadIniFile is a local wrapper, not an external dependency, so inject readIniFile (from read-ini-file) instead and pass it to safeReadIniFile as a parameter. https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * test(login): add coverage for safeReadIniFile ENOENT handling Test that login succeeds with empty settings when the config file does not exist (ENOENT), and that non-ENOENT errors (e.g. EACCES) are properly propagated. https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * refactor: fix ugliness * refactor: just pass context object * refactor: destructure `context` * refactor: pass the `context` object * refactor: destructure `context` * refactor: pass `context` object directly * refactor: remove unnecessary parenthesis * fix: remove unused import * refactor: remove unnecessary parentheses from single-param arrows in tests https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * refactor: extract `LoginFetchResponseHeaders` * fix(login): remove inline default from --registry option description No other pnpm command includes "(default: ...)" in option descriptions. https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * refactor(tests): enforce realistic mock response behavior - Add createMockResponse helpers that enforce single body consumption (calling text() or json() twice, or both, throws an error) - Default headers.get to throwing on unexpected calls, forcing tests to explicitly provide headers when the code under test reads them - Replace all inline response objects with createMockResponse calls https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * fix: formatting * refactor: reuse * docs: clarify what the error is actually about * docs: consistent error message * refactor: use consistent error message convention in test mocks Capitalize and use "Unexpected call to <thing>" pattern instead of AI-generated "unexpected X call" messages. https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * refactor: expand inline process mock objects to multi-line https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * refactor(login): extract PnpmError subclasses and use stricter test assertions Extract LoginNonInteractiveError, LoginInvalidResponseError, LoginMissingCredentialsError, and LoginNoTokenError subclasses instead of throwing PnpmError directly. Update test assertions to use the const promise pattern with toHaveProperty checks on both code and message, matching the convention used elsewhere in the codebase. https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * refactor: undo ai's nonsensical deletion * refactor: simplify * refactor: rename OtpHandling* types to Otp* for brevity OtpHandlingContext → OtpContext OtpHandlingEnquirer → OtpEnquirer OtpHandlingPromptOptions → OtpPromptOptions OtpHandlingPromptResponse → OtpPromptResponse The OtpHandling prefix was named after the function (withOtpHandling) rather than the domain concept. https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * refactor: extract `OtpDate` * refactor: reuse * fix: eslint * refactor: add OtpRequiredError with body validation and globalWarn - Add OtpRequiredError class with static fromUnknown() that validates the EOTP error body shape and returns either a validated error or an OtpBodyWarning when fields have unexpected types - Add globalWarn to OtpContext so withOtpHandling can warn on bad body shapes instead of silently dropping them - Update throwIfOtpRequired in login.ts to pass raw body through so validation happens in withOtpHandling via fromUnknown - Add tests for bad body shapes (wrong types for authUrl/doneUrl) - Add tests for OtpRequiredError.fromUnknown - Propagate globalWarn through LoginContext, DEFAULT_CONTEXT, SHARED_CONTEXT, and all test mocks https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * docs: remove misleading comment from throwIfOtpRequired The comment referenced downstream machinery (OtpRequiredError.fromUnknown) that the reader shouldn't need to know about at this call site. https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * refactor: replace Object.assign hack with OtpRequiredError in throwIfOtpRequired throwIfOtpRequired now validates the raw response body via OtpRequiredError.fromUnknown and throws a proper OtpRequiredError instead of monkey-patching properties onto a plain Error. withOtpHandling skips re-validation when the caught error is already an OtpRequiredError instance. https://claude.ai/code/session_0191GhgPWiD5TroLMoXAmkaZ * chore(git): revert an imperfect fix This reverts commit |
||
|
|
b1ad9c7d83 |
feat(auth): prepend 'Bearer' to auth token generated by tokenHelper (#11097)
* fix(auth-header): decode _password from base64 for default registry auth * fix(auth): prepend 'Bearer ' to auth token generated by tokenHelper * test: skip flaky parallel dlx test on Node 25 * fix(auth): improve tokenHelper Bearer prefix with validation and generic scheme detection - Throw an error when the token helper returns an empty token instead of producing an invalid "Bearer " header - Use a generic auth scheme regex instead of hardcoding only Bearer/Basic, so other schemes (Token, Negotiate, etc.) are preserved as-is - Add tests for raw token prefixing, existing scheme preservation, and empty token error --------- Co-authored-by: Zoltan Kochan <z@kochan.io> |
||
|
|
fb8962f3a5 |
fix(auth-header): decode _password from base64 for default registry auth (#11089)
* fix(auth-header): decode _password from base64 for default registry auth * refactor: extract basicAuth helper to deduplicate password decoding --------- Co-authored-by: Zoltan Kochan <z@kochan.io> |
||
|
|
cd2dc7d481 |
refactor: prefix internal scripts with . to hide them (#11051)
* fix: ensure PNPM_HOME/bin is in PATH during pnpm setup When upgrading from old pnpm (global bin = PNPM_HOME) to new pnpm (global bin = PNPM_HOME/bin), `pnpm setup` would fail because the spawned `pnpm add -g` checks that the global bin dir is in PATH. Prepend PNPM_HOME/bin to PATH in the spawned process env so the check passes during the transition. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: update pnpm to v11 beta 2 * chore: update pnpm to v11 beta 2 * chore: update pnpm to v11 beta 2 * chore: update pnpm to v11 beta 2 * fix: lint * refactor: rename _-prefixed scripts to .-prefixed scripts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update root package.json to use .test instead of _test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: update action-setup --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
0d88df854f |
chore: update all dependencies to latest versions (#11032)
* chore: update all dependencies to latest versions Update all outdated dependencies across the monorepo catalog and fix breaking changes from major version bumps. Notable updates: - ESLint 9 → 10 (fix custom rule API, disable new no-useless-assignment) - @stylistic/eslint-plugin 4 → 5 (auto-fixed indent changes) - @cyclonedx/cyclonedx-library 9 → 10 (adapt to removed SPDX API) - esbuild 0.25 → 0.27 - TypeScript 5.9.2 → 5.9.3 - Various @types packages, test utilities, and build tools Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update unified/remark/mdast imports for v11/v4 API changes Update imports in get-release-text for the new ESM named exports: - mdast-util-to-string: default → { toString } - unified: default → { unified } Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve typecheck errors from dependency updates - isexe v4: use named import { sync } instead of default export - remark-parse/remark-stringify v11: add vfile as packageExtension dependency so TypeScript can resolve type declarations - get-release-text: remove unused @ts-expect-error directives Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert runtime dependency major version bumps Revert major version bumps for runtime dependencies that are bundled into pnpm to fix test failures where pnpm add silently fails: - bin-links: keep ^5.0.0 (was ^6.0.0) - cli-truncate: keep ^4.0.0 (was ^5.2.0) - delay: keep ^6.0.0 (was ^7.0.0) - filenamify: keep ^6.0.0 (was ^7.0.1) - find-up: keep ^7.0.0 (was ^8.0.0) - isexe: keep 2.0.0 (was 4.0.0) - normalize-newline: keep 4.1.0 (was 5.0.0) - p-queue: keep ^8.1.0 (was ^9.1.0) - ps-list: keep ^8.1.1 (was ^9.0.0) - string-length: keep ^6.0.0 (was ^7.0.1) - symlink-dir: keep ^7.0.0 (was ^9.0.0) - terminal-link: keep ^4.0.0 (was ^5.0.0) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore runtime dependency major version bumps Re-apply all runtime dependency major version bumps that were previously reverted. All packages maintain their default exports except isexe v4 which needs named imports. Updated runtime deps: - bin-links: ^5.0.0 → ^6.0.0 - cli-truncate: ^4.0.0 → ^5.2.0 - delay: ^6.0.0 → ^7.0.0 - filenamify: ^6.0.0 → ^7.0.1 - find-up: ^7.0.0 → ^8.0.0 - isexe: 2.0.0 → 4.0.0 (fix: use named import { sync }) - normalize-newline: 4.1.0 → 5.0.0 - p-queue: ^8.1.0 → ^9.1.0 - ps-list: ^8.1.1 → ^9.0.0 - string-length: ^6.0.0 → ^7.0.1 - symlink-dir: ^7.0.0 → ^9.0.0 - terminal-link: ^4.0.0 → ^5.0.0 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert tempy to 3.0.0 to fix bundle hang tempy 3.2.0 pulls in temp-dir 3.0.0 which uses async fs.realpath() inside its module init. When bundled by esbuild into the __esm lazy init pattern, this causes a deadlock during module initialization, making the pnpm binary hang silently on startup. Keeping tempy at 3.0.0 which uses temp-dir 2.x (sync fs.realpathSync). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add comment explaining why tempy cannot be upgraded Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert nock to 13.3.4 for node-fetch compatibility nock 14 changed its HTTP interception mechanism in a way that doesn't properly intercept node-fetch requests, causing audit tests to hang waiting for responses that are never intercepted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add comment explaining why nock cannot be upgraded Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update symlink-dir imports for v10 ESM named exports symlink-dir v10 removed the default export and switched to named exports: { symlinkDir, symlinkDirSync }. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: revert @typescript/native-preview to working version Newer tsgo dev builds (>= 20260318) have a regression where @types/node cannot be resolved, breaking all node built-in types. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: vulnerabilities * fix: align comment indentation in runLifecycleHook Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: pin msgpackr to 1.11.8 for TypeScript 5.9 compatibility msgpackr 1.11.9 has broken type definitions that use Iterable/Iterator without required type arguments, causing compile errors with TS 5.9. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
1701a65845 |
chore: reduce noisy warnings in test output (#11022)
* chore: reduce noisy warnings in test output - Suppress ExperimentalWarning and DEP0169 via --disable-warning in NODE_OPTIONS - Fix MaxListenersExceededWarning by raising limit in StoreIndex when adding exit listeners - Update meta-updater to generate the new _test scripts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: stop streaming pnpm subprocess output during CLI tests Buffer stdout/stderr from execPnpm instead of writing to the parent process in real time. Output is still included in the error message on failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: pipe all subprocess output in CLI tests Use stdio: 'pipe' for all pnpm/pnpx spawn helpers so subprocess output is buffered instead of printed. Output is still included in error messages on failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove duplicate @pnpm/installing.env-installer in pnpm/package.json Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: use pipe stdio in dlx and errorHandler tests Replace stdio: 'inherit' and [null, 'pipe', 'inherit'] with 'pipe' to prevent subprocess output from leaking into test output. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: skip maxListeners adjustment when set to unlimited (0) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
dba4153767 |
refactor: rename packages and consolidate runtime resolvers (#10999)
* refactor: rename workspace.sort-packages and workspace.pkgs-graph - workspace.sort-packages -> workspace.projects-sorter - workspace.pkgs-graph -> workspace.projects-graph Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: rename packages/ to core/ and pkg-manifest.read-package-json to reader - Rename packages/ directory to core/ for clarity - Rename pkg-manifest/read-package-json to pkg-manifest/reader (@pnpm/pkg-manifest.reader) - Update all tsconfig, package.json, and lockfile references Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: consolidate runtime resolvers under engine/runtime domain - Remove unused @pnpm/engine.runtime.node.fetcher package - Rename engine/runtime/node.resolver to node-resolver (dash convention) - Move resolving/bun-resolver to engine/runtime/bun-resolver - Move resolving/deno-resolver to engine/runtime/deno-resolver - Update all package names, tsconfig paths, and lockfile references Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: update lockfile after removing node.fetcher Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: sort tsconfig references and package.json deps alphabetically Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: auto-fix import sorting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: update __typings__ paths in tsconfig.lint.json for moved resolvers Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove deno-resolver from deps of bun-resolver --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
4a36b9a110 |
refactor: rename internal packages to @pnpm/<domain>.<leaf> convention (#10997)
## Summary Rename all internal packages so their npm names follow the `@pnpm/<domain>.<leaf>` convention, matching their directory structure. Also rename directories to remove redundancy and improve clarity. ### Bulk rename (94 packages) All `@pnpm/` packages now derive their name from their directory path using dot-separated segments. Exceptions: `packages/`, `__utils__/`, and `pnpm/artifacts/` keep leaf names only. ### Directory renames (removing redundant prefixes) - `cli/cli-meta` → `cli/meta`, `cli/cli-utils` → `cli/utils` - `config/config` → `config/reader`, `config/config-writer` → `config/writer` - `fetching/fetching-types` → `fetching/types` - `lockfile/lockfile-to-pnp` → `lockfile/to-pnp` - `store/store-connection-manager` → `store/connection-manager` - `store/store-controller-types` → `store/controller-types` - `store/store-path` → `store/path` ### Targeted renames (clarity improvements) - `deps/dependency-path` → `deps/path` (`@pnpm/deps.path`) - `deps/calc-dep-state` → `deps/graph-hasher` (`@pnpm/deps.graph-hasher`) - `deps/inspection/dependencies-hierarchy` → `deps/inspection/tree-builder` (`@pnpm/deps.inspection.tree-builder`) - `bins/link-bins` → `bins/linker`, `bins/remove-bins` → `bins/remover`, `bins/package-bins` → `bins/resolver` - `installing/get-context` → `installing/context` - `store/package-store` → `store/controller` - `pkg-manifest/manifest-utils` → `pkg-manifest/utils` ### Manifest reader/writer renames - `workspace/read-project-manifest` → `workspace/project-manifest-reader` (`@pnpm/workspace.project-manifest-reader`) - `workspace/write-project-manifest` → `workspace/project-manifest-writer` (`@pnpm/workspace.project-manifest-writer`) - `workspace/read-manifest` → `workspace/workspace-manifest-reader` (`@pnpm/workspace.workspace-manifest-reader`) - `workspace/manifest-writer` → `workspace/workspace-manifest-writer` (`@pnpm/workspace.workspace-manifest-writer`) ### Workspace package renames - `workspace/find-packages` → `workspace/projects-reader` - `workspace/find-workspace-dir` → `workspace/root-finder` - `workspace/resolve-workspace-range` → `workspace/range-resolver` - `workspace/filter-packages-from-dir` merged into `workspace/filter-workspace-packages` → `workspace/projects-filter` ### Domain moves - `pkg-manifest/read-project-manifest` → `workspace/project-manifest-reader` - `pkg-manifest/write-project-manifest` → `workspace/project-manifest-writer` - `pkg-manifest/exportable-manifest` → `releasing/exportable-manifest` ### Scope - 1206 files changed - Updated: package.json names/deps, TypeScript imports, tsconfig references, changeset files, renovate.json, test fixtures, import ordering |
||
|
|
f47ef4b125 |
refactor: reorganize monorepo domain structure (#10987)
Reorganize the monorepo's top-level domain directories for clarity: - pkg-manager/ split into: - installing/ (core, headless, client, resolve-dependencies, etc.) - installing/linking/ (hoist, direct-dep-linker, modules-cleaner, etc.) - bins/ (link-bins, package-bins, remove-bins) - completion/ merged into cli/ - dedupe/ moved to installing/dedupe/ - env/ renamed to engine/ with subdomains: - engine/runtime/ (node.fetcher, node.resolver, plugin-commands-env, etc.) - engine/pm/ (plugin-commands-setup, plugin-commands-self-updater) - env.path moved to shell/ - tools/ and runtime/ dissolved - reviewing/ and lockfile audit packages moved to deps/: - deps/inspection/ (list, outdated, dependencies-hierarchy) - deps/compliance/ (audit, licenses, sbom) - registry/ moved to resolving/registry/ - semver/peer-range moved to deps/ - network/fetching-types moved to fetching/ - packages/ slimmed down, moving packages to proper domains: - calc-dep-state, dependency-path -> deps/ - parse-wanted-dependency -> resolving/ - git-utils -> network/ - naming-cases -> text/ - make-dedicated-lockfile -> lockfile/ - render-peer-issues -> installing/ - plugin-commands-doctor -> cli/ - plugin-commands-init -> workspace/ Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
5d5818e44f |
style: enforce node: protocol for builtin imports (#10951)
Add n/prefer-node-protocol rule and autofix all bare builtin imports to use the node: prefix. Simplify the simple-import-sort builtins pattern to just ^node: since all imports now use the prefix. |
||
|
|
1c8c4e49f5 |
style: add eslint-plugin-simple-import-sort (#10947)
Add eslint-plugin-simple-import-sort to enforce consistent import ordering: - Node.js builtins first - External packages second - Relative imports last - Named imports sorted alphabetically within each statement |
||
|
|
01914345d5 |
build: enable @typescript-eslint/no-import-type-side-effects (#10630)
* build: enable `@typescript-eslint/no-import-type-side-effects` * build: disable `@typescript-eslint/consistent-type-imports` * chore: apply fixes for `no-import-type-side-effects` pnpm exec eslint "**/src/**/*.ts" "**/test/**/*.ts" --fix |
||
|
|
e18a879d72 | feat!: drop Node.js 22.12 support | ||
|
|
1b4df57a01 | feat!: drop Node.js 20 and 21 support (#10569) | ||
|
|
bb8baa7cff |
fix(npm-resolver): request full metadata for optional dependencies (#10455)
close #9950 --------- Co-authored-by: Zoltan Kochan <z@kochan.io> |
||
|
|
a00f9e515c | chore: use typescript-go (#10452) |