Pipe the whole response through createGzip(level 1) on the server,
createGunzip on the client. Better compression ratio (cross-file
context), simpler code (no per-file gzip flag), and one decompress
pipe instead of thousands of gunzipSync calls.
274MB uncompressed → ~80MB over the wire. Negligible overhead on
localhost, significant savings on remote servers.
Two bugs that caused headless install to re-download from npm:
1. Client parsed I line key incorrectly: the key format is
"integrity\tpkgId\tbase64" but the parser split at the first tab,
giving only the integrity hash as the key. Fixed to split at the
last tab so key = "integrity\tpkgId".
2. After writing index entries, the WAL wasn't checkpointed. Other
SQLite connections (in worker threads) used stale WAL indexes.
Added StoreIndex.checkpoint() which runs PRAGMA wal_checkpoint.
Also removed gzip from /v1/files streaming (was causing corrupt
header errors) and removed debug logging.
Add an opt-in pnpm agent server that resolves dependencies server-side
and streams only the files missing from the client's store.
Server (@pnpm/agent.server):
- Multi-process HTTP server (Node.js cluster, 9 workers)
- SQLite-backed metadata cache — resolution in ~1s vs ~3.4s with .jsonl
- Streaming NDJSON /v1/install — file digests emitted as packages resolve
- Gzip-compressed streaming /v1/files — no buffering on server or worker
- Binary protocol with server-provided digests (no client rehashing)
Client (@pnpm/agent.client):
- Streaming NDJSON parser dispatches worker batches during resolution
- Worker-thread streaming HTTP + gzip decompress + CAFS writes
- Pre-packed msgpack store index entries written directly to SQLite
- Pipelined headless install via wrapped store controller
Config: `agent: "http://host:port"` in pnpm-workspace.yaml