Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5bcb880
perf(glob): share compatible walks and collect matches per worker
CoderSerio Sep 30, 2026
0d6f7d8
perf(writeFile): remove redundant Buffer copies from writes
CoderSerio Sep 30, 2026
a4d63bc
ci: validate pull requests against all base branches
CoderSerio Sep 30, 2026
b7d7dda
docs: record controlled native overhead performance evidence
CoderSerio Sep 30, 2026
b5c83eb
perf(writeFile): release owned snapshots after worker I/O
CoderSerio Sep 30, 2026
4cda605
docs: align glob table with final measured samples
CoderSerio Sep 30, 2026
b680d15
docs: include measured Buffer cases on write API pages
CoderSerio Sep 30, 2026
d0ef0d5
Merge branch 'feat/node-api-alignment' into perf/native-batch-overheads
CoderSerio Sep 30, 2026
ad30ee8
test(performance): adapt upstream competitor benchmark workloads
CoderSerio Sep 30, 2026
d37c1ea
docs: publish competitor comparisons and concurrency evidence
CoderSerio Sep 30, 2026
ffbf2cb
test(performance): compare every public filesystem API with peers
CoderSerio Oct 1, 2026
111dec5
docs: publish complete per-API competitor performance tables
CoderSerio Oct 1, 2026
c66fe07
test(performance): canonicalize Windows realpath fixture inputs
CoderSerio Oct 1, 2026
fa78dda
fix(glob): preserve Node traversal and exclusion boundaries
CoderSerio Oct 1, 2026
a03cb96
test(performance): make benchmark matrices reproducible and traceable
CoderSerio Oct 1, 2026
bac6fb3
docs: integrate performance evidence into the official benchmark hub
CoderSerio Oct 1, 2026
526bde7
fix(scan): align Windows directory sizes with Node metadata
CoderSerio Oct 1, 2026
7d8f66e
docs: regenerate validated competitor comparison tables
CoderSerio Oct 1, 2026
8ff9896
fix(docs): accept Windows line endings in benchmark templates
CoderSerio Oct 1, 2026
786bd8e
docs: publish reviewed release benchmark evidence
CoderSerio Oct 1, 2026
5db7ff1
Merge pull request #31 from vooyajs/perf/all-api-comparisons
CoderSerio Oct 1, 2026
de00dd6
Merge pull request #30 from vooyajs/perf/competitor-benchmarks
CoderSerio Oct 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ env:
- '**/*.gitignore'
- .editorconfig
pull_request:
branches:
- main
paths-ignore:
- LICENSE
- '**/*.gitignore'
Expand Down Expand Up @@ -180,6 +178,8 @@ jobs:
run: pnpm test:bindings
- name: Test Node fs conformance
run: pnpm test:conformance
- name: Test competitor benchmark contracts
run: pnpm test:competitor-contract
test-linux-binding:
name: Test ${{ matrix.target }} - node@${{ matrix.node }}
needs:
Expand Down Expand Up @@ -261,3 +261,13 @@ jobs:
--platform "${{ steps.docker.outputs.PLATFORM }}" \
"${{ steps.docker.outputs.IMAGE }}" \
sh -lc "corepack enable && pnpm test:conformance"
- name: Test competitor benchmark contracts
run: |
docker run --rm \
-e CI=true \
-e GITHUB_ACTIONS=true \
-v "${{ github.workspace }}:${{ github.workspace }}" \
-w "${{ github.workspace }}" \
--platform "${{ steps.docker.outputs.PLATFORM }}" \
"${{ steps.docker.outputs.IMAGE }}" \
sh -lc "corepack enable && pnpm test:competitor-contract"
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ All notable Vooya FS changes are documented here. The format follows

### Added

- Official benchmark hub and sequential Node 22/24 reproduction matrix, pinned
fixture setup, report overwrite protection and reviewed table generation.

- Full public API competitor comparisons with per-API Node 22/24 tables, raw
samples, validated mutation effects, explicit Node fallback routes and
specialized copy/remove/mkdir/scan baselines.

- Reproducible competitor benchmark suite adapted from pinned fdir/tinyglobby
workloads, with license notices, independent result validation, Node 22/24 raw
evidence and a recursive readdir concurrency diagnostic.

- Public compatibility entry point with file URL/Buffer path handling for core batches,
option-dependent Node execution paths, constants, result-aware TypeScript declarations,
and an actual Promise-based glob batch that also supports async iteration.
Expand All @@ -16,6 +27,13 @@ All notable Vooya FS changes are documented here. The format follows

### Fixed

- `scan`: Windows directory sizes now match Node's zero-size metadata convention,
including followed directory links; file and unfollowed link sizes are unchanged.
- Public glob compatibility for consecutive globstars, wildcard exclusion
boundaries and Node's finite directory-symlink expansion, with regression tests.
- Benchmark provenance now identifies the actual loaded native binary, including
override paths; Windows realpath fixtures use consistent long-path inputs.

- `readdir`: filename encodings and Buffer names, empty/file roots, traversal failures,
recursive symlinks, and the sync/Promise distinction for Dirent traversal.
- `glob`: wildcard prefixes, arrays/deduplication, cwd-rooted exclusions, parent paths,
Expand All @@ -29,6 +47,17 @@ All notable Vooya FS changes are documented here. The format follows
- `writeFile` / `appendFile`: encoding-string options, ASCII/Latin-1 UTF-16 truncation,
Base64 alphabets/padding and case-insensitive encoding names.

### Performance

- Compatible native `glob` patterns share a traversal; workers collect results
locally and merge once, with cross-walk deduplication only when needed.
- `writeFile` / `appendFile` borrow synchronous Buffer bytes and write async
worker-owned snapshots directly, removing redundant payload copies while
preserving entry-time snapshots and Buffer slice boundaries.
- Controlled public-entry Node 22/24 before/after evidence and reproduction
instructions are included in the native overhead report, including small-input
costs and the limits of local filesystem and RSS measurements.

### Compatibility policy

- The measured Rust fast paths remain focused on bulk work. Advanced options use
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<p align="center">
<a href="./README.zh-CN.md">中文</a> ·
<a href="https://github.com/vooyajs/fs">Repository</a> ·
<a href="https://rush-fs-docs.vercel.app/benchmarks">Benchmarks & Docs</a> ·
<a href="https://vooyajs.com/">Vooya</a> ·
<a href="https://vooyajs.github.io/vooya-lab/">Vooya Lab</a>
</p>
Expand Down Expand Up @@ -155,7 +156,16 @@ corepack pnpm test
corepack pnpm doc:build
```

Run evidence-oriented benchmarks with:
The [official benchmark hub](https://rush-fs-docs.vercel.app/benchmarks) links all
26 per-API comparison tables, upstream workloads and raw evidence. Reproduce both
Node versions sequentially with installed runtime paths:

```bash
pnpm perf:matrix --node22 /path/to/node22 --node24 /path/to/node24 \
--output .perf/reproduction
```

Run focused evidence-oriented benchmarks with:

```bash
corepack pnpm perf:fs scan --iterations 10 --warmup 2 --json .perf/scan.json
Expand Down
11 changes: 10 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<p align="center">
<a href="./README.md">English</a> ·
<a href="https://github.com/vooyajs/fs">代码仓库</a> ·
<a href="https://rush-fs-docs.vercel.app/benchmarks">官方文档与性能对比</a> ·
<a href="https://vooyajs.com/">Vooya</a> ·
<a href="https://vooyajs.github.io/vooya-lab/">Vooya Lab</a>
</p>
Expand Down Expand Up @@ -142,7 +143,15 @@ corepack pnpm test
corepack pnpm doc:build
```

运行带证据输出的性能基准:
[官方性能入口](https://rush-fs-docs.vercel.app/benchmarks) 包含全部 26 组 API
对比表、同行测试集和原始证据。指定已安装的 Node 路径即可顺序复跑两版运行时:

```bash
pnpm perf:matrix --node22 /path/to/node22 --node24 /path/to/node24 \
--output .perf/reproduction
```

运行单项性能基准:

```bash
corepack pnpm perf:fs scan --iterations 10 --warmup 2 --json .perf/scan.json
Expand Down
39 changes: 35 additions & 4 deletions api.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,33 @@ function globRoute(pattern, options) {
(opts.exclude === undefined ||
(Array.isArray(opts.exclude) &&
opts.exclude.every(
(pattern) => typeof pattern === 'string' && !/[?[\]!()+{}]|\\/.test(pattern) && !pattern.endsWith('/'),
(pattern) =>
typeof pattern === 'string' &&
!/[?[\]!()+{}]|\\/.test(pattern) &&
!pattern.endsWith('/') &&
(opts.gitIgnore ||
(!pattern.includes('*') &&
!pattern.includes(':') &&
pattern.split('/').every((part) => part !== '' && part !== '.' && part !== '..'))),
))) &&
(opts.cwd === undefined || cwd !== undefined)
if (!supported && opts.gitIgnore) {
const error = new TypeError('gitIgnore requires native-supported glob patterns and string-array excludes')
error.code = 'ERR_INVALID_ARG_VALUE'
throw error
}
return supported ? [pattern, { ...opts, cwd }] : undefined
// Adjacent globstars are one globstar in Node's matcher, including root inclusion.
const normalized = supported
? patterns.map((value) =>
value
.split('/')
.filter((part, i, parts) => part !== '**' || parts[i - 1] !== '**')
.join('/'),
)
: undefined
return supported
? [typeof pattern === 'string' ? normalized[0] : normalized, { ...opts, cwd }, !opts.gitIgnore]
: undefined
}

// Explicit assignments preserve CommonJS named exports for ESM consumers.
Expand Down Expand Up @@ -274,13 +292,26 @@ module.exports.readFile = async function readFile(path, options) {
}
module.exports.globSync = function globSync(pattern, options) {
const args = globRoute(pattern, options)
return args ? syncCall('globSync', args) : fs.globSync(pattern, options)
if (args) {
try {
return syncCall('globSync', args)
} catch (error) {
if (error?.code !== 'ERR_VOOYA_GLOB_NODE_FALLBACK') throw error
}
}
return fs.globSync(pattern, options)
}
module.exports.glob = function glob(pattern, options) {
// Preserve the actual Promise batch API while accepting Node's for-await usage.
const batch = (async () => {
const args = globRoute(pattern, options)
if (args) return asyncCall('glob', args)
if (args) {
try {
return await asyncCall('glob', args)
} catch (error) {
if (error?.code !== 'ERR_VOOYA_GLOB_NODE_FALLBACK') throw error
}
}
const entries = []
for await (const entry of promises.glob(pattern, options)) entries.push(entry)
return entries
Expand Down
4 changes: 2 additions & 2 deletions docs/app/layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default async function RootLayout({ children }) {
<Navbar
logo={<span className="font-bold">Vooya FS</span>}
projectLink="https://github.com/vooyajs/fs"
docsRepositoryBase="https://github.com/vooyajs/fs/tree/feat/vooya-fs-next/docs"
docsRepositoryBase="https://github.com/vooyajs/fs/tree/main/docs"
/>
)
const pageMap = await getPageMap()
Expand All @@ -27,7 +27,7 @@ export default async function RootLayout({ children }) {
footer={<Footer>MIT {new Date().getFullYear()} © Vooya FS.</Footer>}
pageMap={pageMap}
editLink="Edit this page on GitHub"
docsRepositoryBase="https://github.com/vooyajs/fs/blob/feat/vooya-fs-next/docs"
docsRepositoryBase="https://github.com/vooyajs/fs/blob/main/docs"
sidebar={{ defaultMenuCollapseLevel: 1 }}
>
{children}
Expand Down
6 changes: 6 additions & 0 deletions docs/content/api/access.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,9 @@ Vooya FS is faster for typical access checks; for F_OK-only sync checks Node.js
- **Constants**: Use `constants.F_OK`, `R_OK`, `W_OK`, `X_OK` from `node:fs` (or the numeric values).
- **Known gap**: Missing-path and permission errors currently have Node-like messages, but do not expose Node-style `code`, `path`, and `syscall` fields yet.
- **Unsupported Node path inputs**: `Buffer` paths and `URL` paths are not part of the current Vooya FS `access` surface.

## Competitor measurements

See the [full access comparison table](/guide/all-api-evidence#access) for
Node 22/24, peer libraries, sync/Promise modes, small and batch workloads,
execution routes and measurement limits.
47 changes: 40 additions & 7 deletions docs/content/api/append-file.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,49 @@ and stop at padding.

## Performance

From repo benchmarks (`pnpm build && pnpm bench`):

| Scenario | Node.js | Vooya FS | Ratio |
| ---------- | ------- | -------- | ----- |
| appendFile | 30 µs | 27 µs | 0.9x |

On par with Node.js; single-file append is an atomic syscall.
Buffer writes borrow the input bytes for synchronous calls. Promise calls keep
one entry-time copy so the worker owns a stable snapshot, then write directly
from that snapshot. This removes a redundant payload-sized allocation and copy;
it is not a zero-copy or faster-disk guarantee. String encoding is unchanged.

See the [native overhead report](/guide/native-overheads) for release-build
Node 22/24 comparisons, including small inputs, raw samples and memory limits.
Large Buffer workloads are the intended target; small writes remain dominated
by scheduling, opening the file and filesystem cost. These APIs do not implement
Node's `flush` option, so the measurements compare writes without an explicit flush.

Local macOS arm64 medians, two warmups and ten samples; times include the
public entry. Inputs are Buffer subarrays, concurrency 1, warm cache, no flush.

| Node | Input / method | Node ms | Before ms | After ms |
| -------- | -------------- | ------: | --------: | -------: |
| v22.22.0 | 64 B async | 0.118 | 0.052 | 0.064 |
| v22.22.0 | 8 MiB async | 1.027 | 0.948 | 0.829 |
| v22.22.0 | 8 MiB sync | 0.780 | 0.909 | 0.767 |
| v24.21.0 | 64 B async | 0.130 | 0.056 | 0.058 |
| v24.21.0 | 8 MiB async | 1.036 | 1.002 | 0.902 |
| v24.21.0 | 8 MiB sync | 0.695 | 0.787 | 0.671 |

Small-input results include regressions; reduced copying does not imply a speedup
for every payload or filesystem. RSS samples in the report are not peak memory.

## Notes

- **Encodings**: utf8, ascii, latin1, base64, base64url, hex. Same semantics as Node.js.
- **Known gap**: Filesystem errors currently have Node-like messages, but do not expose Node-style `code`, `path`, and `syscall` fields yet.
- **Unsupported Node inputs/options**: `Buffer` paths, `URL` paths, TypedArray/DataView data, `AbortSignal`, and newer `flush` behavior are not part of the current Vooya FS `appendFile` surface.

### Buffer ownership

`Buffer.subarray()` writes only the selected bytes, including a nonzero offset.
Async calls snapshot those bytes before returning the Promise; later mutation of
the original Buffer does not change that operation's input. This is Vooya's
ownership policy, not a promise that Node snapshots mutable inputs identically.
Await writes to the same file in the required order; separate concurrent calls
are not an ordering primitive.

## Competitor measurements

See the [full appendFile comparison table](/guide/all-api-evidence#appendfile) for
Node 22/24, peer libraries, sync/Promise modes, small and batch workloads,
execution routes and measurement limits.
6 changes: 6 additions & 0 deletions docs/content/api/chmod.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,9 @@ Single syscall; on par with Node.js. See [Benchmarks](/benchmarks).
- **Current Windows gap**: Vooya FS currently validates path existence but does not fully mirror Node's readonly-related chmod behavior on Windows.
- **Known gap**: Missing-path errors currently have Node-like messages, but do not expose Node-style `code`, `path`, and `syscall` fields yet.
- **Unsupported Node inputs**: String mode values, `Buffer` paths, and `URL` paths are not part of the current Vooya FS `chmod` surface.

## Competitor measurements

See the [full chmod comparison table](/guide/all-api-evidence#chmod) for
Node 22/24, peer libraries, sync/Promise modes, small and batch workloads,
execution routes and measurement limits.
6 changes: 6 additions & 0 deletions docs/content/api/chown.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,9 @@ Single syscall; on par with Node.js. See [Benchmarks](/benchmarks).
- **Current non-Unix gap**: Vooya FS currently validates path existence but does not fully mirror platform ownership mutation semantics outside Unix.
- **Known gap**: Missing-path and permission errors currently have Node-like messages, but do not expose Node-style `code`, `path`, and `syscall` fields yet.
- **Unsupported Node path inputs**: `Buffer` paths and `URL` paths are not part of the current Vooya FS `chown` surface.

## Competitor measurements

See the [full chown comparison table](/guide/all-api-evidence#chown) for
Node 22/24, peer libraries, sync/Promise modes, small and batch workloads,
execution routes and measurement limits.
Loading
Loading