@vzn/vx 0.0.636 → 0.0.637
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 +39 -11
- package/docs/caching.md +213 -207
- package/docs/cli.md +699 -655
- package/docs/execution.md +67 -75
- package/docs/features.md +44 -40
- package/docs/patterns.md +26 -25
- package/docs/schema.md +376 -198
- package/docs/security.md +76 -39
- package/launcher.sh +43 -0
- package/package.json +5 -5
- package/schemas/cache.json +41 -19
- package/schemas/info.json +23 -11
- package/schemas/last.json +48 -2
- package/schemas/lock.json +1 -1
- package/schemas/plan.json +2 -2
- package/schemas/show.json +5 -3
- package/schemas/why.json +15 -1
- package/skills/vx/SKILL.md +16 -12
- package/src/bin.ts +17 -2
- package/src/cache/archive.ts +11 -0
- package/src/cache/cache.ts +105 -53
- package/src/cache/git-inputs.ts +277 -63
- package/src/cache/index.ts +7 -1
- package/src/cache/inputs.ts +125 -35
- package/src/cache/key-fold.ts +8 -7
- package/src/cache/layer.ts +8 -17
- package/src/cache/layered-cache.ts +16 -102
- package/src/cache/policy.ts +25 -29
- package/src/cache/run-history.ts +36 -14
- package/src/cache/schema.ts +21 -23
- package/src/cache/scoped-cache.ts +224 -0
- package/src/cli/cache.ts +150 -28
- package/src/cli/completions.ts +2 -2
- package/src/cli/core-alias.ts +1 -1
- package/src/cli/foreign-flags.ts +13 -12
- package/src/cli/help.ts +94 -63
- package/src/cli/index.ts +42 -13
- package/src/cli/info.ts +38 -23
- package/src/cli/init.ts +115 -406
- package/src/cli/last.ts +145 -4
- package/src/cli/lock.ts +333 -111
- package/src/cli/plan-format.ts +2 -2
- package/src/cli/plugin-commands.ts +5 -9
- package/src/cli/plugin-templates.ts +10 -10
- package/src/cli/run.ts +103 -149
- package/src/cli/select.ts +33 -39
- package/src/cli/show.ts +17 -19
- package/src/cli/task-verb.ts +2 -9
- package/src/cli/upgrade.ts +402 -145
- package/src/cli/watch-cycle.ts +9 -2
- package/src/cli/watch-filter.ts +33 -60
- package/src/cli/watch-fs.ts +29 -16
- package/src/cli/watch-judge.ts +12 -3
- package/src/cli/watch-set.ts +16 -42
- package/src/cli/watch.ts +188 -215
- package/src/cli/why.ts +28 -17
- package/src/cli/workspace-config.ts +31 -68
- package/src/config.ts +169 -70
- package/src/exec/env.ts +1 -31
- package/src/exec/executor.ts +1 -5
- package/src/exec/kill-tree.ts +2 -1
- package/src/exec/sandbox-runtime.ts +12 -1
- package/src/exec/sandbox-violations.ts +9 -1
- package/src/graph/dependency-spec.ts +1 -1
- package/src/graph/index.ts +1 -0
- package/src/graph/priorities.ts +1 -1
- package/src/graph/scheduler.ts +19 -7
- package/src/graph/task-graph.ts +44 -20
- package/src/handoff.ts +169 -0
- package/src/index.ts +24 -57
- package/src/orchestrator/affected-tasks.ts +7 -5
- package/src/orchestrator/command-windows.ts +44 -0
- package/src/orchestrator/deferred-outputs.ts +12 -10
- package/src/orchestrator/doctor.ts +98 -110
- package/src/orchestrator/download-policy.ts +3 -3
- package/src/orchestrator/events.ts +19 -3
- package/src/orchestrator/excluded-keys.ts +0 -2
- package/src/orchestrator/execute-task.ts +158 -63
- package/src/orchestrator/fingerprint-watch.ts +18 -15
- package/src/orchestrator/forecast.ts +10 -2
- package/src/orchestrator/framed-output.ts +8 -3
- package/src/orchestrator/graph-stage.ts +5 -5
- package/src/orchestrator/history.ts +70 -30
- package/src/orchestrator/hit-restore.ts +15 -3
- package/src/orchestrator/index.ts +18 -4
- package/src/orchestrator/local-shortcircuit.ts +2 -1
- package/src/orchestrator/lockfile-claim.ts +79 -20
- package/src/orchestrator/logger.ts +71 -39
- package/src/orchestrator/metrics.ts +71 -6
- package/src/orchestrator/miss-save.ts +3 -0
- package/src/orchestrator/options.ts +42 -29
- package/src/orchestrator/output-host.ts +72 -0
- package/src/orchestrator/plan.ts +6 -8
- package/src/orchestrator/plugin-host.ts +87 -63
- package/src/orchestrator/plugin.ts +94 -60
- package/src/orchestrator/prepare.ts +113 -72
- package/src/orchestrator/projects.ts +102 -99
- package/src/orchestrator/remote-prefetch.ts +2 -1
- package/src/orchestrator/run-context.ts +25 -6
- package/src/orchestrator/run-failures.ts +17 -8
- package/src/orchestrator/run-lock.ts +1 -1
- package/src/orchestrator/run-report.ts +9 -9
- package/src/orchestrator/run-settings.ts +48 -0
- package/src/orchestrator/run.ts +121 -95
- package/src/orchestrator/sandbox-fix.ts +39 -20
- package/src/orchestrator/sandbox-request.ts +84 -13
- package/src/orchestrator/signals.ts +1 -1
- package/src/orchestrator/stable-keys.ts +13 -11
- package/src/orchestrator/status-line.ts +48 -6
- package/src/orchestrator/summary.ts +46 -62
- package/src/orchestrator/task-hash.ts +12 -83
- package/src/orchestrator/telemetry.ts +3 -3
- package/src/settings.ts +417 -0
- package/src/util/errors.ts +1 -1
- package/src/util/essential-env.ts +31 -0
- package/src/util/hide-env.ts +107 -0
- package/src/util/index.ts +8 -2
- package/src/util/paths.ts +10 -0
- package/src/util/settle.ts +49 -30
- package/src/util/size.ts +23 -4
- package/src/util/timing.ts +3 -2
- package/src/util/ulid.ts +1 -1
- package/src/util/verbs.ts +0 -15
- package/src/workspace/affected.ts +77 -222
- package/src/workspace/config-eval.ts +15 -20
- package/src/workspace/config-imports.ts +78 -3
- package/src/workspace/config-schema.ts +209 -268
- package/src/workspace/dependency-rules.ts +57 -0
- package/src/workspace/filter.ts +2 -2
- package/src/workspace/fingerprint.ts +11 -121
- package/src/workspace/index.ts +20 -27
- package/src/workspace/load-reads.ts +4 -4
- package/src/workspace/lockfile.ts +96 -140
- package/src/workspace/migration.ts +242 -146
- package/src/workspace/package-graph.ts +30 -216
- package/src/workspace/project-loader.ts +80 -285
- package/src/workspace/repo-id.ts +2 -1
- package/src/workspace/workspace.ts +362 -831
- package/src/cache/config-evals.ts +0 -130
- package/src/workspace/config-cache.ts +0 -661
- package/src/workspace/migrate-scripts.ts +0 -1121
package/src/cli/select.ts
CHANGED
|
@@ -14,11 +14,9 @@ import {
|
|
|
14
14
|
buildPackageGraph,
|
|
15
15
|
defaultAffectedBase,
|
|
16
16
|
findWorkspaceRoot,
|
|
17
|
-
type LoadReads,
|
|
18
17
|
type FingerprintClaims,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
loadWorkspace,
|
|
18
|
+
lockKey,
|
|
19
|
+
NO_LOCK,
|
|
22
20
|
parseFilter,
|
|
23
21
|
type ProjectMeta,
|
|
24
22
|
readLockfile,
|
|
@@ -49,15 +47,15 @@ import {
|
|
|
49
47
|
loadCliProjects,
|
|
50
48
|
loadCliWorkspace,
|
|
51
49
|
} from './workspace-config.js'
|
|
50
|
+
import { resolveSetting } from '../settings.js'
|
|
52
51
|
|
|
53
52
|
/**
|
|
54
53
|
* The projects whose tasks declare a `cache.inputs.workspaceFiles` glob
|
|
55
54
|
* matching a changed path, one inside another project included (item
|
|
56
55
|
* 954): `--affected` selects them, since the glob is that task's input. Through the run
|
|
57
56
|
* path's staged load, so a glob a `project` plugin gave a config-less
|
|
58
|
-
* package counts;
|
|
59
|
-
*
|
|
60
|
-
* do load, one by one — a broken out-of-scope config does not fail a
|
|
57
|
+
* package counts; read from the lock as a run does. A load that fails drops to the lock's
|
|
58
|
+
* entries, one by one — a project the lock does not hold does not fail a
|
|
61
59
|
* scoped run, and must not fail its selection either.
|
|
62
60
|
*/
|
|
63
61
|
export async function workspaceGlobOwners(
|
|
@@ -85,26 +83,23 @@ export async function workspaceGlobOwners(
|
|
|
85
83
|
} catch {
|
|
86
84
|
// Fall through to the per-file sweep.
|
|
87
85
|
}
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (load.frozen === true && (await readLockfile(root)) === null) {
|
|
94
|
-
throw new UserError(FROZEN_WITHOUT_LOCK)
|
|
95
|
-
}
|
|
86
|
+
// No lock is refused here, before the tolerant sweep below could answer
|
|
87
|
+
// "nothing affected" and exit 0 without ever reaching the run's own
|
|
88
|
+
// refusal. The sweep reads each entry from the lock: it never evaluates.
|
|
89
|
+
const lock = await readLockfile(root)
|
|
90
|
+
if (lock === null) throw new UserError(NO_LOCK)
|
|
96
91
|
const owners: string[] = []
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
92
|
+
for (const meta of projects) {
|
|
93
|
+
if (meta.configPath === null || meta.configPath === '') continue
|
|
94
|
+
const dir = lockKey(root, meta.dir)
|
|
95
|
+
if (!Object.hasOwn(lock.projects, dir)) continue
|
|
96
|
+
try {
|
|
97
|
+
if (declaresMatch(lock.projects[dir]!.config)) owners.push(meta.name)
|
|
98
|
+
} catch {
|
|
99
|
+
// A hand-edited entry that will not read cannot be shown to declare a
|
|
100
|
+
// matching glob; surfacing it here would fail a run the load tolerates.
|
|
101
|
+
}
|
|
102
|
+
}
|
|
108
103
|
return owners
|
|
109
104
|
}
|
|
110
105
|
|
|
@@ -133,9 +128,7 @@ async function workspaceFingerprintClaims(
|
|
|
133
128
|
}
|
|
134
129
|
|
|
135
130
|
async function loadWorkspaceProjects(cwd: string): Promise<ProjectMeta[]> {
|
|
136
|
-
|
|
137
|
-
const root = await findWorkspaceRoot(cwd, reads)
|
|
138
|
-
return await discoverCliProjects(await loadWorkspace(root, reads))
|
|
131
|
+
return await discoverCliProjects(await findWorkspaceRoot(cwd))
|
|
139
132
|
}
|
|
140
133
|
|
|
141
134
|
export async function findCwdProject(cwd: string): Promise<string | null> {
|
|
@@ -150,9 +143,8 @@ export async function findCwdProject(cwd: string): Promise<string | null> {
|
|
|
150
143
|
export async function findCwdSelection(
|
|
151
144
|
cwd: string,
|
|
152
145
|
): Promise<{ name: string; discovered: { root: string; projects: ProjectMeta[] } } | null> {
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const projects = await discoverCliProjects(await loadWorkspace(root, reads))
|
|
146
|
+
const root = await findWorkspaceRoot(cwd)
|
|
147
|
+
const projects = await discoverCliProjects(root)
|
|
156
148
|
const abs = path.resolve(cwd)
|
|
157
149
|
const within = (dirOf: (p: ProjectMeta) => string): string | null => {
|
|
158
150
|
let best: string | null = null
|
|
@@ -251,14 +243,17 @@ function refusalOf(err: unknown): Refusal {
|
|
|
251
243
|
|
|
252
244
|
/**
|
|
253
245
|
* The filter `--affected[=<base>]` stands for: `...[<base>]`, the changed
|
|
254
|
-
* projects and their dependents. An empty base is
|
|
255
|
-
*
|
|
246
|
+
* projects and their dependents. An empty base is `run.affectedBase`
|
|
247
|
+
* (its variable, then the workspace's, which a plugin's `config` stage may set from nx.json's
|
|
256
248
|
* `defaultBase` or `TURBO_SCM_BASE`), then the guess.
|
|
257
249
|
*/
|
|
258
250
|
export async function affectedFilterFor(cwd: string, affected: string): Promise<string | Refusal> {
|
|
259
251
|
const root = await findWorkspaceRoot(cwd)
|
|
260
252
|
let base = affected
|
|
261
|
-
if (base === '')
|
|
253
|
+
if (base === '') {
|
|
254
|
+
const ws = (await loadCliWorkspace(root)).workspaceConfig
|
|
255
|
+
base = (resolveSetting('run.affectedBase', ws).value as string | undefined) ?? ''
|
|
256
|
+
}
|
|
262
257
|
if (base === '') {
|
|
263
258
|
try {
|
|
264
259
|
base = await defaultAffectedBase(root)
|
|
@@ -339,7 +334,6 @@ export async function resolveFilters(
|
|
|
339
334
|
workspaceGlobOwners: (changed: readonly string[], nested: readonly string[]) =>
|
|
340
335
|
workspaceGlobOwners(root, projects, changed, load, stagedOnce, nested),
|
|
341
336
|
fingerprintClaims: () => workspaceFingerprintClaims(root, projects, load),
|
|
342
|
-
taskEdges: async () => edges ?? taskEdgesFrom(await stagedOnce()),
|
|
343
337
|
...(git !== undefined ? { untracked: async () => (await git.start()).untracked } : {}),
|
|
344
338
|
}
|
|
345
339
|
let names: Set<string>
|
|
@@ -524,7 +518,7 @@ export async function pickTask(
|
|
|
524
518
|
}
|
|
525
519
|
if (entries.length === 0) {
|
|
526
520
|
process.stderr.write(
|
|
527
|
-
'vx run: no tasks declared in any project; declare one under `tasks` in a vx.config, or run `vx init` to write them from package.json scripts
|
|
521
|
+
'vx run: no tasks declared in any project; declare one under `tasks` in a vx.config, or run `vx init` to write them from package.json scripts, then `vx lock`\n',
|
|
528
522
|
)
|
|
529
523
|
return null
|
|
530
524
|
}
|
|
@@ -604,7 +598,7 @@ function didYouMeanProject(
|
|
|
604
598
|
projects: Iterable<{ name: string }>,
|
|
605
599
|
): string {
|
|
606
600
|
const names = [...projects].map((p) => p.name)
|
|
607
|
-
// A scoped name typed without its scope (`vx-
|
|
601
|
+
// A scoped name typed without its scope (`vx-otel` for `@vzn/vx-otel`) is
|
|
608
602
|
// many edits from the whole name and none from the part after the `/`.
|
|
609
603
|
const byBare = new Map<string, string[]>()
|
|
610
604
|
for (const n of names) {
|
|
@@ -615,7 +609,7 @@ function didYouMeanProject(
|
|
|
615
609
|
const typed = pattern.replace(/^!|\.\.\.$|^\.\.\.|\^/g, '')
|
|
616
610
|
// Two edits from any two-letter name: `--filter //` hinted `ui` (X-13).
|
|
617
611
|
if (typed === '//') {
|
|
618
|
-
return
|
|
612
|
+
return '. `//` is the workspace root, which is no project here: add a vx.config there and run `vx lock`'
|
|
619
613
|
}
|
|
620
614
|
const best = nearest(typed, names)
|
|
621
615
|
if (best !== undefined) return `. Did you mean ${best}?`
|
package/src/cli/show.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// `vx show [target]` — introspect the workspace's
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// line per project. `<project>`: every task's
|
|
6
|
-
//
|
|
7
|
-
//
|
|
1
|
+
// `vx show [target]` — introspect the workspace's resolved configs: what a
|
|
2
|
+
// run would see here, now. Configs load through the same path a run uses
|
|
3
|
+
// (`loadProjects`): from the lock, with the plugin `config` and `project`
|
|
4
|
+
// stages applied, so a package a plugin gives tasks to shows them. No lock
|
|
5
|
+
// is `NO_LOCK`. No target: one line per project. `<project>`: every task's
|
|
6
|
+
// resolved config. `<pkg>#<task>`: one task. `<task>`: that task in every
|
|
7
|
+
// project declaring it.
|
|
8
8
|
|
|
9
9
|
import path from 'node:path'
|
|
10
10
|
import type { ProjectConfig, TaskConfig } from '../config.js'
|
|
@@ -23,11 +23,10 @@ import {
|
|
|
23
23
|
type AffectedChanges,
|
|
24
24
|
buildPackageGraph,
|
|
25
25
|
findWorkspaceRoot,
|
|
26
|
-
type LoadReads,
|
|
27
|
-
loadWorkspace,
|
|
28
26
|
type ProjectEntry,
|
|
29
27
|
type ProjectMeta,
|
|
30
28
|
} from '../workspace/index.js'
|
|
29
|
+
import { runRules } from '../settings.js'
|
|
31
30
|
|
|
32
31
|
interface ShowArgs {
|
|
33
32
|
target?: string
|
|
@@ -83,9 +82,8 @@ export function parseShowArgs(args: readonly string[]): ShowArgs {
|
|
|
83
82
|
export async function showCmd(args: readonly string[]): Promise<number> {
|
|
84
83
|
const parsed = parseShowArgs(args)
|
|
85
84
|
if (parsed.error) return refuse('show', args, parsed.error, 'VX_E_USAGE')
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const metas = await discoverCliProjects(await loadWorkspace(root, reads))
|
|
85
|
+
const root = await findWorkspaceRoot(process.cwd())
|
|
86
|
+
const metas = await discoverCliProjects(root)
|
|
89
87
|
const byName = new Map(metas.map((m) => [m.name, m]))
|
|
90
88
|
|
|
91
89
|
// `//#task` is Turbo's root package's task: the root project here (D-39),
|
|
@@ -149,7 +147,7 @@ export async function showCmd(args: readonly string[]): Promise<number> {
|
|
|
149
147
|
affected = f
|
|
150
148
|
raw.unshift(f)
|
|
151
149
|
}
|
|
152
|
-
const r = await resolveFilters(process.cwd(), raw, {
|
|
150
|
+
const r = await resolveFilters(process.cwd(), raw, {}, affected)
|
|
153
151
|
if ('error' in r) throw new UserError(`vx show: ${r.error}`, r.code ?? 'VX_E_USAGE')
|
|
154
152
|
if ('empty' in r) process.stderr.write(`vx show: ${r.empty}\n`)
|
|
155
153
|
selected = new Set('empty' in r ? [] : r.names)
|
|
@@ -158,7 +156,7 @@ export async function showCmd(args: readonly string[]): Promise<number> {
|
|
|
158
156
|
}
|
|
159
157
|
const inSelection = (name: string): boolean => selected === undefined || selected.has(name)
|
|
160
158
|
|
|
161
|
-
const projects = await loadCliProjects(root, metas, scope, {
|
|
159
|
+
const projects = await loadCliProjects(root, metas, scope, { closure: true })
|
|
162
160
|
|
|
163
161
|
if (parsed.target === undefined) {
|
|
164
162
|
const shown = metas.filter((m) => inSelection(m.name))
|
|
@@ -200,7 +198,7 @@ export async function showCmd(args: readonly string[]): Promise<number> {
|
|
|
200
198
|
plugins: ws.plugins,
|
|
201
199
|
workspaceRoot: root,
|
|
202
200
|
cacheDir: ws.cacheDir,
|
|
203
|
-
rules: ws.workspaceConfig
|
|
201
|
+
rules: runRules(ws.workspaceConfig),
|
|
204
202
|
warn: warnToStderr,
|
|
205
203
|
}),
|
|
206
204
|
)
|
|
@@ -342,7 +340,7 @@ function renderProject(
|
|
|
342
340
|
// Round-trip so the printed object is exactly the JSON form of the
|
|
343
341
|
// resolved config (drops `undefined` fields).
|
|
344
342
|
const shown = shownConfig(config)
|
|
345
|
-
return `${JSON.stringify(
|
|
343
|
+
return `${JSON.stringify({ name, dir, config: shown })}\n`
|
|
346
344
|
}
|
|
347
345
|
const tags = config?.tags ?? []
|
|
348
346
|
const tasks = Object.entries(config?.tasks ?? {})
|
|
@@ -365,7 +363,7 @@ function renderTask(
|
|
|
365
363
|
): string {
|
|
366
364
|
if (format === 'json') {
|
|
367
365
|
const obj = { name, dir, task: taskName, config: shownTask(task) }
|
|
368
|
-
return `${JSON.stringify(
|
|
366
|
+
return `${JSON.stringify(obj)}\n`
|
|
369
367
|
}
|
|
370
368
|
return `${name} — ${dir}\n\n${taskBlock(taskName, task)}`
|
|
371
369
|
}
|
|
@@ -383,7 +381,7 @@ function renderTaskAcross(
|
|
|
383
381
|
task: taskName,
|
|
384
382
|
config: shownTask(p.config.tasks![taskName]!),
|
|
385
383
|
}))
|
|
386
|
-
return `${JSON.stringify(
|
|
384
|
+
return `${JSON.stringify(list)}\n`
|
|
387
385
|
}
|
|
388
386
|
return declaring
|
|
389
387
|
.map(
|
|
@@ -406,7 +404,7 @@ function taskBlock(taskName: string, raw: TaskConfig): string {
|
|
|
406
404
|
const exec = task.exec
|
|
407
405
|
rows.push(['command', exec?.command ?? '(group)'])
|
|
408
406
|
add('dependsOn', list(task.dependsOn))
|
|
409
|
-
add('timeout', exec?.timeout
|
|
407
|
+
add('timeout', exec?.timeout)
|
|
410
408
|
add('retries', exec?.retries)
|
|
411
409
|
add('env.passThrough', list(exec?.env?.passThrough))
|
|
412
410
|
add(
|
package/src/cli/task-verb.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// name would change meaning the day a plugin declares that verb.
|
|
6
6
|
|
|
7
7
|
import { isDefaultBuild, nxProjectTarget, projectNamed } from '../orchestrator/index.js'
|
|
8
|
-
import { findWorkspaceRoot
|
|
8
|
+
import { findWorkspaceRoot } from '../workspace/index.js'
|
|
9
9
|
import { nearest } from '../util/index.js'
|
|
10
10
|
import { findCwdProject } from './select.js'
|
|
11
11
|
import { discoverCliProjects, loadCliProjects } from './workspace-config.js'
|
|
@@ -79,14 +79,7 @@ async function workspaceProjects(
|
|
|
79
79
|
): Promise<Awaited<ReturnType<typeof loadCliProjects>> | null> {
|
|
80
80
|
try {
|
|
81
81
|
const root = await findWorkspaceRoot(cwd)
|
|
82
|
-
return await loadCliProjects(
|
|
83
|
-
root,
|
|
84
|
-
await discoverCliProjects(await loadWorkspace(root)),
|
|
85
|
-
'all',
|
|
86
|
-
{
|
|
87
|
-
noCreate: true,
|
|
88
|
-
},
|
|
89
|
-
)
|
|
82
|
+
return await loadCliProjects(root, await discoverCliProjects(root))
|
|
90
83
|
} catch {
|
|
91
84
|
// No workspace, or a config that does not load: the caller's plain line stands.
|
|
92
85
|
return null
|