@vzn/vx 0.0.622 → 0.0.623
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 +5 -5
- package/src/cli/why.ts +60 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.623",
|
|
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.623",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.623",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.623",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.623"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cli/why.ts
CHANGED
|
@@ -9,7 +9,9 @@ import path from 'node:path'
|
|
|
9
9
|
import { Cache } from '../cache/index.js'
|
|
10
10
|
import { flagHint, formatValue, seeHelp } from './help.js'
|
|
11
11
|
import { splitTaskId } from '../graph/index.js'
|
|
12
|
+
import type { Database } from 'bun:sqlite'
|
|
12
13
|
import {
|
|
14
|
+
type CacheKeyDiff,
|
|
13
15
|
cacheKeyDiff,
|
|
14
16
|
explainCacheKeyQuery as explainCacheKey,
|
|
15
17
|
latestRunId,
|
|
@@ -232,9 +234,13 @@ export async function whyCmd(args: readonly string[]): Promise<number> {
|
|
|
232
234
|
)
|
|
233
235
|
}
|
|
234
236
|
const diff = cacheKeyDiff(db, runId, taskId)
|
|
237
|
+
// An upstream change is never the cause, only its carrier: follow
|
|
238
|
+
// each moved dependency in this run down to the tasks whose own
|
|
239
|
+
// inputs moved, so one `vx why` names the file, not the next task.
|
|
240
|
+
const roots = rootCauses(db, runId, taskId, diff.entries)
|
|
235
241
|
|
|
236
242
|
if (parsed.format === 'json') {
|
|
237
|
-
process.stdout.write(`${JSON.stringify({ taskId, runId, why, diff })}\n`)
|
|
243
|
+
process.stdout.write(`${JSON.stringify({ taskId, runId, why, diff, roots })}\n`)
|
|
238
244
|
return 0
|
|
239
245
|
}
|
|
240
246
|
|
|
@@ -293,7 +299,22 @@ export async function whyCmd(args: readonly string[]): Promise<number> {
|
|
|
293
299
|
` ${e.change.padEnd(7)} ${e.kind.padEnd(kindW)} ${printable(e.name)} ${beforeAfter}`.trimEnd(),
|
|
294
300
|
)
|
|
295
301
|
}
|
|
296
|
-
|
|
302
|
+
if (roots.length > 0) {
|
|
303
|
+
lines.push('', ' root cause:')
|
|
304
|
+
for (const r of roots.slice(0, ROOTS_SHOWN)) {
|
|
305
|
+
const what = r.entries.map((e) =>
|
|
306
|
+
e.name === e.kind ? e.kind : `${e.kind} ${printable(e.name)}`,
|
|
307
|
+
)
|
|
308
|
+
lines.push(
|
|
309
|
+
` ${r.chain.join(' \u2190 ')} \u2190 ${what.slice(0, 3).join(', ')}${what.length > 3 ? ` and ${what.length - 3} more` : ''}`,
|
|
310
|
+
)
|
|
311
|
+
}
|
|
312
|
+
if (roots.length > ROOTS_SHOWN) lines.push(` and ${roots.length - ROOTS_SHOWN} more`)
|
|
313
|
+
}
|
|
314
|
+
const rootKinds = roots.flatMap((r) => r.entries.map((e) => e.kind))
|
|
315
|
+
const kinds = [...new Set([...diff.entries.map((e) => e.kind), ...rootKinds])].filter(
|
|
316
|
+
(k) => k in WHAT_TO_DO && !(k === 'upstream' && roots.length > 0),
|
|
317
|
+
)
|
|
297
318
|
if (kinds.length > 0) {
|
|
298
319
|
lines.push('', ' what to do:')
|
|
299
320
|
// An upstream row names the task to ask next: the command, not a
|
|
@@ -321,3 +342,40 @@ export async function whyCmd(args: readonly string[]): Promise<number> {
|
|
|
321
342
|
cache.close()
|
|
322
343
|
}
|
|
323
344
|
}
|
|
345
|
+
|
|
346
|
+
const ROOTS_SHOWN = 5
|
|
347
|
+
|
|
348
|
+
interface RootCause {
|
|
349
|
+
/** From the asked task down to the one whose own inputs moved. */
|
|
350
|
+
chain: string[]
|
|
351
|
+
entries: CacheKeyDiff['entries']
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* The tasks under `taskId`'s moved upstreams whose OWN key components
|
|
356
|
+
* moved in the same run, each with the chain that carried it up. A task
|
|
357
|
+
* visited once is not walked again (a diamond names its root once).
|
|
358
|
+
*/
|
|
359
|
+
function rootCauses(
|
|
360
|
+
db: Database,
|
|
361
|
+
runId: string,
|
|
362
|
+
taskId: string,
|
|
363
|
+
entries: CacheKeyDiff['entries'],
|
|
364
|
+
): RootCause[] {
|
|
365
|
+
const roots: RootCause[] = []
|
|
366
|
+
const seen = new Set([taskId])
|
|
367
|
+
const walk = (chain: string[], moved: CacheKeyDiff['entries']): void => {
|
|
368
|
+
for (const e of moved) {
|
|
369
|
+
if (e.kind !== 'upstream' || seen.has(e.name)) continue
|
|
370
|
+
seen.add(e.name)
|
|
371
|
+
const next = [...chain, e.name]
|
|
372
|
+
const d = cacheKeyDiff(db, runId, e.name)
|
|
373
|
+
if (!d.found) continue
|
|
374
|
+
const own = d.entries.filter((x) => x.kind !== 'upstream')
|
|
375
|
+
if (own.length > 0) roots.push({ chain: next, entries: own })
|
|
376
|
+
walk(next, d.entries)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
walk([taskId], entries)
|
|
380
|
+
return roots
|
|
381
|
+
}
|