@vzn/vx 0.0.453 → 0.0.455
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.455",
|
|
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.
|
|
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.455",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.455",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.455",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.455"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
|
@@ -130,12 +130,16 @@ export async function saveMiss(a: SaveMissArgs): Promise<{ landed: Promise<void>
|
|
|
130
130
|
// `cache.outputs` is not a write grant; `exec.sandbox.allow.write` is.
|
|
131
131
|
const sandboxed = node.config.exec?.sandbox !== undefined
|
|
132
132
|
const grantsWrite = (node.config.exec?.sandbox?.allow?.write?.length ?? 0) > 0
|
|
133
|
-
|
|
133
|
+
// The workspace side too: a `workspaceFiles` directory linked out of
|
|
134
|
+
// the workspace drops every file the same way (M-65).
|
|
135
|
+
const linkedOut =
|
|
136
|
+
outputDirLinkedOut(node.projectDir, a.outputs, 'the project') ??
|
|
137
|
+
outputDirLinkedOut(a.workspaceRoot, a.wsOutputs, 'the workspace')
|
|
134
138
|
log.status(
|
|
135
139
|
`[vx] ${node.id}: cache.outputs matched no files (${[...a.outputs, ...a.wsOutputs].join(', ')}) — ` +
|
|
136
140
|
`an empty artifact is saved; a later hit restores nothing` +
|
|
137
141
|
(linkedOut !== undefined
|
|
138
|
-
? ` — ${linkedOut.dir} is a symlink to ${linkedOut.target}, outside
|
|
142
|
+
? ` — ${linkedOut.dir} is a symlink to ${linkedOut.target}, outside ${linkedOut.base}, and vx keeps only outputs inside it: make ${linkedOut.dir} a directory`
|
|
139
143
|
: sandboxed && !grantsWrite
|
|
140
144
|
? ` — the task is sandboxed and declares no exec.sandbox.allow.write, so its writes never reached disk`
|
|
141
145
|
: ''),
|
|
@@ -269,14 +273,16 @@ function markWritten(
|
|
|
269
273
|
|
|
270
274
|
/**
|
|
271
275
|
* The first output directory a pattern names that is a symlink resolving
|
|
272
|
-
* outside the project
|
|
273
|
-
*
|
|
276
|
+
* outside `baseDir` (the project, or the workspace root for
|
|
277
|
+
* `workspaceFiles`): every file under it is dropped as outside, and the
|
|
278
|
+
* empty-artifact warning blamed the glob (M-61, M-65).
|
|
274
279
|
*/
|
|
275
280
|
function outputDirLinkedOut(
|
|
276
|
-
|
|
281
|
+
baseDir: string,
|
|
277
282
|
outputs: readonly string[],
|
|
278
|
-
|
|
279
|
-
|
|
283
|
+
base: string,
|
|
284
|
+
): { dir: string; target: string; base: string } | undefined {
|
|
285
|
+
const realProject = realpathOrNull(baseDir) ?? baseDir
|
|
280
286
|
// `dist` names the tree `dist/**`: the same reading the resolver gives.
|
|
281
287
|
for (const pattern of asTrees(outputs.filter((o) => !o.startsWith('!')))) {
|
|
282
288
|
const literal = staticPrefix(pattern)
|
|
@@ -284,7 +290,7 @@ function outputDirLinkedOut(
|
|
|
284
290
|
.filter((p) => p !== '' && p !== '.')
|
|
285
291
|
for (let i = 1; i <= literal.length; i++) {
|
|
286
292
|
const dir = literal.slice(0, i).join('/')
|
|
287
|
-
const abs = path.join(
|
|
293
|
+
const abs = path.join(baseDir, dir)
|
|
288
294
|
try {
|
|
289
295
|
if (!lstatSync(abs).isSymbolicLink()) continue
|
|
290
296
|
} catch {
|
|
@@ -292,7 +298,7 @@ function outputDirLinkedOut(
|
|
|
292
298
|
}
|
|
293
299
|
const target = realpathOrNull(abs)
|
|
294
300
|
if (target !== null && target !== realProject && !target.startsWith(realProject + path.sep)) {
|
|
295
|
-
return { dir, target }
|
|
301
|
+
return { dir, target, base }
|
|
296
302
|
}
|
|
297
303
|
}
|
|
298
304
|
}
|
|
@@ -536,7 +536,7 @@ export function validateProjectConfig(config: ProjectConfig, configPath: string)
|
|
|
536
536
|
if (typeof g !== 'string' || g.length === 0) {
|
|
537
537
|
throw new UserError(`${where}.cache.outputs.files must be an array of non-empty strings`)
|
|
538
538
|
}
|
|
539
|
-
if (
|
|
539
|
+
if (isAbsoluteGlob(g)) {
|
|
540
540
|
throw new UserError(
|
|
541
541
|
`${where}.cache.outputs.files: absolute paths are not allowed (got "${g}") — ` +
|
|
542
542
|
`outputs must be project-relative globs`,
|
|
@@ -574,7 +574,7 @@ export function validateProjectConfig(config: ProjectConfig, configPath: string)
|
|
|
574
574
|
if (typeof g !== 'string' || g.length === 0) {
|
|
575
575
|
throw new UserError(`${where}.cache.inputs.files must be an array of non-empty strings`)
|
|
576
576
|
}
|
|
577
|
-
if (g.startsWith('
|
|
577
|
+
if (!g.startsWith('!/') && isAbsoluteGlob(g)) {
|
|
578
578
|
throw new UserError(
|
|
579
579
|
`${where}.cache.inputs.files: absolute paths are not allowed (got "${g}") — ` +
|
|
580
580
|
`inputs must be project-relative globs`,
|
|
@@ -1025,7 +1025,19 @@ function namesDirItself(glob: string): boolean {
|
|
|
1025
1025
|
*/
|
|
1026
1026
|
function hasParentSegment(glob: string): boolean {
|
|
1027
1027
|
const g = glob.startsWith('!') ? glob.slice(1) : glob
|
|
1028
|
-
|
|
1028
|
+
// A brace alternative is a segment too: `{../shared,src}/**` splits on `/`
|
|
1029
|
+
// into `{..`, so it passed, and its `..` arm matched nothing — an input
|
|
1030
|
+
// that silently left the key (M-64).
|
|
1031
|
+
return g.split(/[/{},]/).some((seg) => seg === '..')
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* True when a glob is absolute, or a brace alternative in it is:
|
|
1036
|
+
* `{/etc,dist}/*` matched nothing outside and said so nowhere (M-64).
|
|
1037
|
+
*/
|
|
1038
|
+
function isAbsoluteGlob(glob: string): boolean {
|
|
1039
|
+
const g = glob.startsWith('!') ? glob.slice(1) : glob
|
|
1040
|
+
return g.startsWith('/') || (g.includes('{') && /[{,]\//.test(g))
|
|
1029
1041
|
}
|
|
1030
1042
|
|
|
1031
1043
|
/**
|
|
@@ -1203,7 +1215,7 @@ function validateWorkspaceGlobs(v: unknown, where: string, negation: boolean): v
|
|
|
1203
1215
|
if (typeof g !== 'string' || g.length === 0) {
|
|
1204
1216
|
throw new UserError(`${where} must be an array of non-empty strings`)
|
|
1205
1217
|
}
|
|
1206
|
-
if (
|
|
1218
|
+
if (isAbsoluteGlob(g)) {
|
|
1207
1219
|
throw new UserError(
|
|
1208
1220
|
`${where}: absolute paths are not allowed (got "${g}") — ` +
|
|
1209
1221
|
`entries are workspace-root-relative globs`,
|