@vzn/vx 0.0.21 → 0.0.22

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.
Files changed (123) hide show
  1. package/README.md +21 -18
  2. package/package.json +6 -6
  3. package/src/bin.ts +52 -3
  4. package/src/cache/archive.ts +170 -21
  5. package/src/cache/cache.ts +667 -288
  6. package/src/cache/chained-cache.ts +14 -2
  7. package/src/cache/config-evals.ts +29 -6
  8. package/src/cache/file-hashes.ts +63 -35
  9. package/src/cache/git-inputs.ts +398 -69
  10. package/src/cache/index.ts +8 -1
  11. package/src/cache/inputs.ts +403 -102
  12. package/src/cache/key-fold.ts +232 -0
  13. package/src/cache/layer.ts +81 -11
  14. package/src/cache/layered-cache.ts +199 -32
  15. package/src/cache/output-index.ts +109 -10
  16. package/src/cache/run-history.ts +8 -3
  17. package/src/cache/zstd.ts +53 -30
  18. package/src/cli/cache.ts +51 -21
  19. package/src/cli/format.ts +4 -15
  20. package/src/cli/help.ts +13 -3
  21. package/src/cli/index.ts +35 -9
  22. package/src/cli/info.ts +49 -7
  23. package/src/cli/init.ts +17 -5
  24. package/src/cli/last.ts +96 -21
  25. package/src/cli/lock.ts +34 -17
  26. package/src/cli/plan-format.ts +4 -4
  27. package/src/cli/plugin-commands.ts +20 -5
  28. package/src/cli/run.ts +55 -12
  29. package/src/cli/select.ts +23 -13
  30. package/src/cli/show.ts +9 -4
  31. package/src/cli/upgrade.ts +134 -14
  32. package/src/cli/watch.ts +611 -102
  33. package/src/cli/why.ts +8 -16
  34. package/src/cli/workspace-config.ts +21 -9
  35. package/src/config.ts +46 -9
  36. package/src/exec/executor.ts +51 -5
  37. package/src/exec/index.ts +10 -0
  38. package/src/exec/kill-tree.ts +281 -0
  39. package/src/exec/local-executor.ts +1 -1
  40. package/src/exec/runner.ts +327 -82
  41. package/src/exec/sandbox-binds.ts +34 -5
  42. package/src/exec/sandbox-paths.ts +22 -2
  43. package/src/exec/sandbox-runtime.ts +627 -102
  44. package/src/exec/sandbox-violations.ts +36 -23
  45. package/src/graph/dependency-spec.ts +14 -14
  46. package/src/graph/index.ts +12 -2
  47. package/src/graph/priorities.ts +130 -0
  48. package/src/graph/scheduler.ts +182 -134
  49. package/src/graph/task-graph.ts +552 -188
  50. package/src/index.ts +16 -2
  51. package/src/orchestrator/admission.ts +41 -17
  52. package/src/orchestrator/colors.ts +8 -3
  53. package/src/orchestrator/deferred-outputs.ts +22 -7
  54. package/src/orchestrator/doctor.ts +122 -14
  55. package/src/orchestrator/events.ts +71 -2
  56. package/src/orchestrator/excluded-keys.ts +155 -0
  57. package/src/orchestrator/execute-task.ts +331 -37
  58. package/src/orchestrator/failure-recap.ts +121 -0
  59. package/src/orchestrator/fingerprint-watch.ts +89 -0
  60. package/src/orchestrator/framed-output.ts +81 -14
  61. package/src/orchestrator/history.ts +6 -7
  62. package/src/orchestrator/hit-restore.ts +82 -18
  63. package/src/orchestrator/index.ts +7 -1
  64. package/src/orchestrator/keyed-projects.ts +89 -0
  65. package/src/orchestrator/local-shortcircuit.ts +83 -23
  66. package/src/orchestrator/lockfile-claim.ts +152 -32
  67. package/src/orchestrator/logger.ts +116 -20
  68. package/src/orchestrator/metrics.ts +156 -60
  69. package/src/orchestrator/miss-save.ts +106 -43
  70. package/src/orchestrator/options.ts +30 -4
  71. package/src/orchestrator/persistent.ts +41 -21
  72. package/src/orchestrator/placement.ts +55 -25
  73. package/src/orchestrator/plan.ts +24 -11
  74. package/src/orchestrator/plugin-host.ts +17 -23
  75. package/src/orchestrator/plugin.ts +31 -8
  76. package/src/orchestrator/prepare.ts +99 -13
  77. package/src/orchestrator/projects.ts +19 -8
  78. package/src/orchestrator/remote-prefetch.ts +21 -1
  79. package/src/orchestrator/run-artifacts.ts +9 -0
  80. package/src/orchestrator/run-context.ts +19 -4
  81. package/src/orchestrator/run-lock.ts +349 -0
  82. package/src/orchestrator/run-records.ts +6 -23
  83. package/src/orchestrator/run-report.ts +4 -2
  84. package/src/orchestrator/run.ts +264 -57
  85. package/src/orchestrator/sandbox-request.ts +388 -62
  86. package/src/orchestrator/save-lane.ts +4 -7
  87. package/src/orchestrator/shell-verdict.ts +117 -0
  88. package/src/orchestrator/signals.ts +86 -30
  89. package/src/orchestrator/stable-keys.ts +202 -10
  90. package/src/orchestrator/status-line.ts +33 -1
  91. package/src/orchestrator/summary.ts +49 -2
  92. package/src/orchestrator/task-hash.ts +87 -7
  93. package/src/orchestrator/task-log-buffer.ts +7 -7
  94. package/src/orchestrator/telemetry-host.ts +16 -7
  95. package/src/orchestrator/telemetry.ts +69 -28
  96. package/src/orchestrator/upstream.ts +91 -45
  97. package/src/util/bun-version.ts +43 -0
  98. package/src/util/errors.ts +86 -0
  99. package/src/util/hash.ts +10 -1
  100. package/src/util/index.ts +32 -3
  101. package/src/util/paths.ts +147 -5
  102. package/src/util/procfs.ts +20 -0
  103. package/src/util/settle.ts +4 -2
  104. package/src/util/size.ts +33 -0
  105. package/src/util/task-id.ts +20 -0
  106. package/src/util/timing.ts +11 -1
  107. package/src/util/which.ts +48 -0
  108. package/src/workspace/affected.ts +272 -64
  109. package/src/workspace/config-cache.ts +262 -32
  110. package/src/workspace/config-eval.ts +49 -12
  111. package/src/workspace/config-imports.ts +126 -5
  112. package/src/workspace/config-schema.ts +237 -41
  113. package/src/workspace/filter.ts +33 -18
  114. package/src/workspace/fingerprint.ts +29 -11
  115. package/src/workspace/index.ts +7 -0
  116. package/src/workspace/json-data.ts +90 -0
  117. package/src/workspace/load-reads.ts +32 -0
  118. package/src/workspace/lockfile.ts +33 -11
  119. package/src/workspace/migrate-scripts.ts +60 -7
  120. package/src/workspace/migration.ts +40 -1
  121. package/src/workspace/package-graph.ts +154 -16
  122. package/src/workspace/project-loader.ts +227 -77
  123. package/src/workspace/workspace.ts +173 -23
@@ -0,0 +1,155 @@
1
+ // `--exclude-dependencies` changes what RUNS, never what a key is (nx#35234).
2
+ // The graph module takes the dropped edges out of the schedule
3
+ // (`excludeDependencies`); this derives the key each dropped dependency
4
+ // would have, without running it, and hands it to the dependant as
5
+ // `excludedUpstream`, which every key site folds next to the live outcomes
6
+ // (`keyUpstream`). A dependant's key is then the one a full run derives.
7
+ //
8
+ // Folding the key makes the dependant's own entry say "built against that
9
+ // dependency at its current inputs", and nothing here proves the
10
+ // dependency's outputs on disk are the ones those inputs produce: it did
11
+ // not run. So a task whose key folds a dropped dependency, and everything
12
+ // built on it, runs and may hit but does not SAVE (`excludedTaint` seeds
13
+ // the run's taint): a save would file stale bytes under the key the next
14
+ // full run looks up.
15
+
16
+ import type { CacheLayer, GitFilesCache } from '../cache/index.js'
17
+ import { isGroupTask, type TaskNode, type TaskOutcome } from '../graph/index.js'
18
+ import { computeGroupHash, computeTaskHash, type HashCache } from './task-hash.js'
19
+ import { filterUpstreamHashes } from './upstream.js'
20
+
21
+ export interface KeyExcludedArgs {
22
+ /** The scheduled graph, after `excludeDependencies`. */
23
+ nodes: Map<string, TaskNode>
24
+ keyOnly: ReadonlyMap<string, TaskNode>
25
+ dropped: ReadonlyMap<string, readonly string[]>
26
+ cache: CacheLayer
27
+ workspaceRoot: string
28
+ workspaceFingerprint: string
29
+ forwardArgs?: readonly string[] | undefined
30
+ nestedDirsByProject: ReadonlyMap<string, string[]>
31
+ gitFilesCache: GitFilesCache
32
+ hashCache: HashCache
33
+ }
34
+
35
+ /**
36
+ * Set `excludedUpstream` on every scheduled task that lost an edge. Keys
37
+ * are derived over the whole graph as it stands at the start of the run,
38
+ * the way `deriveStableKeys` derives them: a dropped task's own
39
+ * dependencies, scheduled or not, fold theirs.
40
+ */
41
+ export async function keyExcludedDependencies(args: KeyExcludedArgs): Promise<void> {
42
+ const nodeOf = (id: string): TaskNode => args.nodes.get(id) ?? args.keyOnly.get(id)!
43
+ const fullDeps = (node: TaskNode): readonly string[] => [
44
+ ...node.deps,
45
+ ...(args.dropped.get(node.id) ?? []),
46
+ ]
47
+ // Each key's promise is made after its dependencies' promises, in a
48
+ // post-order walk on an explicit stack, so nothing recurses once per edge:
49
+ // a dropped chain is as deep as the graph, and the builder takes 50,000
50
+ // (item 737). The keys still resolve concurrently.
51
+ const keys = new Map<string, Promise<string | undefined>>()
52
+ const outcomes = (ids: readonly string[]): Promise<TaskOutcome[]> =>
53
+ Promise.all(ids.map(async (id) => synthetic(nodeOf(id), await keys.get(id)!)))
54
+ const derive = async (node: TaskNode): Promise<string | undefined> => {
55
+ const upstream = await outcomes(fullDeps(node))
56
+ if (isGroupTask(node)) return computeGroupHash(upstream)
57
+ // A persistent task has no key on the live path, so none here either.
58
+ if (node.config.exec?.persistent !== undefined) return undefined
59
+ return computeTaskHash({
60
+ node,
61
+ upstream,
62
+ workspaceRoot: args.workspaceRoot,
63
+ workspaceFingerprint: args.workspaceFingerprint,
64
+ cache: args.cache,
65
+ forwardArgs: args.forwardArgs,
66
+ nestedProjectDirs: args.nestedDirsByProject.get(node.projectName) ?? [],
67
+ gitFilesCache: args.gitFilesCache,
68
+ hashCache: args.hashCache,
69
+ })
70
+ }
71
+ const stack: Array<[id: string, depsMade: boolean]> = []
72
+ for (const lost of args.dropped.values()) for (const id of lost) stack.push([id, false])
73
+ while (stack.length > 0) {
74
+ const [id, depsMade] = stack.pop()!
75
+ if (keys.has(id)) continue
76
+ const node = nodeOf(id)
77
+ if (depsMade) {
78
+ keys.set(id, derive(node))
79
+ continue
80
+ }
81
+ stack.push([id, true])
82
+ for (const dep of fullDeps(node)) if (!keys.has(dep)) stack.push([dep, false])
83
+ }
84
+ for (const [id, lost] of args.dropped) {
85
+ args.nodes.get(id)!.excludedUpstream = await outcomes(lost)
86
+ }
87
+ }
88
+
89
+ /**
90
+ * The taint `--exclude-dependencies` seeds: every scheduled task whose key
91
+ * folds a dropped dependency's key, and how many cacheable tasks the taint
92
+ * reaches from there (the run's one line says so; a persistent task or one
93
+ * with no `cache` saves nothing anyway, so it is not counted, though the
94
+ * taint still passes through it).
95
+ */
96
+ export function excludedTaint(nodes: ReadonlyMap<string, TaskNode>): {
97
+ seeds: ReadonlySet<string>
98
+ unsaved: number
99
+ } {
100
+ const seeds = new Set<string>()
101
+ for (const node of nodes.values()) if (foldsExcludedKey(node)) seeds.add(node.id)
102
+ if (seeds.size === 0) return { seeds, unsaved: 0 }
103
+ // One walk over the dependant edges; re-scanning every node until nothing
104
+ // grew was a pass per hop, quadratic on a chain seeded at its bottom.
105
+ const dependants = new Map<string, string[]>()
106
+ for (const node of nodes.values()) {
107
+ for (const dep of node.deps) {
108
+ const list = dependants.get(dep)
109
+ if (list === undefined) dependants.set(dep, [node.id])
110
+ else list.push(node.id)
111
+ }
112
+ }
113
+ const reached = new Set(seeds)
114
+ const stack = [...seeds]
115
+ while (stack.length > 0) {
116
+ for (const up of dependants.get(stack.pop()!) ?? []) {
117
+ if (reached.has(up)) continue
118
+ reached.add(up)
119
+ stack.push(up)
120
+ }
121
+ }
122
+ let unsaved = 0
123
+ for (const id of reached) {
124
+ const cfg = nodes.get(id)!.config
125
+ if (cfg.cache !== undefined && cfg.exec?.persistent === undefined) unsaved++
126
+ }
127
+ return { seeds, unsaved }
128
+ }
129
+
130
+ /**
131
+ * Whether `node`'s key folds a dropped dependency's key: a group folds all
132
+ * of its dependencies, an exec task what its `cache.inputs.tasks` selects.
133
+ * A task with `tasks: []` folds none, so its entry claims nothing about a
134
+ * dependency and it saves as usual.
135
+ */
136
+ function foldsExcludedKey(node: TaskNode): boolean {
137
+ const excluded = node.excludedUpstream
138
+ if (excluded === undefined) return false
139
+ if (isGroupTask(node)) return true
140
+ return (
141
+ filterUpstreamHashes(excluded, node.config.cache?.inputs?.tasks, node.projectName, node.id)
142
+ .length > 0
143
+ )
144
+ }
145
+
146
+ /** A dependency as a key reads it: only `node` and `hash` matter. */
147
+ function synthetic(node: TaskNode, hash: string | undefined): TaskOutcome {
148
+ return {
149
+ node,
150
+ status: 'success',
151
+ exitCode: 0,
152
+ durationMs: 0,
153
+ ...(hash !== undefined ? { hash } : {}),
154
+ }
155
+ }