@vzn/vx 0.0.575 → 0.0.576

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vzn/vx",
3
- "version": "0.0.575",
3
+ "version": "0.0.576",
4
4
  "description": "The fastest task runner and build cache for JavaScript monorepos.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -21,10 +21,10 @@
21
21
  "node": ">=18"
22
22
  },
23
23
  "optionalDependencies": {
24
- "@vzn/vx-linux-x64": "0.0.575",
25
- "@vzn/vx-linux-arm64": "0.0.575",
26
- "@vzn/vx-darwin-x64": "0.0.575",
27
- "@vzn/vx-darwin-arm64": "0.0.575"
24
+ "@vzn/vx-linux-x64": "0.0.576",
25
+ "@vzn/vx-linux-arm64": "0.0.576",
26
+ "@vzn/vx-darwin-x64": "0.0.576",
27
+ "@vzn/vx-darwin-arm64": "0.0.576"
28
28
  },
29
29
  "dependencies": {
30
30
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
package/src/cli/cache.ts CHANGED
@@ -172,7 +172,11 @@ async function pruneCmd(args: readonly string[]): Promise<number> {
172
172
  let dir: string
173
173
  let storeRoot: string | null = null
174
174
  if (parsed.cacheDir !== undefined) dir = await cliCacheDir(root, parsed.cacheDir)
175
- else ({ cacheDir: dir, storeRoot } = await loadCliWorkspace(root))
175
+ else {
176
+ const ws = await loadCliWorkspace(root)
177
+ dir = ws.cacheDir
178
+ storeRoot = await ws.storeRoot
179
+ }
176
180
  // A workspace that never ran has nothing to prune, and opening the cache
177
181
  // to find that out made it: `.vx/cache` with a database and a
178
182
  // `.gitignore`, where item 900 had held the dry run to making nothing
@@ -27,8 +27,8 @@ export interface CliWorkspace {
27
27
  workspaceConfig: WorkspaceConfig | null
28
28
  plugins: readonly VxPlugin[]
29
29
  cacheDir: string
30
- /** The shared store a run opens `cacheDir` with (`resolveStoreRoot`). */
31
- storeRoot: string | null
30
+ /** The shared store a run opens `cacheDir` with (`resolveStoreRoot`), asked on load. */
31
+ storeRoot: Promise<string | null>
32
32
  }
33
33
 
34
34
  export const warnToStderr = (message: string): void => {
@@ -152,7 +152,7 @@ export async function loadCliProjects(
152
152
  workspaceRoot,
153
153
  undefined,
154
154
  'open',
155
- opts.cacheDir === undefined ? ws.storeRoot : null,
155
+ opts.cacheDir === undefined ? await ws.storeRoot : null,
156
156
  )
157
157
  try {
158
158
  const loaded = await loadProjects({
@@ -155,7 +155,8 @@ async function collectWorkspaceInfo(
155
155
  ? resolveCacheDir(root, workspaceConfig)
156
156
  : path.resolve(cwd, opts.cacheDir)
157
157
  const metas = await discoverProjects(await loadWorkspace(root, reads), plugins, cacheDir, warn)
158
- const storeRoot = opts.cacheDir === undefined ? resolveStoreRoot(root, workspaceConfig) : null
158
+ const storeRoot =
159
+ opts.cacheDir === undefined ? await resolveStoreRoot(root, workspaceConfig) : null
159
160
  const cache = new Cache(cacheDir, undefined, undefined, undefined, 'inspect', storeRoot)
160
161
  let stats
161
162
  let orphans
@@ -224,6 +224,8 @@ export async function prepareRun(options: RunOptions, log: Logger): Promise<Prep
224
224
  const cacheDir = options.cacheDir
225
225
  ? path.resolve(options.cwd, options.cacheDir)
226
226
  : resolveCacheDir(workspaceRoot, workspaceConfig)
227
+ // Asked now, read when the cache opens: a repository with no remote is
228
+ // named by its root commit, a git spawn the discovery below overlaps.
227
229
  const storeRoot = options.cacheDir ? null : resolveStoreRoot(workspaceRoot, workspaceConfig)
228
230
  const projectMetas =
229
231
  reused !== undefined
@@ -286,7 +288,7 @@ export async function prepareRun(options: RunOptions, log: Logger): Promise<Prep
286
288
  workspaceRoot,
287
289
  options.artifactCeiling,
288
290
  'open',
289
- storeRoot,
291
+ await storeRoot,
290
292
  )
291
293
  localCache.assertWritable()
292
294
  // Two digests from one read: the config-evaluation cache keys on every
@@ -24,10 +24,11 @@ import path from 'node:path'
24
24
  * first root commit; null for a shallow clone with no remote and outside
25
25
  * git: those share nothing.
26
26
  */
27
- export function repoIdOf(root: string): string | null {
27
+ export async function repoIdOf(root: string): Promise<string | null> {
28
28
  const located = locateGitDir(root)
29
29
  if (located === null) return null
30
- const identity = remoteIdentity(root, located.commonDir) ?? firstCommit(root)
30
+ const identity =
31
+ (await remoteIdentity(root, located.commonDir)) ?? (await firstCommit(located.commonDir, root))
31
32
  if (identity === null) return null
32
33
  const rel = path.relative(located.gitRoot, path.resolve(root)).split(path.sep).join('/')
33
34
  const key = sha256(`${identity}#${rel}`)
@@ -108,10 +109,10 @@ function readOwnedFile(p: string): string | null {
108
109
  }
109
110
 
110
111
  /** The remote's identity from the config file, else from `git remote -v`. */
111
- function remoteIdentity(root: string, commonDir: string): string | null {
112
+ async function remoteIdentity(root: string, commonDir: string): Promise<string | null> {
112
113
  const config = readOwnedFile(path.join(commonDir, 'config'))
113
114
  const remotes = config === null ? null : parseGitConfigRemotes(config)
114
- const urls = remotes ?? remotesFromGit(root)
115
+ const urls = remotes ?? (await remotesFromGit(root))
115
116
  const found = new Map<string, string>()
116
117
  let first: string | null = null
117
118
  for (const [name, url] of urls) {
@@ -160,8 +161,8 @@ export function parseGitConfigRemotes(contents: string): [string, string][] | nu
160
161
  return remotes
161
162
  }
162
163
 
163
- function remotesFromGit(root: string): [string, string][] {
164
- const out = git(root, ['remote', '-v'])
164
+ async function remotesFromGit(root: string): Promise<[string, string][]> {
165
+ const out = await git(root, ['remote', '-v'])
165
166
  if (out === null) return []
166
167
  const pairs: [string, string][] = []
167
168
  for (const line of out.split('\n')) {
@@ -183,17 +184,22 @@ export function parseRemoteUrl(url: string): string | null {
183
184
  return ssh === null ? null : `${ssh[1]}/${ssh[3]}/${ssh[4]}`
184
185
  }
185
186
 
186
- /** The sorted-first root commit, so every clone agrees; null when shallow. */
187
- function firstCommit(root: string): string | null {
188
- if (git(root, ['rev-parse', '--is-shallow-repository'])?.trim() === 'true') return null
189
- const roots = git(root, ['rev-list', '--max-parents=0', 'HEAD'])
187
+ /**
188
+ * The sorted-first root commit, so every clone agrees; null when shallow.
189
+ * Shallow is git's own test, a `shallow` file in the common dir: asking
190
+ * `rev-parse` cost a spawn on every run of a repo with no remote.
191
+ */
192
+ async function firstCommit(commonDir: string, root: string): Promise<string | null> {
193
+ if (existsSync(path.join(commonDir, 'shallow'))) return null
194
+ const roots = await git(root, ['rev-list', '--max-parents=0', 'HEAD'])
190
195
  return roots?.split(/\r?\n/).filter(Boolean).sort()[0] ?? null
191
196
  }
192
197
 
193
- function git(cwd: string, args: string[]): string | null {
198
+ async function git(cwd: string, args: string[]): Promise<string | null> {
194
199
  try {
195
- const p = Bun.spawnSync(['git', ...args], { cwd, stdout: 'pipe', stderr: 'ignore' })
196
- return p.exitCode === 0 ? p.stdout.toString() : null
200
+ const p = Bun.spawn(['git', ...args], { cwd, stdout: 'pipe', stderr: 'ignore' })
201
+ const [out, code] = await Promise.all([new Response(p.stdout).text(), p.exited])
202
+ return code === 0 ? out : null
197
203
  } catch {
198
204
  return null
199
205
  }
@@ -385,17 +385,21 @@ export function resolveCacheDir(root: string, config: WorkspaceConfig | null): s
385
385
  * `VX_CACHE_DIR`): that directory then holds everything, as one
386
386
  * workspace's alone. Null with no repository identity or no home.
387
387
  */
388
- export function resolveStoreRoot(root: string, config: WorkspaceConfig | null): string | null {
388
+ export async function resolveStoreRoot(
389
+ root: string,
390
+ config: WorkspaceConfig | null,
391
+ ): Promise<string | null> {
389
392
  if (config?.cacheDir !== undefined || process.env['VX_CACHE_DIR']) return null
390
393
  // HOME first: Bun's homedir() keeps the HOME the process started with (1.4.2).
391
394
  const home = process.env['HOME'] || homedir()
392
395
  if (!path.isAbsolute(home)) return null
393
396
  let id = repoIds.get(root)
394
397
  if (id === undefined) repoIds.set(root, (id = repoIdOf(root)))
395
- return id === null ? null : path.join(home, '.vx', id, 'cache')
398
+ const resolved = await id
399
+ return resolved === null ? null : path.join(home, '.vx', resolved, 'cache')
396
400
  }
397
401
 
398
- const repoIds = new Map<string, string | null>()
402
+ const repoIds = new Map<string, Promise<string | null>>()
399
403
 
400
404
  /**
401
405
  * Read the workspace's package-glob list, supporting all common