@vzn/vx 0.0.122 → 0.0.124
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.124",
|
|
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.
|
|
21
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
22
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
23
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
20
|
+
"@vzn/vx-linux-x64": "0.0.124",
|
|
21
|
+
"@vzn/vx-linux-arm64": "0.0.124",
|
|
22
|
+
"@vzn/vx-darwin-x64": "0.0.124",
|
|
23
|
+
"@vzn/vx-darwin-arm64": "0.0.124"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cache/index.ts
CHANGED
package/src/cache/inputs.ts
CHANGED
|
@@ -643,7 +643,18 @@ export async function stampOutputs(args: {
|
|
|
643
643
|
outputs: string[]
|
|
644
644
|
nestedProjectDirs: string[]
|
|
645
645
|
}): Promise<Map<string, OutputStamp>> {
|
|
646
|
-
|
|
646
|
+
return stampFiles(await resolveOutputs(args))
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/** `stampOutputs` for root-anchored `cache.outputs.workspaceFiles` (A-43). */
|
|
650
|
+
export async function stampWorkspaceOutputs(args: {
|
|
651
|
+
workspaceRoot: string
|
|
652
|
+
outputs: string[]
|
|
653
|
+
}): Promise<Map<string, OutputStamp>> {
|
|
654
|
+
return stampFiles(await resolveWorkspaceOutputs(args))
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function stampFiles(files: readonly string[]): Map<string, OutputStamp> {
|
|
647
658
|
const out = new Map<string, OutputStamp>()
|
|
648
659
|
for (const f of files) {
|
|
649
660
|
try {
|
|
@@ -668,7 +679,21 @@ export async function ownOutputsSince(
|
|
|
668
679
|
args: { projectDir: string; outputs: string[]; nestedProjectDirs: string[] },
|
|
669
680
|
before: ReadonlyMap<string, OutputStamp>,
|
|
670
681
|
): Promise<string[]> {
|
|
671
|
-
|
|
682
|
+
return changedSince(await resolveOutputs(args), before)
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/** `ownOutputsSince` for root-anchored `cache.outputs.workspaceFiles` (A-43). */
|
|
686
|
+
export async function ownWorkspaceOutputsSince(
|
|
687
|
+
args: { workspaceRoot: string; outputs: string[] },
|
|
688
|
+
before: ReadonlyMap<string, OutputStamp>,
|
|
689
|
+
): Promise<string[]> {
|
|
690
|
+
return changedSince(await resolveWorkspaceOutputs(args), before)
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function changedSince(
|
|
694
|
+
after: readonly string[],
|
|
695
|
+
before: ReadonlyMap<string, OutputStamp>,
|
|
696
|
+
): string[] {
|
|
672
697
|
const own: string[] = []
|
|
673
698
|
for (const f of after) {
|
|
674
699
|
const was = before.get(f)
|
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
cleanWorkspaceOutputs,
|
|
10
10
|
type OutputStamp,
|
|
11
11
|
ownOutputsSince,
|
|
12
|
+
ownWorkspaceOutputsSince,
|
|
12
13
|
stampOutputs,
|
|
14
|
+
stampWorkspaceOutputs,
|
|
13
15
|
FULL_CACHE_POLICY,
|
|
14
16
|
type GitFilesCache,
|
|
15
17
|
} from '../cache/index.js'
|
|
@@ -616,6 +618,7 @@ async function executeCachedTask(args: ExecuteArgs): Promise<TaskOutcome> {
|
|
|
616
618
|
// earlier run are its command's to clean, as they are under Turbo.
|
|
617
619
|
const additive = (node.addsToOutputsOf?.length ?? 0) > 0
|
|
618
620
|
let stampedBefore: ReadonlyMap<string, OutputStamp> | undefined
|
|
621
|
+
let wsStampedBefore: ReadonlyMap<string, OutputStamp> | undefined
|
|
619
622
|
|
|
620
623
|
// Cache lookup. On hit, time the user-perceived restore op
|
|
621
624
|
// (clean+restore+log-replay) — that's what the framed-block footer
|
|
@@ -756,7 +759,12 @@ async function executeCachedTask(args: ExecuteArgs): Promise<TaskOutcome> {
|
|
|
756
759
|
endClean()
|
|
757
760
|
args.gitFilesCache?.markOutputsChanged(node.projectDir, cleanedRels)
|
|
758
761
|
}
|
|
759
|
-
if (willWrite && !deferralRequested && wsOutputs.length > 0) {
|
|
762
|
+
if (additive && willWrite && !deferralRequested && wsOutputs.length > 0) {
|
|
763
|
+
// A root-anchored addition is stamped as the project one above: a
|
|
764
|
+
// glob clean deleted the upstream's files before this task, which
|
|
765
|
+
// reads them, ran (A-43).
|
|
766
|
+
wsStampedBefore ??= await stampWorkspaceOutputs(wsCleanArgs)
|
|
767
|
+
} else if (willWrite && !deferralRequested && wsOutputs.length > 0) {
|
|
760
768
|
// Root-anchored deletions can land in other projects' dirs; mark
|
|
761
769
|
// them so stale per-project git snapshots can't survive the wipe.
|
|
762
770
|
const cleanedWsRels = await cleanWorkspaceOutputs(wsCleanArgs)
|
|
@@ -1028,8 +1036,13 @@ async function executeCachedTask(args: ExecuteArgs): Promise<TaskOutcome> {
|
|
|
1028
1036
|
additive && stampedBefore !== undefined
|
|
1029
1037
|
? await ownOutputsSince(cleanArgs, stampedBefore)
|
|
1030
1038
|
: undefined
|
|
1039
|
+
const ownWsOutputFiles =
|
|
1040
|
+
additive && wsStampedBefore !== undefined
|
|
1041
|
+
? await ownWorkspaceOutputsSince(wsCleanArgs, wsStampedBefore)
|
|
1042
|
+
: undefined
|
|
1031
1043
|
const { landed } = await saveMiss({
|
|
1032
1044
|
...(ownOutputFiles !== undefined ? { ownOutputFiles } : {}),
|
|
1045
|
+
...(ownWsOutputFiles !== undefined ? { ownWsOutputFiles } : {}),
|
|
1033
1046
|
node,
|
|
1034
1047
|
hash,
|
|
1035
1048
|
cache,
|
|
@@ -71,6 +71,8 @@ export interface SaveMissArgs {
|
|
|
71
71
|
* would also take the upstream's files the task adds beside.
|
|
72
72
|
*/
|
|
73
73
|
ownOutputFiles?: string[] | undefined
|
|
74
|
+
/** `ownOutputFiles` for `cache.outputs.workspaceFiles` (A-43). */
|
|
75
|
+
ownWsOutputFiles?: string[] | undefined
|
|
74
76
|
/** The Tier-3 input fingerprint rows captured by the pre-exec describe. */
|
|
75
77
|
captured: readonly TaskInputComponent[]
|
|
76
78
|
command: string
|
|
@@ -101,10 +103,12 @@ export async function saveMiss(a: SaveMissArgs): Promise<{ landed: Promise<void>
|
|
|
101
103
|
outputs: a.outputs,
|
|
102
104
|
nestedProjectDirs: a.nestedProjectDirs,
|
|
103
105
|
}))
|
|
104
|
-
const wsOutputFiles =
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
const wsOutputFiles =
|
|
107
|
+
a.ownWsOutputFiles ??
|
|
108
|
+
(await resolveWorkspaceOutputs({
|
|
109
|
+
workspaceRoot: a.workspaceRoot,
|
|
110
|
+
outputs: a.wsOutputs,
|
|
111
|
+
}))
|
|
108
112
|
endResolve()
|
|
109
113
|
// The mirror of the input warning: declared outputs that resolve to
|
|
110
114
|
// nothing save an empty artifact, and the next hit "restores" it —
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync, statSync } from 'node:fs'
|
|
1
|
+
import { accessSync, constants as fsConstants, existsSync, readFileSync, statSync } from 'node:fs'
|
|
2
2
|
import { constants as osConstants } from 'node:os'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { execWord, exitSignal } from '../exec/index.js'
|
|
@@ -46,8 +46,12 @@ function fileVerdict(file: string): string {
|
|
|
46
46
|
try {
|
|
47
47
|
const st = statSync(file)
|
|
48
48
|
if (st.isDirectory()) return 'is a directory'
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
// Ask the kernel: an execute bit for the group or others does not
|
|
50
|
+
// make a file this user owns executable to this user (root aside).
|
|
51
|
+
if (!executable(file)) return 'is not executable — chmod +x it'
|
|
52
|
+
// The kernel reads the #! line as bytes, and a path's bytes are its
|
|
53
|
+
// UTF-8: a latin1 read named a present non-ASCII interpreter missing.
|
|
54
|
+
head = readFileSync(file).subarray(0, 256).toString('utf8')
|
|
51
55
|
} catch (err) {
|
|
52
56
|
return `could not be read (${(err as Error).message})`
|
|
53
57
|
}
|
|
@@ -115,3 +119,12 @@ const SIGNAL_WHY: Record<string, string> = {
|
|
|
115
119
|
SIGSYS: 'a system call was refused — a seccomp filter, or the sandbox',
|
|
116
120
|
SIGQUIT: 'the process was sent a quit (Ctrl-\\, or a kill -QUIT); a core dump may be beside it',
|
|
117
121
|
}
|
|
122
|
+
|
|
123
|
+
function executable(file: string): boolean {
|
|
124
|
+
try {
|
|
125
|
+
accessSync(file, fsConstants.X_OK)
|
|
126
|
+
return true
|
|
127
|
+
} catch {
|
|
128
|
+
return false
|
|
129
|
+
}
|
|
130
|
+
}
|