@vzn/vx 0.0.180 → 0.0.182
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.182",
|
|
4
4
|
"description": "A fast, correct task runner and build cache for JavaScript monorepos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"node": ">=18"
|
|
18
18
|
},
|
|
19
19
|
"optionalDependencies": {
|
|
20
|
-
"@vzn/vx-linux-x64": "0.0.
|
|
21
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
22
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
23
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
20
|
+
"@vzn/vx-linux-x64": "0.0.182",
|
|
21
|
+
"@vzn/vx-linux-arm64": "0.0.182",
|
|
22
|
+
"@vzn/vx-darwin-x64": "0.0.182",
|
|
23
|
+
"@vzn/vx-darwin-arm64": "0.0.182"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
|
@@ -132,12 +132,12 @@ interface Pending {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
|
-
* How long a single config evaluation may take
|
|
136
|
-
*
|
|
137
|
-
* worker cannot stall a long-lived process indefinitely. Read
|
|
138
|
-
* test can drive the deadline instead of waiting it out.
|
|
135
|
+
* How long a single config evaluation may take, in the worker or in process
|
|
136
|
+
* (D-66). Real evaluations are ~10 ms; this exists only so a hung config or a
|
|
137
|
+
* killed worker cannot stall a run or a long-lived process indefinitely. Read
|
|
138
|
+
* per call so a test can drive the deadline instead of waiting it out.
|
|
139
139
|
*/
|
|
140
|
-
function
|
|
140
|
+
export function evalBudgetMs(): number {
|
|
141
141
|
const raw = process.env['VX_CONFIG_WORKER_TIMEOUT_MS']
|
|
142
142
|
// Same kind of value as the teardown deadline, so the same treatment: a
|
|
143
143
|
// BOUND on a worker that may be wedged, with no "no limit" reading. Falls
|
|
@@ -266,7 +266,7 @@ export async function evaluateConfigFresh(configPath: string): Promise<unknown>
|
|
|
266
266
|
// otherwise take milliseconds. Nothing else bounds it: there is no
|
|
267
267
|
// run-level timeout. The budget is enormous next to the ~10 ms a real
|
|
268
268
|
// evaluation costs, so it can only fire on a genuine wedge.
|
|
269
|
-
const budget =
|
|
269
|
+
const budget = evalBudgetMs()
|
|
270
270
|
const reply = await new Promise<WorkerReply>((resolve, reject) => {
|
|
271
271
|
pending.set(id, { resolve, reject })
|
|
272
272
|
timer = setTimeout(() => {
|
|
@@ -74,7 +74,14 @@ export function nonJsonPaths(value: unknown): NonJsonValue[] {
|
|
|
74
74
|
ancestors.push(v)
|
|
75
75
|
for (const key of Object.keys(v)) {
|
|
76
76
|
keys.push(key)
|
|
77
|
-
|
|
77
|
+
// Read by descriptor: a getter is code, and each read may answer
|
|
78
|
+
// differently. `vx show` read one three times, so the key, the
|
|
79
|
+
// check and the command could each see another value, and a
|
|
80
|
+
// throwing one surfaced as vx's own stack (D-67). Never called here.
|
|
81
|
+
const d = Object.getOwnPropertyDescriptor(v, key)
|
|
82
|
+
if (d !== undefined && d.get !== undefined) found('a getter')
|
|
83
|
+
else if (d !== undefined && d.set !== undefined) found('a setter')
|
|
84
|
+
else walk(d?.value, false)
|
|
78
85
|
keys.pop()
|
|
79
86
|
}
|
|
80
87
|
ancestors.pop()
|
|
@@ -3,7 +3,7 @@ import path from 'node:path'
|
|
|
3
3
|
import type { ProjectConfig, WorkspaceConfig } from '../config.js'
|
|
4
4
|
import { UserError, xxh3hex } from '../util/index.js'
|
|
5
5
|
import { validateProjectConfig, validateWorkspace } from './config-schema.js'
|
|
6
|
-
import { beginEvalRound, CONFIG_EXIT, evaluateConfigFresh } from './config-eval.js'
|
|
6
|
+
import { beginEvalRound, CONFIG_EXIT, evalBudgetMs, evaluateConfigFresh } from './config-eval.js'
|
|
7
7
|
import { hasEsmExport, unprovidedBareImports } from './config-imports.js'
|
|
8
8
|
import {
|
|
9
9
|
configEvalKey,
|
|
@@ -137,8 +137,26 @@ async function loadDefaultExport(
|
|
|
137
137
|
}
|
|
138
138
|
let ns: { default?: unknown }
|
|
139
139
|
const unguard = guardExit()
|
|
140
|
+
let deadline: ReturnType<typeof setTimeout> | undefined
|
|
140
141
|
try {
|
|
141
|
-
|
|
142
|
+
// The worker's budget, here too: a top-level await that never settled
|
|
143
|
+
// while a timer kept the loop alive hung `vx run` for good, silently
|
|
144
|
+
// (D-66).
|
|
145
|
+
const budget = evalBudgetMs()
|
|
146
|
+
ns = (await Promise.race([
|
|
147
|
+
import(specifier),
|
|
148
|
+
new Promise<never>((_, reject) => {
|
|
149
|
+
deadline = setTimeout(
|
|
150
|
+
() =>
|
|
151
|
+
reject(
|
|
152
|
+
new UserError(
|
|
153
|
+
`${kind} config ${configPath} did not finish evaluating within ${budget}ms (VX_CONFIG_WORKER_TIMEOUT_MS)`,
|
|
154
|
+
),
|
|
155
|
+
),
|
|
156
|
+
budget,
|
|
157
|
+
)
|
|
158
|
+
}),
|
|
159
|
+
])) as { default?: unknown }
|
|
142
160
|
} catch (err) {
|
|
143
161
|
// A served module's frames name its specifier; the user wrote the path.
|
|
144
162
|
if (err instanceof Error && err.stack !== undefined) {
|
|
@@ -146,6 +164,7 @@ async function loadDefaultExport(
|
|
|
146
164
|
}
|
|
147
165
|
throw configLoadError(err, configPath, kind) ?? err
|
|
148
166
|
} finally {
|
|
167
|
+
clearTimeout(deadline)
|
|
149
168
|
unguard()
|
|
150
169
|
if (source !== null) releaseSource(hash)
|
|
151
170
|
}
|