Skip to main content

安装加速

除了作为一个注册表,pnpr 还可以通过在服务器端解析项目的依赖图,并向 pnpm 提供一个可直接使用的锁文件,从而加速 pnpm install。然后 pnpm 进行正常的无头安装,直接并行地从各个注册表获取每个压缩包。

🌐 Besides acting as a registry, pnpr can speed up pnpm install by resolving a project's dependency graph server-side and handing pnpm a ready-to-use lockfile. pnpm then runs a normal headless install, fetching every tarball directly from the registries in parallel.

为什么它更快

🌐 Why it's faster

安装一个全新项目(没有锁文件或有新依赖)的慢步骤是解析,而不是下载压缩包。为了解析依赖图,pnpm 必须逐步处理:获取包的元数据(称为“packument”)、读取其依赖范围、获取这些依赖的 packument,如此类推,一层一层地进行。每一层依赖于上一层的结果,因此这些请求不能完全并行化——总时间大致为图的深度 × 到注册表的往返延迟。从注意本电脑或远离注册表的 CI 运行器来看,这种往返深度是安装时间的主要因素。

🌐 The slow part of installing a fresh project (one with no lockfile, or with new dependencies) is resolution, not downloading tarballs. To resolve a dependency graph, pnpm has to walk it: fetch a package's metadata (its "packument"), read its dependency ranges, fetch the packuments for those, and so on, level by level. Each level depends on the answers from the previous one, so the requests can't all be parallelized — the total time is roughly the depth of the graph × the round-trip latency to the registry. From a laptop or a CI runner that sits far from the registry, that round-trip depth dominates the install.

pnpr 折叠那个深度:

🌐 pnpr collapses that depth:

  • It runs the resolution close to the registry, with low latency to the upstream, so each of those sequential round-trips is cheap.

  • It keeps a warm, shared cache of packuments and verified resolution results across every client that uses it. A team or CI fleet pointed at one pnpr server resolves against a cache that's already hot from everyone else's installs.

  • It returns the whole resolved lockfile in a single response, so the client pays one round-trip for resolution instead of one per graph level.

关键是,pnpr 只进行解析。Tarball 仍然由客户端获取,直接从注册表并行获取——就像正常安装一样,而通过服务器的单一连接永远无法匹配。加速路径消除了受延迟影响的解析瀑布,而不会将 tarball 下载变成单一的服务器中介流。

🌐 Crucially, pnpr only does the resolution. The tarballs are still fetched by the client, directly from the registries in parallel — exactly like a normal install, which a single connection through the server could never match. The accelerated path removes the latency-bound resolution waterfall without turning tarball downloads into a single server-mediated stream.

当项目已经有一个最新的锁定文件时,没有需要解决的内容,因此这条路径不适用——加速主要用于全新安装和依赖变更。

🌐 When the project already has an up-to-date lockfile, there's nothing to resolve, so this path doesn't apply — the speedup is for cold installs and dependency changes.

它是如何运作的

🌐 How it works

  1. pnpm 检查 GET /-/pnpr 以验证服务器是否使用兼容的 pnpr 解析器协议版本。
  2. pnpm 会将项目的依赖、注册表设置、策略设置以及现有锁文件(如果有的话)发送 POST /-/pnpr/v0/resolve。客户端的上游凭据从不发送——私有注册表通过服务器自身的凭据访问(见下文)。
  3. 服务器根据客户端的注册表进行解析,按照客户端的策略验证输入的锁文件,并流式传输 application/x-ndjson 响应。随着树遍历的进行,package 帧会宣布已解析的 tar 包,终端 done 帧携带解析后的锁文件和统计信息。终端 violationserror 帧会中止安装。
  4. pnpm 会写入解析后的锁文件,并像正常安装一样直接从注册表获取 tar 包。

对于已经具有最新锁文件的冻结恢复,pnpm 可以使用 POST /-/pnpr/v0/verify-lockfile 仅获取服务器端的信任判定,而不是再次解析。

🌐 For frozen restores with an already-fresh lockfile, pnpm can use POST /-/pnpr/v0/verify-lockfile to get only the server-side trust verdict instead of resolving again.

请参阅 pnpm/pnpm#12230pnpm/pnpm#12234 了解背景。

🌐 See pnpm/pnpm#12230 and pnpm/pnpm#12234 for background.

服务器将访问哪些注册表

🌐 Which registries the server will touch

解析器仅从运算符配置的源获取资源——一个由配置的注册表、声明的公共路由、内置官方 npm 注册表,以及 pnpr 自有源组成的默认拒绝许可列表。任何 registry/namedRegistries 的请求——或其直接的 http(s)/git URL 依赖、覆盖项或锁文件 tarball URL——指向其他地方的请求,在任何服务器端获取之前都会被拒绝,并且每一次重定向跳转都会根据相同的许可列表重新检查。调用者不能使用解析器让服务器从任意内部地址获取资源。

🌐 The resolver only fetches from origins the operator has configured — a default-deny allowlist made of the configured registries, the declared public routes, the built-in official npm registry, and pnpr's own origin. A request whose registry/namedRegistries — or whose direct http(s)/git URL dependencies, overrides, or lockfile tarball URLs — point anywhere else is rejected before any server-side fetch, and every redirect hop is re-checked against the same allowlist. A caller can't use the resolver to make the server fetch from an arbitrary internal address.

私有注册表

🌐 Private registries

客户端从不将他们的上游注册表凭据转发给 pnpr。相反,在服务器端配置了一个私有注册表作为上游注册表,使用服务器拥有的凭据以及一个 access: 策略来指定哪些 pnpr 用户可以通过它进行解析。调用者使用自己的 pnpr 令牌向 pnpr 进行身份验证;pnpr 为每次上游获取选择自己的凭据。上游令牌仅由服务器持有,可服务整个团队。

🌐 Clients never forward their upstream registry credentials to pnpr. Instead, a private registry is configured server-side as an upstream registry with a server-owned credential and an access: policy naming which pnpr users may resolve through it. The caller authenticates to pnpr with their own pnpr token; pnpr selects its own credential for each upstream fetch. One upstream token, held only by the server, serves a whole team.

解析后的锁文件中的 tar 包 URL 遵循相同的拆分方式:

🌐 Tarball URLs in the resolved lockfile follow the same split:

  • A public package keeps its upstream URL — the client fetches it directly from the registry or its CDN, never through pnpr.

  • A private package resolved through an access-bearing upstream registry is emitted as that registry's /~<name>/ endpoint URL, so the client fetches it back through pnpr (which enforces the registry's access policy and serves it from a per-registry private cache).

因为对于其注册表配置指向它的客户端来说,注册表端点 URL 是规范的,所以锁文件条目保持仅完整性——注册表主机存在于客户端的 .npmrc 中,而不在锁文件中——并且无论项目是通过 /resolve 安装还是直接对注册表端点安装,都将解析到相同的锁文件。

🌐 Because a registry endpoint URL is canonical for a client whose registry configuration points at it, lockfile entries stay integrity-only — the registry host lives in the client's .npmrc, not in the lockfile — and a project resolves to the same lockfile whether it installs through /resolve or directly against the registry endpoint.

解析缓存是具有授权意识的:仅涉及公共路由的解析会被缓存一次,并由所有调用者共享,而涉及私有路由的解析则根据所需的访问权限进行键控,并且仅在调用者在当前上游凭据下仍拥有该访问权限时才会重放。轮换上游凭据会自动将新的解析移动到新的缓存命名空间。

🌐 The resolution cache is authorization-aware: a resolution that touched only public routes is cached once and shared by every caller, while a resolution that touched private routes is keyed by the access it required and is only replayed for callers who still hold that access at the current upstream credential. Rotating an upstream credential automatically moves new resolutions to a fresh cache namespace.

启用它

🌐 Enabling it

在你的 pnpm-workspace.yaml 中将 pnprServer 设置为 pnpr 服务器的 URL:

🌐 Set pnprServer in your pnpm-workspace.yaml to the URL of a pnpr server:

pnprServer: http://127.0.0.1:7677

pnpm install 期间,pnpm 将把解析任务卸载到该服务器。解析器 POST 端点需要有效的 pnpr Authorization 头。pnpm 会转发为 pnprServer URL 配置的认证信息,并且普通的 pnpm login --registry <server-url> 会写入 pnpm 用于注册表和解析器请求的令牌。登录和令牌端点始终可用,因此即使是仅提供解析器服务且不暴露注册表界面的 pnpr 服务器,这也仍然可行。

🌐 pnpm will offload resolution to that server during pnpm install. The resolver POST endpoints require a valid pnpr Authorization header. pnpm forwards the auth configured for the pnprServer URL, and a normal pnpm login --registry <server-url> writes the token pnpm uses for both registry and resolver requests. The login and token endpoints are always served, so this works even against a resolver-only pnpr server that exposes no registry surface.