A performance- and correctness-focused patch release. Cold installs get a streaming tarball pipeline, Linux gets an O_TMPFILE+linkat CAS fast path, and the resolver's cold path overlaps DNS, TLS, and packument prefetch with the manifest/workspace/lockfile work that used to serialize them. On the fix side, aube run once again finds node-gyp for package scripts, and aube update / aube outdated stop trying to fetch unpublished workspace: deps from the registry.
Added
-
Pre-resolver packument prefetch + shared HTTP utilities (#529 by @imjustprism) — a new
aube-util::httpmodule consolidates client-side primitives (prewarm,priority,race,resolve,ticket_cache) so leaf crates share one warm-pool surface with consistent killswitch semantics. On install entry, aube now readspackage.jsonand fires fire-and-forget packument GETs for every registry-shaped direct dep before workspace yaml load, settings resolve, lockfile parse, and resolver construction — by the time the resolver pops its first task, the packument cache and reqwest pool are warm.RegistryClient::prewarm_connectionnow covers the default registry plus every scoped (@org:registry=...) and per-uri auth registry, with parallel DNS preresolve so DNS RTT hides behind the TLS handshake. Abbreviated packument GETs also sendPriority: u=0(RFC 9218 Critical) so H2 schedulers prioritize resolver-blocking metadata over pending tarball frames. New killswitches:AUBE_DISABLE_DNS_PRERESOLVE,AUBE_DISABLE_REQUEST_RACING,AUBE_DISABLE_PREFETCH,AUBE_DISABLE_TLS_TICKET_CACHE. Prefetch is a no-op when offline or when any lockfile is present. -
Cold install pipeline overhaul (#522 by @imjustprism) — several overlapping wins on the cold-cache path:
- Streaming tarball pipeline (opt-in via
AUBE_TARBALL_STREAM=1, killswitchAUBE_DISABLE_TARBALL_STREAM) — HTTP body chunks pipe through SHA-512 + gz + tar + CAS via an mpsc bridge instead of buffering the whole tarball; non-SHA-512 SRI falls back to buffered. Bounded by the registry'starball_max_bytescap. - Linux
O_TMPFILE+linkatCAS publish withEOPNOTSUPPfallback to the tempfile path,posix_fallocateto avoid ext4 fragmentation, andposix_fadvise(DONTNEED)to free page cache after publish. Killswitch:AUBE_DISABLE_O_TMPFILE. - Materialize-stream into the lockfile fast path — both lockfile and no-lockfile branches now share the GVS prewarm materializer, hiding 30-200ms of GVS reflinks behind the in-flight download tail.
- Resolver tuning — foldhash on
graph_hashhot maps, pre-sized resolver caches, thread-localnode_semver::Versionparse cache,PARALLEL_IMPORT_THRESHOLDlowered from 256 to 16 (median npm tarball is 7 files), and pinned tokioworker_threads(cpu.min(8)) /max_blocking_threads(64)(tunable viaAUBE_TOKIO_WORKERS/AUBE_TOKIO_BLOCKING). - Windows gets
FILE_ATTRIBUTE_NOT_CONTENT_INDEXEDon the store root; cross-volume detection (drive letters on Windows,devid on Unix) is gated per-platform.
Reported same-volume Windows cold-install ratios: 1.80x-8.75x faster than Bun across svelte/vite/next/babylon.
- Streaming tarball pipeline (opt-in via
-
Per-project materialize pipelined into fetch (#527 by @imjustprism) — when GVS is off, each fetched
(canonical_key, PackageIndex)triggersmaterialize_intoagainst.aube/<dep_path>/immediately, so by the time fetch finishes the dedicated link phase only has to create top-levelnode_modules/<name>symlinks. The driver now usesJoinSetinstead ofVec<JoinHandle>, so on early-return all in-flight tasks abort instead of detaching and racing install cleanup. ~10% improvement on warm fresh installs in the local benchmark matrix.
Fixed
-
aube run/aube testfindnode-gyp(#518 by @jdx) — package scripts only hadnode_modules/.binprepended toPATH, soaube testwould fail withnode-gyp: not foundon hosts that didn't already ship it. Script execution now reuses aube's existing node-gyp bootstrap (via a lazy shim bin dir +AUBE_NODE_GYP_EXE/AUBE_NODE_GYP_PROJECT_DIR), matching pnpm/npm behavior. Ports pnpm'slifecycleScripts.ts:128coverage into the offline node-gyp bootstrap bats suite. -
workspace:deps inaube update/aube outdated(#523 by @jdx, fixes #520) —aube updatenow discovers workspace packagename/versionpairs and passes them into resolver workspace resolution soworkspace:deps frompackage.json#workspacesresolve locally instead of triggering registry packument fetches.aube outdatedfilters out direct deps withworkspace:specifiers and reports "no matching dependencies" rather than attempting a packument fetch. Adds a newWARN_AUBE_WORKSPACE_PACKAGE_MISSING_NAMEwarning code for workspace packages without anamefield. -
Resolver peer-context divergence is fatal (#522 by @imjustprism) —
apply_peer_contextshittingMAX_ITERATIONSused to log a warning and ship a broken graph; it now returns a fatalError::PeerContextDivergence(usize).state::remove_stateerrors at--forceand GVS-transition sites also propagate instead of being silently swallowed, so permission-denied or Windows-locked sidecars no longer defeat the freshness check. -
Tarball hardening (#522 by @imjustprism) — entries declared as 0 bytes with non-zero stream payload are now rejected (synthetic-entry injection guard), and GNU
LongName/LongLinkmetadata records are correctly accepted. -
Patches loaded once per cwd (#529 by @imjustprism) —
load_patches_for_linkerwalkedpatches/from disk 2-3 times per install (lockfile-prewarm, no-lockfile-prewarm, and link-phase sites). Now cached per cwd viaOnceLock<Mutex<HashMap<PathBuf, ...>>>.
Full Changelog: v1.9.0...v1.9.1
💚 Sponsor aube
aube is part of en.dev — an independent developer-tooling studio run by @jdx, also behind mise. Work on aube is funded entirely by sponsors.
If aube is saving your team install time or CI minutes, please consider sponsoring at en.dev. Individual and company sponsorships are what keep the project fast, free, and independent.