Skip to content

vfs: add minimal node:vfs subsystem#63115

Open
mcollina wants to merge 14 commits intonodejs:mainfrom
mcollina:vfs-minimal
Open

vfs: add minimal node:vfs subsystem#63115
mcollina wants to merge 14 commits intonodejs:mainfrom
mcollina:vfs-minimal

Conversation

@mcollina
Copy link
Copy Markdown
Member

@mcollina mcollina commented May 4, 2026

Adds an experimental node:vfs builtin (gated behind --experimental-vfs) with VirtualFileSystem, VirtualProvider, MemoryProvider, and RealFSProvider. No integration with node:fs, the module loader, or SEA those are intended to land in follow-up PRs.

Extracted from: #61478

Approximate line counts: code ~4k / docs ~1k / tests ~5k — total ~10k lines, with tests being the largest share.

@nodejs-github-bot
Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/config
  • @nodejs/loaders
  • @nodejs/startup

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels May 4, 2026
@bakkot
Copy link
Copy Markdown
Contributor

bakkot commented May 4, 2026

No integration with node:fs

The docs in this PR claim that you can call myVfs.mount('/virtual') and subsequently regular node:fs operations which read from paths beginning with /virtual will read from the VFS. That sounds like integration to me. Are the docs wrong, or am I misunderstanding what "no integration" means?

mcollina added 13 commits May 5, 2026 10:33
Adds the node:vfs builtin module with VirtualFileSystem and
provider classes. No integration with fs, modules, or SEA.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Adapts tests that exercised behavior through fs integration so
they call the VFS API directly instead.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Cover VirtualDir iteration and disposal, MemoryFileHandle read/write
methods via the provider, and the VirtualProvider base class
(capability flags, readonly stubs, default implementations).

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Covers MemoryProvider, copyFile mode, rm edge cases, hardlinks,
bigint read positions, and parent timestamps via the VFS API.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Cover the callback-style async API, additional read/write stream
flows, the promises.watch async iterable, and async methods of
RealFSProvider.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Removes the unused createEXDEV error helper, adds direct tests for
MemoryProvider numeric flags / symlink loops / utimes variants, and
adds a base-class VirtualFileHandle test.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Adds targeted tests covering the lazy population, dynamic content
provider, readonly-mode, and symlink-traversal paths in MemoryProvider;
the path-escape and RealFileHandle EBADF paths in RealFSProvider; the
abort/buffer-encoding/recursive watch paths in VFSWatcher; and the
empty-file / EBADF fd / explicit-fd-with-start paths in the streams.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Adds direct unit tests for stats default-option paths (including the
process.getuid?.() fallback), file-handle base-class branches, the
empty-options provider write/append paths, the access-mode permission
denials, the watcher closed-state and async-iterable resolver-drain
branches, and various RealFSProvider escape and EBADF paths.

Brings overall branch coverage from 89% to 95.7%, and stats.js to
100% branch coverage.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Replaces the -coverage / -branches / -misc suffixes with focused
files named after the API or behaviour they exercise. Splits the
larger multi-topic files into one-topic-per-file.

Renames:
- callbacks.js → callback-api.js
- stats-defaults.js → stats-helpers.js
- file-handle-base.js → virtual-file-handle.js
- provider-base.js → virtual-provider.js
- provider-memory.js → memory-provider.js
- real-provider-async.js → real-provider-promises.js
- mkdir-recursive-return.js → mkdir.js

New files (split out of -coverage/-branches/-misc):
- access-modes, create, link, mkdtemp, rename, symlinks, utimes,
  write-options
- memory-file-handle, memory-provider-dynamic, memory-provider-flags
- real-provider-handle, real-provider-symlinks, real-provider-watch
- stream-errors, stream-explicit-fd
- watch, watch-abort-signal, watch-encoding, watch-promises,
  watch-recursive

Coverage maintained at 97.6% line / 95.2% branch / 95.3% function.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Adds an --experimental-vfs runtime option that gates loading of the
node:vfs builtin module, matching the pattern used by node:quic and
node:stream/iter. Without the flag, require('node:vfs') / import
'node:vfs' throw ERR_UNKNOWN_BUILTIN_MODULE.

All VFS test files are updated to pass --experimental-vfs.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Fixes the JS lint warnings on the VFS subsystem and tests:
primordial alphabetical ordering, em-dash → hyphen, error-codes
multiline destructuring, removal of unused JSDoc @returns, and the
test-side mustSucceed / async-iife-no-unused-result rules.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
internal/vfs/fd.js doesn't require any internal/vfs/* modules so
there's no circular dependency to defer. Replace the getLazy wrapper
with a direct import.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Removes mount/unmount, virtualCwd, overlay mode, fs/module
integration sections, SEA usage, and worker-thread guidance
since none of that ships in this PR.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollina mcollina marked this pull request as ready for review May 5, 2026 10:04
@mcollina
Copy link
Copy Markdown
Member Author

mcollina commented May 5, 2026

No integration with node:fs

The docs in this PR claim that you can call myVfs.mount('/virtual') and subsequently regular node:fs operations which read from paths beginning with /virtual will read from the VFS. That sounds like integration to me. Are the docs wrong, or am I misunderstanding what "no integration" means?

Fixed, good spot.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 5, 2026

Codecov Report

❌ Patch coverage is 96.73123% with 189 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.85%. Comparing base (9390c81) to head (779fc37).
⚠️ Report is 20 commits behind head on main.

Files with missing lines Patch % Lines
lib/internal/vfs/providers/memory.js 93.55% 62 Missing and 4 partials ⚠️
lib/internal/vfs/file_system.js 96.68% 38 Missing ⚠️
lib/internal/vfs/watcher.js 95.63% 30 Missing ⚠️
lib/internal/vfs/providers/real.js 96.54% 17 Missing ⚠️
lib/internal/vfs/streams.js 95.46% 16 Missing ⚠️
lib/internal/vfs/provider.js 98.38% 10 Missing ⚠️
lib/internal/vfs/file_handle.js 98.88% 7 Missing and 1 partial ⚠️
lib/internal/vfs/dir.js 98.16% 2 Missing ⚠️
lib/internal/vfs/fd.js 97.70% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #63115      +/-   ##
==========================================
+ Coverage   89.65%   89.85%   +0.19%     
==========================================
  Files         712      724      +12     
  Lines      220829   227037    +6208     
  Branches    42375    43361     +986     
==========================================
+ Hits       197986   203999    +6013     
- Misses      14655    14870     +215     
+ Partials     8188     8168      -20     
Files with missing lines Coverage Δ
lib/internal/bootstrap/realm.js 96.22% <100.00%> (+<0.01%) ⬆️
lib/internal/process/pre_execution.js 98.28% <100.00%> (-0.11%) ⬇️
lib/internal/vfs/errors.js 100.00% <100.00%> (ø)
lib/internal/vfs/stats.js 100.00% <100.00%> (ø)
lib/vfs.js 100.00% <100.00%> (ø)
src/node_options.cc 76.63% <100.00%> (+0.02%) ⬆️
src/node_options.h 98.00% <100.00%> (+0.01%) ⬆️
lib/internal/vfs/dir.js 98.16% <98.16%> (ø)
lib/internal/vfs/fd.js 97.70% <97.70%> (ø)
lib/internal/vfs/file_handle.js 98.88% <98.88%> (ø)
... and 6 more

... and 57 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Adds 'vfs' to the C++ cannot_be_required list so existing tests
(test-code-cache, test-process-get-builtin, test-require-resolve)
treat it like other flagged experimental modules. Adds the flag to
doc/node.1 and reorders the entry in doc/api/cli.md.

Assisted-by: Claude-Opus4.7
Signed-off-by: Matteo Collina <hello@matteocollina.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants