github nubjs/nub v0.10.0
Nub 0.10.0

latest release: v0.10.0-canary.20261106.579
2 hours ago

Nub 0.10.0 brings dependency build scripts and --filter in line with pnpm 12, writes pnpm lockfiles that pnpm accepts in more layouts, and backfills util.debounce, util.throttle and the JSON.parse options proposal.

Important

Five changes can surface in an existing project:

  • An unapproved dependency build script fails the install with ERR_NUB_IGNORED_BUILDS and exit 1. strictDepBuilds=false restores the warning (#1050)
  • A git dependency with a prepare script builds only when approved, and otherwise fails with ERR_NUB_GIT_DEP_PREPARE_NOT_ALLOWED (#1045)
  • --filter ./packages selects only a project whose directory is packages. Use ./packages/* or ./packages/** for the projects under it (#1056)
  • A --filter that selects no project exits 0, and --fail-if-no-match exits 1 (#1064)
  • nub rebuild <pkg> no longer approves the package it names (#1050)

Dependency build scripts

An install that meets a build script nobody has approved now fails, as under pnpm 12. It fails last, once node_modules is linked and the approved builds and the project's own scripts have run. Global installs and nubx stay non-strict.

$ nub install
dependencies:
+ core-js@3.40.0

ERR_NUB_IGNORED_BUILDS

  × Ignored build scripts: core-js@3.40.0
  help: Run `nub approve-builds` to pick which dependencies may run their
        build scripts, or set `strictDepBuilds=false` to install without them.
Area What changed Link
Denials nub approve-builds '!pkg' and --allow-build='!pkg' record a denial, and --allow-build=pkg refuses a package the project denies. An explicit false outranks dangerouslyAllowAllBuilds. #1050
Settings dangerouslyAllowAllBuilds and strictDepBuilds are read from .npmrc, the environment and, in a pnpm project, pnpm-workspace.yaml. #1050
nub rebuild Reports unapproved packages instead of passing over them. A filtered rebuild works in a shared-lockfile workspace, where it did nothing. #1050
Member approvals nub rebuild, nub ignored-builds and nub approve-builds read the same approvals as nub install, workspace members' included. #1050
Git dependency approval Approved by <name>@git+<repository> or the same key with #<commit>. A bare name, name@version or * does not approve one, and a denial installs it unbuilt. #1045
Git dependency builds A hosted dependency such as rimraf@github:isaacs/rimraf#v6.1.3 is now built. It linked with no dist/. The build no longer needs cp, which stock Windows lacks. #1045, fixes #885, #886
Project lifecycle stages Project scripts follow pnpm 12's stage lists in every project: install adds preprepare and postprepare, remove runs the uninstall stages, and --prod skips the prepare family. #1052

Workspace filters

Nub now uses pnpm 12's selector grammar and directory globs on every workspace command, including nub run (#1056).

- nub run --filter ./packages build
+ nub run --filter './packages/*' build
Area What changed Link
Empty selection Does nothing and exits 0. install, remove and update print Scope: 0 of N workspace projects, and nub ci still cleans node_modules first, as pnpm's does. #1064
Workspace root run, exec, add and test drop the root from a selection that names no project. Other commands keep it. #1056
Repeated filtered install Reports up to date. It reinstalled and reran lifecycle scripts. #1055
recursiveInstall Read from pnpm-workspace.yaml in a pnpm project. #1055
Flags before the verb --filter-prod, -C and --dir work there. #1064
nub ls in a member Lists that member. #1064

Lockfile compatibility

Area What changed Link
Per-project pnpm lockfiles Under shared-workspace-lockfile=false, a member's lockfile matches pnpm 10's bytes on the tested workspaces. pnpm's --frozen-lockfile rejected the old form. #1060
Frozen installs of those trees nub install --frozen-lockfile passes on a tree Nub or pnpm 10 wrote, and a plain install keeps a member's locked versions. It failed on the first and re-resolved on the second. #1062
Rebuild in those trees nub rebuild finds a member's packages in the root store. It built nothing and exited 0. #1058
pnpm's env document pnpm-lock.yaml records a pinned pnpm in its leading document, so pnpm 11 and 12 accept Nub's lockfile under --frozen-lockfile, including after nub pm use pnpm. #1054
Local paths in pnpm-lock.yaml link:./x is written link:x and file:./x is written file:x, and empty packages: and snapshots: are omitted. A package spelled with ./ relinks on its first install after upgrading. #1079
A file: dependency outside the workspace A member's file: directory above the workspace root installs under nub ci and --frozen-lockfile. It failed with is not a directory. #1004
package-lock.json An optional dependency of a dev dependency is flagged dev and optional. It was devOptional. #1088, fixes #1087
Optional peers Every resolved optional peer is linked next to its package, a plain install repairs an existing nub.lock, and bun.lock records peers as bun writes them. #1040, fixes #937
Catalog updates nub update -r raises a ^ or ~ catalog entry to the version it resolved, and a catalog-only lockfile change is written instead of skipped. #1057
Overrides on catalog: A range-keyed override matches a catalog: dependency by its reference, as under pnpm. #1039, fixes #771

Package manager

Area What changed Link
nub pack and nub publish Never pack a file from outside the package. A symlinked or absolute main, or a symlinked README, put that file in the tarball. #1036
workspace: specs Both commands resolve workspace: and catalog: specs and apply publishConfig. The specs shipped verbatim, which made the package uninstallable. #1036, fixes #817
nub update at a workspace root Keeps members' locked versions. It re-resolved every member and bumped packages nobody named. #1043, fixes #1001
patchedDependencies Git renames, header-only deletions, empty new files and mode changes apply as under pnpm 11. A patched executable keeps its mode. #1037, fixes #844, #845
nub patch-commit Replaces the patch file with one consolidated patch instead of appending a section per round. nub patch resolves its target through the lockfile. #1010
Workspace .npmrc A command run in a member reads the root's registry and auth. The member's file layers on top under pnpm 10, Yarn 1 and a Nub project, and is not read under pnpm 11, npm or Bun. #1051
DNS Hosts resolve through the OS with an in-process cache, so VPN-scoped DNS, /etc/resolver/* and Windows NRPT apply. #953, fixes #942
Broken downloads A tarball whose connection breaks off mid-body is downloaded again. It failed the install. #1078
Failed installs An install that fails after a virtual-store mode change keeps node_modules. It removed the tree before resolving. #1035, fixes #841
--offline Refuses an uncached full packument fetch, which went to the network. #1059
file: dependencies on macOS Two projects with a file: dependency at the same relative path get their own files. They shared whichever installed first, and a tarball rebuilt in place stayed stale. #995
Overrides to a local copy A registry package whose dependency is overridden to a file: path is ejected into the project. The link dangled and require failed with Cannot find module. #1080
Store locks on NFS Prune, maintenance and GC lock files open readable, so shared locks work on Linux NFS. They failed with EBADF. #1046, fixes #1028
Add-time refusals --allow-similar-name and --allow-new-name each clear their own check, and every refusal names its flag and allowedUnpopularPackages. #1074
Aliases and bins A dependency under its own name keeps its bin over an alias of the same package, so tsc stays with typescript when typescript5 is also installed. #1074
Remix 3 ^3.0.0, 3.1.0 and >=2 keep the shared store. Only a spec that may select a 3.0.0 prerelease gets a project-local store. #1042, fixes #927
pnpm 12 through nub pm shim The pnpm and pnpx shims run pnpm 12. They failed with SyntaxError: Unexpected identifier 'is'. #983
Mismatched nub pm shim projects Registry and account commands such as npm login pass through to the system manager instead of being refused. #1021
nub pm use nub The Turborepo heads-up says which versions recognize Nub. #1034
Peer placements A package with peer-dependent copies reuses its file index and verified store read. Remapping across eight peer contexts went from 1.647 ms to 1.411 ms in a stage benchmark on Linux x86-64. #984

Runtime

Area What changed Link
util.debounce, util.throttle Backfilled from Node 26.10 on older supported versions, and typed in @nubjs/types. #974
JSON.parse options JSON.parse(text, { freeze, preferNullPrototype }) is polyfilled from the Stage 2.7 proposal, and typed in @nubjs/types. #992
YAML and TOML imports Values keep NaN, -0, dates and own __proto__ keys. A TOML date is a Date, and a TOML integer past 2^53 is a BigInt. It threw on import. #1067
UV_THREADPOOL_SIZE Assigning it in process.env before the first pool use sizes the pool, as under Node. Nub's preload had already built the pool. #1013
Temporal The polyfilled global has the spec's property attributes. It was a frozen module namespace. #1015
require() of a replaced package @js-temporal/polyfill, urlpattern-polyfill and abort-controller load the installed package. They failed with ENOENT on Node 22.15+. #1061
Decorated files 400 decorated ESM files on Node 26.11 start in 0.18s, down from 0.99s, on macOS arm64 with a warm transpile cache. #796, fixes #795
Startup Augmented startup no longer loads internal/file on Node 20+. #556
Entry tsconfig check The check follows the entry's real path and skips a dependency's own config. A workspace bin run through its node_modules link failed before starting. #1041, fixes #1032
Unreadable tsconfig The diagnostic names the OS error. It read the same as a missing file. #899
Varlock Nub stands down when Varlock already resolved the schema or is the program being launched. varlock run --path ./config exited 1. #724
Terminal jobs A child that stops on its first terminal read resumes after the foreground handoff. It stayed stopped. #998
Test and watch modes --test=<value>, --watch=<value>, --watch_path and a watch flag in NODE_OPTIONS stay out of the children they start, which could recurse without end. nodeOptions in nub.jsonc refuses a watch flag. #1075, #1086

Node discovery and provisioning

Area What changed Link
latest and other aliases Aliases and partial pins select releases only. A .nvmrc of latest failed with a 404 once nodejs.org listed a 27.0.0 alpha. #1033
Version-manager shims The version behind a mise, asdf, nodenv or Volta shim is read on every launch. A cached version passed Node 26 flags to Node 24, which exited 9. #1049
Launch loops A PATH node that hands the launch back to Nub, or links back to it, is skipped. Any chain of Nub processes stops at 64 with ERR_NUB_LAUNCH_LOOP. #1044, #1063, #1073, fixes #832
Windows runtime cache Validates the cache directory rather than every ancestor. A broad grant on the user's profile made each command print warnings and run without the runtime. #1047, fixes #908

Install and upgrade

Area What changed Link
nub upgrade under mise Detects a mise install and runs mise upgrade --bump nub. It took the npm channel, which planted a second global copy. #571
Release assets Each stable release carries packslip.sigstore.json, a signed manifest of its archives, so mise can install Nub straight from the release. #982

Docs, testing and internals

  • Every docs page was restructured and tightened (#994).
  • Integration suites run against an offline Verdaccio registry with committed fixtures (#765).
  • CI's macOS jobs moved to macos-15 after GitHub retired macos-14 (4a599b6).
  • Only the approved release run can write a stable tag (#1017).

What's Changed

  • release: only the approved run can write a stable tag by @colinhacks in #1017
  • release: the approval summary says to run stager, not stager approve by @colinhacks in #1018
  • Resume stopped terminal jobs and isolate the Windows cache gate by @pullfrog[bot] in #998
  • pm shim: let registry and account verbs through in a mismatched project by @colinhacks in #1021
  • runtime: build libuv's threadpool at the first loop turn, so a program can size its own by @colinhacks in #1013
  • aube-linker: interpreter-preference test no longer symlinks /bin/echo by @colinhacks in #1038
  • node: resolve latest and other aliases to a release, never a prerelease by @colinhacks in #1033
  • tests: node shim check accepts the copy fallback across filesystems by @colinhacks in #1048
  • runtime: memoize vendored-package resolution in resolveSpec by @pullfrog[bot] in #796
  • pm use nub: say which turbo versions recognize nub by @colinhacks in #1034
  • pm: open store lock files readable so shared locks work on NFS by @colinhacks in #1046
  • fix(pm): support pnpm 12 shims by @afonsojramos in #983
  • resolver: match overrides against a catalog: dependency's reference, as pnpm does by @colinhacks in #1039
  • lockfile: rebase only link: versions from a pnpm importer by @Arthur031221 in #1004
  • pm: resolve workspace: specs in pack and publish manifests by @colinhacks in #1036
  • run: check the tsconfig of the entry's real directory, and skip a dependency's own by @colinhacks in #1041
  • pm: resolve DNS through the OS with an in-process cache by @meoyawn in #953
  • pm: apply git rename, header-only delete, empty-file and mode patches by @colinhacks in #1037
  • pm: eject the store only for the Remix 3.0.0 prereleases by @colinhacks in #1042
  • pm: key the macOS extracted-tree cache by package content by @colinhacks in #995
  • pm_shim tests: keep an installed Nub shim off the inherited PATH by @colinhacks in #1053
  • update: keep members' locked versions when updating at the workspace root by @colinhacks in #1043
  • pm: link every resolved optional peer of a package by @colinhacks in #1040
  • update: floor-bump catalog entries, and record catalog-only lockfile changes by @colinhacks in #1057
  • pm: run pnpm's project lifecycle stages on install, update and remove by @colinhacks in #1052
  • node discovery: skip a PATH shim that hands the launch back to nub by @colinhacks in #1044
  • runtime: describe the nub condition as an export condition, not a runtime key by @colinhacks in #990
  • codex: raise hook child descriptor limit by @jdalton in #985
  • test: isolate real-app matrix from release-age gate by @pullfrog[bot] in #978
  • release: sign a packslip for each stable release by @jdx in #982
  • pm: build git deps only when allowlisted, as pnpm 12; prepare without cp; prepare hosted-git archives by @colinhacks in #1045
  • runtime cache: validate only the Windows cache directory, not its ancestors by @colinhacks in #1047
  • ci: isolate Windows compile fixture caches by @pullfrog[bot] in #1023
  • fix(runtime): normalize Temporal namespace property attributes by @afonsojramos in #1015
  • runtime: let require() of a clobbered package load the installed package by @colinhacks in #1061
  • ci: run the Rust jobs when the published schema or skill.md changes by @pullfrog[bot] in #652
  • runtime: polyfill the JSON.parse options proposal (Stage 2.7) by @colinhacks in #992
  • test(pm): drop pre-#663 workarounds for the root-manifest cache flake by @colinhacks in #1069
  • bench(install): record tool versions in results; fix --cwd; re-measure bun 1.3.14 by @colinhacks in #549
  • compile: keep the bootstrap preload when the payload carries a resolve shim by @colinhacks in #1065
  • registry: refuse an uncached full packument fetch under --offline by @colinhacks in #1059
  • chore(make): add fix target for clippy autofix by @jdalton in #759
  • pm: read the workspace root .npmrc from a workspace member by @colinhacks in #1051
  • install: keep node_modules when an install fails after a virtual-store mode change by @colinhacks in #1035
  • ci: parallelize documentation scans by @jdalton in #1002
  • ci(node-matrix): retry setup-node once on the smoke legs by @pullfrog[bot] in #591
  • pm: pnpm 12's --filter selector grammar and directory globs for every workspace command by @colinhacks in #1056
  • pm: record the pinned pnpm in pnpm-lock.yaml's env document by @colinhacks in #1054
  • docs: restructure and tighten every docs page by @colinhacks in #994
  • ci: close trunk-red only when each reported workflow recovers; name failing legs in the Node matrix gate by @colinhacks in #588
  • test: give the code cache generator a file as stdin by @colinhacks in #1071
  • feat(runtime): backfill util debounce and throttle by @jdalton in #974
  • rebuild: find a member's packages in the root store under per-project lockfiles by @colinhacks in #1058
  • node discovery: never cache a version-manager shim's version by @colinhacks in #1049
  • runtime: stop loading internal/file at startup; node-compat: reap each test's session by @colinhacks in #556
  • perf(pm): cut eight-context index remapping time by 14.3% by @jdalton in #984
  • node discovery: skip a PATH node that links back to nub by @colinhacks in #1063
  • pm: a --filter that selects no project exits 0, as pnpm 12 does by @colinhacks in #1064
  • pm: patch-commit writes one consolidated patch; nub patch resolves through the lockfile by @btea in #1010
  • data loaders: keep NaN, -0, BigInt, dates and proto keys by @colinhacks in #1067
  • pm: a repeated filtered install is up to date; recursiveInstall is read from pnpm-workspace.yaml by @colinhacks in #1055
  • pm: write per-project lockfiles in their project's frame by @colinhacks in #1060
  • compile: report a compiled program's uncaught error where it was thrown by @colinhacks in #1066
  • test(registry): offline Verdaccio test registry + Socket safety checks by @jdalton in #765
  • env: stand down when Varlock already resolved the schema, or is the program being launched by @colinhacks in #724
  • fix(tsconfig): name the OS error when the tsconfig file cannot be read by @colinhacks in #899
  • Align dependency build-script policy with pnpm 12 by @colinhacks in #1050
  • node: spell --test and --watch bare so the children they start never inherit them by @colinhacks in #1075
  • node discovery: bound any chain of nub processes by @colinhacks in #1073
  • install: download a tarball again when its connection breaks off mid-body by @colinhacks in #1078
  • compile: keep build-machine paths out of region comments by @colinhacks in #1077
  • compile: bundle an unbundled package's dependencies from its real directory by @colinhacks in #1068
  • feat(upgrade): add mise install channel by @afonsojramos in #571
  • tooling: pin chrome-devtools-mcp and launch it on chrome-headless-shell by @colinhacks in #1085
  • pm: write pnpm-lock.yaml's local paths and empty sections as pnpm does by @colinhacks in #1079
  • pm: name each add-time refusal's own flag; keep a bin with the dependency under its own name by @colinhacks in #1074
  • site: bump next to 16.4.5 for Google Fonts /l/font URLs by @colinhacks in #1084
  • node: keep watch mode out of its children under every spelling and channel by @colinhacks in #1086
  • pm: read member lockfiles before the drift check under per-project lockfiles by @colinhacks in #1062
  • pm: flag an optional dep of a dev dep as dev and optional in package-lock.json by @colinhacks in #1088
  • pm: eject a registry package whose dependency is overridden to a local copy by @colinhacks in #1080
  • compile: ship a package as files when the bundle cannot reach its own install by @colinhacks in #1090

New Contributors

Full Changelog: v0.9.7...v0.10.0

Don't miss a new nub release

NewReleases is sending notifications on new releases.