* 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>
- `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.
- 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>
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.
- 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
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
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.
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