@vzn/vx 0.0.337 → 0.0.339
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.
|
|
3
|
+
"version": "0.0.339",
|
|
4
4
|
"description": "A fast, correct 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.
|
|
25
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
26
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
27
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
24
|
+
"@vzn/vx-linux-x64": "0.0.339",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.339",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.339",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.339"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cache/git-inputs.ts
CHANGED
|
@@ -753,8 +753,7 @@ function parseStatusOutput(
|
|
|
753
753
|
const untracked: string[] = []
|
|
754
754
|
const undecodable = new Set<string>()
|
|
755
755
|
let ignoredAttributes = false
|
|
756
|
-
for (
|
|
757
|
-
const token = tokens[i]!
|
|
756
|
+
for (const token of tokens) {
|
|
758
757
|
if (token.length < 4) continue
|
|
759
758
|
if (token[0] === '!') {
|
|
760
759
|
if (token === '!! .gitattributes' || token.endsWith('/.gitattributes'))
|
|
@@ -767,19 +766,9 @@ function parseStatusOutput(
|
|
|
767
766
|
untracked.push(token.slice(3))
|
|
768
767
|
continue
|
|
769
768
|
}
|
|
769
|
+
// No rename record follows: the spawn passes `--no-renames`, so a
|
|
770
|
+
// rename's source is a record of its own (items 976, A-59).
|
|
770
771
|
dirty.add(token.slice(3))
|
|
771
|
-
// An R or C in either column → the next token is the rename/copy
|
|
772
|
-
// source path. Y carries it for a rename git sees in the worktree (the
|
|
773
|
-
// copy added with `git add -N`, the original removed): read on X alone,
|
|
774
|
-
// the source was parsed as a record of its own, stayed trusted, and a
|
|
775
|
-
// deleted file was keyed from the index (item 976).
|
|
776
|
-
const x = token[0]
|
|
777
|
-
const y = token[1]
|
|
778
|
-
const renamed = x === 'R' || x === 'C' || y === 'R' || y === 'C'
|
|
779
|
-
if (renamed && i + 1 < tokens.length && tokens[i + 1]!.length > 0) {
|
|
780
|
-
i++
|
|
781
|
-
dirty.add(tokens[i]!)
|
|
782
|
-
}
|
|
783
772
|
}
|
|
784
773
|
return { dirty, untracked, undecodable, ignoredAttributes }
|
|
785
774
|
}
|
|
@@ -931,7 +920,18 @@ export async function startGitEnumeration(
|
|
|
931
920
|
// `--ignored=matching` names an ignored path without descending into an
|
|
932
921
|
// ignored directory: git applies an ignored `.gitattributes` as it does
|
|
933
922
|
// any other, and the filter gate below must see it (A-19).
|
|
934
|
-
|
|
923
|
+
// `--no-renames`: a deletion paired as an unmerged path's rename source
|
|
924
|
+
// prints only as `UU <path>`, and the deleted file stayed trusted (A-59).
|
|
925
|
+
spawnGit([
|
|
926
|
+
'status',
|
|
927
|
+
'--porcelain',
|
|
928
|
+
'-z',
|
|
929
|
+
'-uall',
|
|
930
|
+
'--ignored=matching',
|
|
931
|
+
'--no-renames',
|
|
932
|
+
'--',
|
|
933
|
+
...pathspecs,
|
|
934
|
+
]),
|
|
935
935
|
// The gate for whether a clean filter can rewrite bytes between the
|
|
936
936
|
// index and the worktree: git's merged config (`core.autocrlf`) and,
|
|
937
937
|
// from git 2.42, the attributes files it reads outside the tree
|
package/src/cache/key-fold.ts
CHANGED
|
@@ -96,7 +96,10 @@ import type { CacheKeyInput } from './layer.js'
|
|
|
96
96
|
// artifact records none (item 943).
|
|
97
97
|
// v36: each artifact ends in a `.vx-sum` CRC-32 of its entries, checked on
|
|
98
98
|
// scan and restore; a v35 artifact has none (L-19).
|
|
99
|
-
|
|
99
|
+
// v37: an entry saved while `git status` hid a deletion (paired as an
|
|
100
|
+
// unmerged path's rename source) holds output built without a file its key
|
|
101
|
+
// folds (A-59).
|
|
102
|
+
export const CACHE_VERSION = 'vx-cache-v37'
|
|
100
103
|
|
|
101
104
|
/**
|
|
102
105
|
* The digest a key folds for an input gone between its enumeration and its
|
|
@@ -34,20 +34,55 @@ import type {
|
|
|
34
34
|
* clean. We pair them by pid instead (a process has at most
|
|
35
35
|
* one syscall in flight, so the pid is a sufficient key).
|
|
36
36
|
*
|
|
37
|
-
* We capture the first quoted-string argument as the path
|
|
38
|
-
* are relative resolve against the
|
|
37
|
+
* We capture the first quoted-string argument as the path, a C string
|
|
38
|
+
* (`cStringPath` decodes it). Paths that are relative resolve against the
|
|
39
|
+
* task's cwd (set by Bun.spawn).
|
|
39
40
|
*/
|
|
40
41
|
const SYSCALLS = 'openat|access|statx|newfstatat'
|
|
42
|
+
const QUOTED = '"((?:[^"\\\\]|\\\\.)+)"'
|
|
41
43
|
const STRACE_DONE_RE = new RegExp(
|
|
42
|
-
`^(\\d+)\\s+(${SYSCALLS})\\([^"]
|
|
44
|
+
`^(\\d+)\\s+(${SYSCALLS})\\([^"]*${QUOTED}[^)]*\\)\\s*=\\s*-1\\s+(ENOENT|EACCES|EPERM)`,
|
|
43
45
|
)
|
|
44
|
-
const STRACE_UNFINISHED_RE = new RegExp(`^(\\d+)\\s+(${SYSCALLS})\\([^"]
|
|
46
|
+
const STRACE_UNFINISHED_RE = new RegExp(`^(\\d+)\\s+(${SYSCALLS})\\([^"]*${QUOTED}[^)]*<unfinished`)
|
|
45
47
|
const STRACE_RESUMED_RE = new RegExp(
|
|
46
48
|
`^(\\d+)\\s+<\\.\\.\\. (${SYSCALLS}) resumed>.*?=\\s*-1\\s+(ENOENT|EACCES|EPERM)`,
|
|
47
49
|
)
|
|
48
50
|
/** A resumed call that SUCCEEDED — clears the pending entry, emits nothing. */
|
|
49
51
|
const STRACE_RESUMED_OK_RE = new RegExp(`^(\\d+)\\s+<\\.\\.\\. (${SYSCALLS}) resumed>`)
|
|
50
52
|
|
|
53
|
+
const C_ESCAPES: Record<string, number> = { n: 10, t: 9, r: 13, v: 11, f: 12, a: 7, b: 8 }
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The path a strace C string spells. strace escapes a quote, a backslash
|
|
57
|
+
* and a control byte, and writes a non-ASCII byte as octal (`é` is
|
|
58
|
+
* `\303\251`): read raw, `q"t.txt` was cut at `q\` and `é.txt` named
|
|
59
|
+
* `\303\251.txt`, so the report and every `ignore` pattern missed the file.
|
|
60
|
+
*/
|
|
61
|
+
function cStringPath(raw: string): string {
|
|
62
|
+
if (!raw.includes('\\')) return raw
|
|
63
|
+
const bytes: number[] = []
|
|
64
|
+
for (let i = 0; i < raw.length; i++) {
|
|
65
|
+
const c = raw[i]!
|
|
66
|
+
if (c !== '\\') {
|
|
67
|
+
bytes.push(...Buffer.from(c, 'utf8'))
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
const next = raw[++i] ?? ''
|
|
71
|
+
const octal = /^[0-7]{1,3}/.exec(raw.slice(i, i + 3))?.[0]
|
|
72
|
+
const hex = next === 'x' ? /^[0-9a-fA-F]{1,2}/.exec(raw.slice(i + 1, i + 3))?.[0] : undefined
|
|
73
|
+
if (octal !== undefined) {
|
|
74
|
+
bytes.push(parseInt(octal, 8))
|
|
75
|
+
i += octal.length - 1
|
|
76
|
+
} else if (hex !== undefined) {
|
|
77
|
+
bytes.push(parseInt(hex, 16))
|
|
78
|
+
i += hex.length
|
|
79
|
+
} else {
|
|
80
|
+
bytes.push(C_ESCAPES[next] ?? next.charCodeAt(0))
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return Buffer.from(bytes).toString('utf8')
|
|
84
|
+
}
|
|
85
|
+
|
|
51
86
|
/** One denied syscall, however strace chose to lay it out. */
|
|
52
87
|
export interface DeniedCall {
|
|
53
88
|
syscall: string
|
|
@@ -68,7 +103,7 @@ export function deniedCalls(text: string): DeniedCall[] {
|
|
|
68
103
|
for (const line of text.split('\n')) {
|
|
69
104
|
const done = STRACE_DONE_RE.exec(line)
|
|
70
105
|
if (done?.[2] !== undefined && done[3] !== undefined && done[4] !== undefined) {
|
|
71
|
-
out.push({ syscall: done[2], rawPath: done[3], errno: done[4] })
|
|
106
|
+
out.push({ syscall: done[2], rawPath: cStringPath(done[3]), errno: done[4] })
|
|
72
107
|
continue
|
|
73
108
|
}
|
|
74
109
|
const unfinished = STRACE_UNFINISHED_RE.exec(line)
|
|
@@ -77,7 +112,7 @@ export function deniedCalls(text: string): DeniedCall[] {
|
|
|
77
112
|
unfinished[2] !== undefined &&
|
|
78
113
|
unfinished[3] !== undefined
|
|
79
114
|
) {
|
|
80
|
-
pending.set(unfinished[1], { syscall: unfinished[2], rawPath: unfinished[3] })
|
|
115
|
+
pending.set(unfinished[1], { syscall: unfinished[2], rawPath: cStringPath(unfinished[3]) })
|
|
81
116
|
continue
|
|
82
117
|
}
|
|
83
118
|
const resumedOk = STRACE_RESUMED_OK_RE.exec(line)
|
|
@@ -615,7 +615,7 @@ export async function installPlugins(args: InstallPluginsArgs): Promise<() => vo
|
|
|
615
615
|
} catch (err) {
|
|
616
616
|
throw new PluginSetupError(
|
|
617
617
|
plugin,
|
|
618
|
-
`plugin '${plugin.name}' failed
|
|
618
|
+
`plugin '${plugin.name}' failed in setup: ${err instanceof Error ? err.message : String(err)}`,
|
|
619
619
|
)
|
|
620
620
|
}
|
|
621
621
|
}
|
|
@@ -247,12 +247,13 @@ function upstreamBuildOnWorker(tasks: GeneratedTask[]): void {
|
|
|
247
247
|
/**
|
|
248
248
|
* A root script that runs the members rather than checking the repo:
|
|
249
249
|
* pnpm's `-r` / `--filter` / `-C`, Yarn's `--cwd` (excalidraw's
|
|
250
|
-
* `build:common`)
|
|
250
|
+
* `build:common`) and `yarn workspace <name>` (cal.com's `prisma`), npm's
|
|
251
|
+
* `--prefix`, the workspace flags, Bun's `--filter`,
|
|
251
252
|
* Vite+'s `vp run` (tiptap), and the other runners, vx itself included. Mapped, it ran every member's
|
|
252
253
|
* task again beside the member's own (D-45).
|
|
253
254
|
*/
|
|
254
255
|
const RUNS_MEMBERS =
|
|
255
|
-
/(^|[\s;&|(])(turbo|nx|lerna|ultra|wireit|nps|moon|rush|vx)(\s|$)|\bworkspaces?\s+(foreach|run)\b|\bvp\s+run\s|\bcd\s/
|
|
256
|
+
/(^|[\s;&|(])(turbo|nx|lerna|ultra|wireit|nps|moon|rush|vx)(\s|$)|\bworkspaces?\s+(foreach|run)\b|\byarn\s+workspace\s|\bvp\s+run\s|\bcd\s/
|
|
256
257
|
|
|
257
258
|
/** A workspace flag, read only where a package manager takes it (`pmRunsMembers`). */
|
|
258
259
|
const MEMBER_FLAG =
|