@vzn/vx 0.0.225 → 0.0.227

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,6 +30,47 @@ Same graph, commands and concurrency: [how it is measured](https://vznjs.github.
30
30
 
31
31
  <!-- bench:end -->
32
32
 
33
+ ## Tried on real repos
34
+
35
+ Public monorepos, each run with its own `turbo.json` or Nx graph against
36
+ the tool it ships with, same machine and tasks per row, medians.
37
+
38
+ <!-- repos:start — generated by packages/vx-bench/update-site.ts from real-repos.json; do not hand-edit -->
39
+
40
+ | Repo | Against | Cold build | Restore outputs | Nothing to do |
41
+ | ------------------------------------------------------------------- | ------- | --------------------- | ------------------- | ---------------------- |
42
+ | [solidjs/solid](https://github.com/solidjs/solid) | Turbo | 40.6 s / 45.5 s | 66 ms / 127 ms | 51 ms / 95 ms |
43
+ | [withastro/astro](https://github.com/withastro/astro) | Turbo | 48.0 s / 57.9 s | 478 ms / 1.43 s | 249 ms / 1.38 s |
44
+ | [payloadcms/payload](https://github.com/payloadcms/payload) | Turbo | 126.9 s / 127.5 s | 3.44 s / 3.46 s | 256 ms / **237 ms** |
45
+ | [medusajs/medusa](https://github.com/medusajs/medusa) | Turbo | 308 s / 315 s | 3.86 s / 7.16 s | 947 ms / 3.29 s |
46
+ | [n8n-io/n8n](https://github.com/n8n-io/n8n) | Turbo | 137.2 s / **133.7 s** | 8.27 s / 12.5 s | 842 ms / 1.36 s |
47
+ | [calcom/cal.com](https://github.com/calcom/cal.com) | Turbo | 245.7 s / 250.7 s | 17.4 s / 19.9 s | 14.7 s / 18.5 s |
48
+ | [unocss/unocss](https://github.com/unocss/unocss) | Turbo | 45.1 s / 47.3 s | 859 ms / **185 ms** | 770 ms / **126 ms** |
49
+ | [vueuse/vueuse](https://github.com/vueuse/vueuse) | Turbo | 21.4 s / 29.2 s | 263 ms / **128 ms** | 246 ms / **51–161 ms** |
50
+ | [t3-oss/create-t3-turbo](https://github.com/t3-oss/create-t3-turbo) | Turbo | 27.2 s / 29.6 s | 199 ms / 340 ms | 174 ms / 342 ms |
51
+ | [TanStack/query](https://github.com/TanStack/query) | Nx | 34.6 s / 37.1 s | 910 ms / 1.62 s | 291 ms / 1.60 s |
52
+ | [strapi/strapi](https://github.com/strapi/strapi) | Nx | 238.2 s / 245.7 s | 1.61 s / 3.94 s | 341 ms / 4.02 s |
53
+ | [novuhq/novu](https://github.com/novuhq/novu) | Nx | 291.2 s / 299.4 s | 3.10 s / 9.05 s | 655 ms / 8.55 s |
54
+ | [TanStack/router](https://github.com/TanStack/router) | Nx | 123.0 s / 138.0 s | 1.27 s / 3.82 s | 626 ms / 3.91 s |
55
+ | [refinedev/refine](https://github.com/refinedev/refine) | Nx | 96.2 s / 105.9 s | 1.06 s / 1.19 s | 303 ms / 1.16 s |
56
+
57
+ vx first, theirs second; **bold** marks the other tool winning. These
58
+ rows ran vx without a lock (every config evaluated per run); the harness
59
+ now runs it from a `vx lock` snapshot (`--frozen`). How each
60
+ repo was run, versions, dates and why a tool wins where it does:
61
+ [vx on real Turbo and Nx repos](https://vznjs.github.io/vx/benchmarks/real-repos/).
62
+
63
+ <!-- repos:end -->
64
+
65
+ ## Why it is faster
66
+
67
+ - **Nothing boots per task.** No daemon, no Node process per task: vx
68
+ spawns your command directly.
69
+ - **Hashing without reading.** On a clean tree, cache keys come from
70
+ git's index; no file is read.
71
+ - **One pass over the repo.** One git walk per run; each stat and
72
+ lookup happens once.
73
+
33
74
  ## Try it on your repo
34
75
 
35
76
  In a Turborepo or Nx workspace:
@@ -56,15 +97,6 @@ pending); [`examples/turbo`](examples/turbo) runs this setup, and
56
97
  and an Nx repo, on every commit. Which `turbo.json`, `nx.json` and `project.json` keys vx honours,
57
98
  key by key: [Turbo and Nx config support](https://vznjs.github.io/vx/compare/turbo-nx-support/).
58
99
 
59
- ## Why it is faster
60
-
61
- - **Nothing boots per task.** No daemon, no Node process per task: vx
62
- spawns your command directly.
63
- - **Hashing without reading.** On a clean tree, cache keys come from
64
- git's index; no file is read.
65
- - **One pass over the repo.** One git walk per run; each stat and
66
- lookup happens once.
67
-
68
100
  ## Install
69
101
 
70
102
  ```sh
@@ -141,38 +173,6 @@ keep your Turbo or Nx remote cache. [Parity map](packages/vx/docs/parity.md) ·
141
173
  Vite does it. Core ships none and needs none.
142
174
  - **Free.** MIT. No paywall, no cloud, no account.
143
175
 
144
- ## Tried on real repos
145
-
146
- Public monorepos, each run with its own `turbo.json` or Nx graph against
147
- the tool it ships with, same machine and tasks per row, medians.
148
-
149
- <!-- repos:start — generated by packages/vx-bench/update-site.ts from real-repos.json; do not hand-edit -->
150
-
151
- | Repo | Against | Cold build | Restore outputs | Nothing to do |
152
- | ------------------------------------------------------------------- | ------- | --------------------- | ------------------- | ---------------------- |
153
- | [solidjs/solid](https://github.com/solidjs/solid) | Turbo | 40.6 s / 45.5 s | 66 ms / 127 ms | 51 ms / 95 ms |
154
- | [withastro/astro](https://github.com/withastro/astro) | Turbo | 48.0 s / 57.9 s | 478 ms / 1.43 s | 249 ms / 1.38 s |
155
- | [payloadcms/payload](https://github.com/payloadcms/payload) | Turbo | 126.9 s / 127.5 s | 3.44 s / 3.46 s | 256 ms / **237 ms** |
156
- | [medusajs/medusa](https://github.com/medusajs/medusa) | Turbo | 308 s / 315 s | 3.86 s / 7.16 s | 947 ms / 3.29 s |
157
- | [n8n-io/n8n](https://github.com/n8n-io/n8n) | Turbo | 137.2 s / **133.7 s** | 8.27 s / 12.5 s | 842 ms / 1.36 s |
158
- | [calcom/cal.com](https://github.com/calcom/cal.com) | Turbo | 245.7 s / 250.7 s | 17.4 s / 19.9 s | 14.7 s / 18.5 s |
159
- | [unocss/unocss](https://github.com/unocss/unocss) | Turbo | 45.1 s / 47.3 s | 859 ms / **185 ms** | 770 ms / **126 ms** |
160
- | [vueuse/vueuse](https://github.com/vueuse/vueuse) | Turbo | 21.4 s / 29.2 s | 263 ms / **128 ms** | 246 ms / **51–161 ms** |
161
- | [t3-oss/create-t3-turbo](https://github.com/t3-oss/create-t3-turbo) | Turbo | 27.2 s / 29.6 s | 199 ms / 340 ms | 174 ms / 342 ms |
162
- | [TanStack/query](https://github.com/TanStack/query) | Nx | 34.6 s / 37.1 s | 910 ms / 1.62 s | 291 ms / 1.60 s |
163
- | [strapi/strapi](https://github.com/strapi/strapi) | Nx | 238.2 s / 245.7 s | 1.61 s / 3.94 s | 341 ms / 4.02 s |
164
- | [novuhq/novu](https://github.com/novuhq/novu) | Nx | 291.2 s / 299.4 s | 3.10 s / 9.05 s | 655 ms / 8.55 s |
165
- | [TanStack/router](https://github.com/TanStack/router) | Nx | 123.0 s / 138.0 s | 1.27 s / 3.82 s | 626 ms / 3.91 s |
166
- | [refinedev/refine](https://github.com/refinedev/refine) | Nx | 96.2 s / 105.9 s | 1.06 s / 1.19 s | 303 ms / 1.16 s |
167
-
168
- vx first, theirs second; **bold** marks the other tool winning. These
169
- rows ran vx without a lock (every config evaluated per run); the harness
170
- now runs it from a `vx lock` snapshot (`--frozen`). How each
171
- repo was run, versions, dates and why a tool wins where it does:
172
- [vx on real Turbo and Nx repos](https://vznjs.github.io/vx/benchmarks/real-repos/).
173
-
174
- <!-- repos:end -->
175
-
176
176
  ## How it compares
177
177
 
178
178
  | | vx | Turborepo | Nx |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vzn/vx",
3
- "version": "0.0.225",
3
+ "version": "0.0.227",
4
4
  "description": "A fast, correct task runner and build cache for JavaScript monorepos.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -17,10 +17,10 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "optionalDependencies": {
20
- "@vzn/vx-linux-x64": "0.0.225",
21
- "@vzn/vx-linux-arm64": "0.0.225",
22
- "@vzn/vx-darwin-x64": "0.0.225",
23
- "@vzn/vx-darwin-arm64": "0.0.225"
20
+ "@vzn/vx-linux-x64": "0.0.227",
21
+ "@vzn/vx-linux-arm64": "0.0.227",
22
+ "@vzn/vx-darwin-x64": "0.0.227",
23
+ "@vzn/vx-darwin-arm64": "0.0.227"
24
24
  },
25
25
  "dependencies": {
26
26
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
@@ -17,8 +17,8 @@
17
17
  // be a git work tree; non-git environments are not supported.
18
18
 
19
19
  import path from 'node:path'
20
- import { lstatSync, readdirSync } from 'node:fs'
21
- import { realpath, rm, rmdir } from 'node:fs/promises'
20
+ import { lstatSync, readdirSync, realpathSync } from 'node:fs'
21
+ import { rm, rmdir } from 'node:fs/promises'
22
22
  import type { CacheInputs } from '../config.js'
23
23
  import {
24
24
  asTrees,
@@ -533,7 +533,7 @@ export async function resolveOutputs(args: {
533
533
  // outside the project, so any future caller reaching it by another route (a
534
534
  // programmatic embedder, a config source that skips the loader) is contained
535
535
  // by construction.
536
- return (await containedIn(args.projectDir, scanned)).sort()
536
+ return containedIn(args.projectDir, scanned).sort()
537
537
  }
538
538
 
539
539
  /**
@@ -558,7 +558,7 @@ export async function resolveOutputs(args: {
558
558
  * task that produced it) is REFUSED. When the caller deletes, unresolvable
559
559
  * means leave it alone.
560
560
  */
561
- async function containedIn(root: string, paths: readonly string[]): Promise<string[]> {
561
+ function containedIn(root: string, paths: readonly string[]): string[] {
562
562
  // One `path.dirname` per path, kept alongside — computing it again in the
563
563
  // final filter measured as the DOMINANT added cost on a wide output tree
564
564
  // (string work, not syscalls: 10k files in 20 dirs cost more than 5k files
@@ -571,9 +571,19 @@ async function containedIn(root: string, paths: readonly string[]): Promise<stri
571
571
  lexDirs.push(path.dirname(p))
572
572
  }
573
573
  if (lexical.length === 0) return []
574
- const realRoot = await realpath(root).catch(() => root)
574
+ // Sync, as `hashFile`'s lstat: a realpath is microseconds, and the
575
+ // promise round trip per call was most of this function's cost on every
576
+ // miss (B, 2026-09-30).
577
+ const real = (p: string): string | null => {
578
+ try {
579
+ return realpathSync(p)
580
+ } catch {
581
+ return null
582
+ }
583
+ }
584
+ const realRoot = real(root) ?? root
575
585
  const uniqueDirs = [...new Set(lexDirs)]
576
- const resolved = await Promise.all(uniqueDirs.map((d) => realpath(d).catch(() => null)))
586
+ const resolved = uniqueDirs.map(real)
577
587
  const contained = new Set<string>()
578
588
  for (const [i, dir] of uniqueDirs.entries()) {
579
589
  const real = resolved[i]
@@ -795,7 +805,7 @@ export async function resolveWorkspaceOutputs(args: {
795
805
  // deliberately ignore PROJECT boundaries — that is the escape hatch — but
796
806
  // escaping the WORKSPACE was never part of it, and `cleanWorkspaceOutputs`
797
807
  // deletes what this returns.
798
- return (await containedIn(args.workspaceRoot, scanned)).sort()
808
+ return containedIn(args.workspaceRoot, scanned).sort()
799
809
  }
800
810
 
801
811
  /**