@vzn/vx 0.0.390 → 0.0.392
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/cache/run-history.ts +45 -22
- package/src/index.ts +1 -1
- package/src/orchestrator/index.ts +1 -0
- package/src/orchestrator/plugin.ts +27 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.392",
|
|
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.392",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.392",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.392",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.392"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cache/run-history.ts
CHANGED
|
@@ -7,6 +7,30 @@ import type { Database, SQLQueryBindings } from 'bun:sqlite'
|
|
|
7
7
|
import { lazyStatement } from './schema.js'
|
|
8
8
|
import type { InvocationRecord, RunRecord } from './layer.js'
|
|
9
9
|
|
|
10
|
+
const INSERT_RUNS = `
|
|
11
|
+
INSERT INTO runs(
|
|
12
|
+
hash, project, task, status, exit_code, duration_ms, forward_args,
|
|
13
|
+
started_at, ended_at,
|
|
14
|
+
run_id, cpu_ms, peak_rss_bytes, wallclock_start_ns, wallclock_end_ns,
|
|
15
|
+
cache_hit, attempts, cached,
|
|
16
|
+
blocked_by, timed_out, sandbox_violations, not_ready
|
|
17
|
+
)
|
|
18
|
+
VALUES `
|
|
19
|
+
/** One `runs` row's placeholders: the 21 columns `bindRun` fills. */
|
|
20
|
+
const RUN_TUPLE = `(${Array(21).fill('?').join(', ')})`
|
|
21
|
+
|
|
22
|
+
/** An INSERT of `n` `runs` rows. */
|
|
23
|
+
function insertRunsSql(n: number): string {
|
|
24
|
+
return INSERT_RUNS + Array(n).fill(RUN_TUPLE).join(', ')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Rows per INSERT: 40 × 21 variables stays under 999, the oldest default
|
|
29
|
+
* ceiling. A statement per row was 8.7 ms of 1,000 rows, 40 at a time 6.4
|
|
30
|
+
* (2026-10-03).
|
|
31
|
+
*/
|
|
32
|
+
const RUNS_PER_INSERT = 40
|
|
33
|
+
|
|
10
34
|
export class RunHistory {
|
|
11
35
|
private readonly insertRun: ReturnType<Database['prepare']>
|
|
12
36
|
private readonly insertInvocation: ReturnType<Database['prepare']>
|
|
@@ -16,19 +40,7 @@ export class RunHistory {
|
|
|
16
40
|
/** The store's salted value digest (L-4). */
|
|
17
41
|
private readonly digestValue: (value: string) => string,
|
|
18
42
|
) {
|
|
19
|
-
this.insertRun = lazyStatement(
|
|
20
|
-
this.db,
|
|
21
|
-
`
|
|
22
|
-
INSERT INTO runs(
|
|
23
|
-
hash, project, task, status, exit_code, duration_ms, forward_args,
|
|
24
|
-
started_at, ended_at,
|
|
25
|
-
run_id, cpu_ms, peak_rss_bytes, wallclock_start_ns, wallclock_end_ns,
|
|
26
|
-
cache_hit, attempts, cached,
|
|
27
|
-
blocked_by, timed_out, sandbox_violations, not_ready
|
|
28
|
-
)
|
|
29
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
30
|
-
`,
|
|
31
|
-
)
|
|
43
|
+
this.insertRun = lazyStatement(this.db, insertRunsSql(1))
|
|
32
44
|
this.insertInvocation = lazyStatement(
|
|
33
45
|
this.db,
|
|
34
46
|
`
|
|
@@ -59,11 +71,24 @@ export class RunHistory {
|
|
|
59
71
|
// `bun:sqlite`'s `transaction()` returns a callable that wraps the
|
|
60
72
|
// body in BEGIN/COMMIT, fsyncing once at the end. For a 200-task
|
|
61
73
|
// run that's one fsync instead of 200.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
this.db.transaction(() => this.insertRuns(runs))()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** `runs` as few INSERTs; each distinct forward-args list digested once. */
|
|
78
|
+
private insertRuns(runs: readonly RunRecord[]): void {
|
|
79
|
+
const digests = new Map<string, string>()
|
|
80
|
+
const digest = (value: string): string => {
|
|
81
|
+
let d = digests.get(value)
|
|
82
|
+
if (d === undefined) digests.set(value, (d = this.digestValue(value)))
|
|
83
|
+
return d
|
|
84
|
+
}
|
|
85
|
+
for (let i = 0; i < runs.length; i += RUNS_PER_INSERT) {
|
|
86
|
+
const block = runs.slice(i, i + RUNS_PER_INSERT)
|
|
87
|
+
const params: SQLQueryBindings[] = []
|
|
88
|
+
for (const r of block) params.push(...bindRun(r, digest))
|
|
89
|
+
// `query` caches by text: one statement for every full block.
|
|
90
|
+
this.db.query(insertRunsSql(block.length)).run(...params)
|
|
91
|
+
}
|
|
67
92
|
}
|
|
68
93
|
|
|
69
94
|
recordRunBundle(bundle: { runs: readonly RunRecord[]; invocation: InvocationRecord }): void {
|
|
@@ -73,11 +98,9 @@ export class RunHistory {
|
|
|
73
98
|
// the entry-save transaction (`save`/`ingest`) so a warm
|
|
74
99
|
// all-cache-hit run — which writes no `runs`-vs-`entry_inputs`
|
|
75
100
|
// mismatch — pays nothing for the moat it isn't refreshing.
|
|
76
|
-
const insertRun = this.insertRun
|
|
77
|
-
const insertInvocation = this.insertInvocation
|
|
78
101
|
this.db.transaction(() => {
|
|
79
|
-
|
|
80
|
-
insertInvocation.run(...bindInvocation(bundle.invocation))
|
|
102
|
+
this.insertRuns(bundle.runs)
|
|
103
|
+
this.insertInvocation.run(...bindInvocation(bundle.invocation))
|
|
81
104
|
})()
|
|
82
105
|
}
|
|
83
106
|
|
package/src/index.ts
CHANGED
|
@@ -51,7 +51,7 @@ export type {
|
|
|
51
51
|
SandboxDenials,
|
|
52
52
|
} from './config.js'
|
|
53
53
|
export { defineProject, defineWorkspace } from './config.js'
|
|
54
|
-
export { definePlugin } from './orchestrator/index.js'
|
|
54
|
+
export { definePlugin, refuseUnknownOptions } from './orchestrator/index.js'
|
|
55
55
|
|
|
56
56
|
// Programmatic engine API: run / plan / prepare (docs/cli.md § Programmatic
|
|
57
57
|
// API). The graph primitives, the cache-key hashing seam and the git / host
|
|
@@ -15,7 +15,7 @@ import type { Cache, CacheLayer, CachePolicy } from '../cache/index.js'
|
|
|
15
15
|
import { PLUGIN_PACKAGE, type ProjectConfig, type WorkspaceConfig } from '../config.js'
|
|
16
16
|
import type { TaskExecutor } from '../exec/index.js'
|
|
17
17
|
import type { TaskNode, TaskOutcome } from '../graph/index.js'
|
|
18
|
-
import { UserError } from '../util/index.js'
|
|
18
|
+
import { nearest, UserError } from '../util/index.js'
|
|
19
19
|
import type { ProjectMeta } from '../workspace/index.js'
|
|
20
20
|
import type { EventBus, RunStartInfo } from './events.js'
|
|
21
21
|
import type { TelemetryContext, TelemetrySink } from './telemetry.js'
|
|
@@ -483,6 +483,32 @@ export function definePlugin(origin: PluginOrigin, hooks: PluginHooks): VxPlugin
|
|
|
483
483
|
return { ...hooks, name, [PLUGIN_PACKAGE]: name } as VxPlugin
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
+
/**
|
|
487
|
+
* Refuse an option a plugin factory does not take, as core refuses an
|
|
488
|
+
* unknown config field. Bun strips a config's types, so a misspelt option
|
|
489
|
+
* (`reapi({ endpont })`) reached the factory, which read it as unset and
|
|
490
|
+
* quietly declined: the run went local with no word. `factory` names the
|
|
491
|
+
* call in the message (`reapi()`), `known` is the options the factory reads.
|
|
492
|
+
*/
|
|
493
|
+
export function refuseUnknownOptions(
|
|
494
|
+
factory: string,
|
|
495
|
+
options: unknown,
|
|
496
|
+
known: readonly string[],
|
|
497
|
+
): void {
|
|
498
|
+
if (options === undefined) return
|
|
499
|
+
if (options === null || typeof options !== 'object' || Array.isArray(options)) {
|
|
500
|
+
throw new UserError(`${factory}: options must be an object`)
|
|
501
|
+
}
|
|
502
|
+
for (const key of Object.keys(options)) {
|
|
503
|
+
if (known.includes(key)) continue
|
|
504
|
+
const near = nearest(key, known)
|
|
505
|
+
throw new UserError(
|
|
506
|
+
`${factory} has unknown option "${key}" (allowed: ${[...known].sort().join(', ')})` +
|
|
507
|
+
(near === undefined ? '' : ` — did you mean ${near}?`),
|
|
508
|
+
)
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
486
512
|
export interface InstallPluginsArgs {
|
|
487
513
|
plugins: readonly Plugin[]
|
|
488
514
|
workspaceRoot: string
|