@vzn/vx 0.0.427 → 0.0.429
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/init.ts +3 -0
- package/src/workspace/config-eval.ts +16 -15
- package/src/workspace/project-loader.ts +18 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.429",
|
|
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.429",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.429",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.429",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.429"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cli/init.ts
CHANGED
|
@@ -374,6 +374,9 @@ function namelessNotes(root: string, dirs: readonly string[]): string[] {
|
|
|
374
374
|
})
|
|
375
375
|
.map((dir) => path.relative(root, dir).split(path.sep).join('/'))
|
|
376
376
|
.sort()
|
|
377
|
+
// The root's own manifest is the empty relative path: a nameless
|
|
378
|
+
// single-package repo read `not mapped: — its package.json…` (M-54).
|
|
379
|
+
.map((rel) => (rel === '' ? 'the root' : rel))
|
|
377
380
|
if (withScripts.length === 0) return []
|
|
378
381
|
const shown =
|
|
379
382
|
withScripts.slice(0, 3).join(', ') +
|
|
@@ -128,8 +128,12 @@ self.onmessage = async (e) => {
|
|
|
128
128
|
const envBefore = watched ? { ...live } : null
|
|
129
129
|
const cwdBefore = watched ? globalThis.process.cwd() : null
|
|
130
130
|
// The umask is the process's, not the worker's: a config's umask(0o777)
|
|
131
|
-
// here left every file vx wrote after it 000 (D-125).
|
|
132
|
-
|
|
131
|
+
// here left every file vx wrote after it 000 (D-125). Read only when
|
|
132
|
+
// blaming, the one evaluation in flight: Bun reads it by setting 0 and
|
|
133
|
+
// back, so this read beside the main thread's left the process at 0
|
|
134
|
+
// and blamed an innocent config. An ordinary load's change is the
|
|
135
|
+
// loader's to see, on the main thread, once the round is done.
|
|
136
|
+
const umaskIn = watched ? globalThis.process.umask() : null
|
|
133
137
|
const same = (a, b) =>
|
|
134
138
|
a !== undefined && b !== undefined && Object.is(a.value, b.value) && a.get === b.get && a.set === b.set &&
|
|
135
139
|
a.writable === b.writable && a.enumerable === b.enumerable && a.configurable === b.configurable
|
|
@@ -148,9 +152,7 @@ self.onmessage = async (e) => {
|
|
|
148
152
|
return out
|
|
149
153
|
}
|
|
150
154
|
const umaskBack = () => {
|
|
151
|
-
if (globalThis.process.umask()
|
|
152
|
-
globalThis.process.umask(umaskIn)
|
|
153
|
-
return true
|
|
155
|
+
if (umaskIn !== null && globalThis.process.umask() !== umaskIn) globalThis.process.umask(umaskIn)
|
|
154
156
|
}
|
|
155
157
|
try {
|
|
156
158
|
const ns = await import(path)
|
|
@@ -160,24 +162,29 @@ self.onmessage = async (e) => {
|
|
|
160
162
|
const mod = await ns?.default
|
|
161
163
|
const isObject = mod !== null && typeof mod === 'object'
|
|
162
164
|
const nonJson = isObject ? nonJsonPaths(mod) : []
|
|
165
|
+
// Seen, then put back, BEFORE the reply: the caller terminates a
|
|
166
|
+
// blaming worker as soon as it answers, and a restore after the reply
|
|
167
|
+
// lost that race and left the process at the config's mask.
|
|
168
|
+
const report = changed()
|
|
169
|
+
umaskBack()
|
|
163
170
|
postMessage({
|
|
164
171
|
id,
|
|
165
172
|
ok: true,
|
|
166
173
|
nonJson,
|
|
167
174
|
json: isObject && nonJson.length === 0 ? JSON.stringify(mod) : null,
|
|
168
175
|
fn: typeof mod === 'function',
|
|
169
|
-
changed:
|
|
170
|
-
umask: umaskBack(),
|
|
176
|
+
changed: report,
|
|
171
177
|
})
|
|
172
178
|
} catch (err) {
|
|
179
|
+
const report = changed()
|
|
180
|
+
umaskBack()
|
|
173
181
|
postMessage({
|
|
174
182
|
id,
|
|
175
183
|
ok: false,
|
|
176
184
|
name: err?.name ?? 'Error',
|
|
177
185
|
message: err?.message ?? String(err),
|
|
178
186
|
stack: err?.stack ?? null,
|
|
179
|
-
changed:
|
|
180
|
-
umask: umaskBack(),
|
|
187
|
+
changed: report,
|
|
181
188
|
position:
|
|
182
189
|
err?.position && typeof err.position === 'object'
|
|
183
190
|
? { file: err.position.file, line: err.position.line, column: err.position.column }
|
|
@@ -189,9 +196,6 @@ self.onmessage = async (e) => {
|
|
|
189
196
|
|
|
190
197
|
const FUNCTION_EXPORT = (): void => {}
|
|
191
198
|
|
|
192
|
-
/** A repeat load moved the process's umask; the loader names the config (D-125). */
|
|
193
|
-
export class UmaskChanged extends Error {}
|
|
194
|
-
|
|
195
199
|
const WORKER_URL = `data:text/javascript,${encodeURIComponent(WORKER_SRC)}`
|
|
196
200
|
|
|
197
201
|
interface WorkerReply {
|
|
@@ -200,8 +204,6 @@ interface WorkerReply {
|
|
|
200
204
|
json: string | null
|
|
201
205
|
/** The default export is a function, which JSON cannot carry back. */
|
|
202
206
|
fn?: boolean
|
|
203
|
-
/** The evaluation moved the process's umask; the worker put it back. */
|
|
204
|
-
umask?: boolean
|
|
205
207
|
nonJson: NonJsonValue[]
|
|
206
208
|
name: string
|
|
207
209
|
message: string
|
|
@@ -363,7 +365,6 @@ export async function evaluateConfigFresh(configPath: string): Promise<unknown>
|
|
|
363
365
|
timer.unref?.()
|
|
364
366
|
w.postMessage({ id, path: abs, env: { ...process.env } })
|
|
365
367
|
})
|
|
366
|
-
if (reply.umask === true) throw new UmaskChanged()
|
|
367
368
|
const [nonJson] = reply.nonJson
|
|
368
369
|
if (nonJson !== undefined) throw new UserError(nonJsonMessage(configPath, nonJson))
|
|
369
370
|
// A function stands in for the one the config exported, so the caller's
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
CONFIG_EXIT,
|
|
10
10
|
evalBudgetMs,
|
|
11
11
|
evaluateConfigFresh,
|
|
12
|
-
UmaskChanged,
|
|
13
12
|
WATCHED_BUILTIN_NAMES,
|
|
14
13
|
} from './config-eval.js'
|
|
15
14
|
import { hasEsmExport, unprovidedBareImports } from './config-imports.js'
|
|
@@ -496,7 +495,6 @@ export async function loadProjectConfigs(
|
|
|
496
495
|
if (repeat) refuseUnprovidedImports(bytes!, configPath, 'Project')
|
|
497
496
|
const mod = repeat
|
|
498
497
|
? await evaluateConfigFresh(configPath).catch((err: unknown) => {
|
|
499
|
-
if (err instanceof UmaskChanged) throw builtinsChanged(['process.umask'], configPath)
|
|
500
498
|
throw configLoadError(err, configPath, 'Project') ?? err
|
|
501
499
|
})
|
|
502
500
|
: await loadDefaultExport(configPath, 'Project', bytes!)
|
|
@@ -508,7 +506,10 @@ export async function loadProjectConfigs(
|
|
|
508
506
|
...restoreBuiltins(builtins, overlapping),
|
|
509
507
|
...restoreEnv(env),
|
|
510
508
|
...restoreCwd(cwd),
|
|
511
|
-
|
|
509
|
+
// Alone in the round: nothing else reads the umask now. Beside
|
|
510
|
+
// other loads the round reads it once they are done (see
|
|
511
|
+
// `currentUmask`).
|
|
512
|
+
...(overlapping ? [] : restoreUmask(umask)),
|
|
512
513
|
]
|
|
513
514
|
// Loads overlap, so another config's change can surface after this
|
|
514
515
|
// one: named here, the refusal blamed the wrong file (D-119). The round
|
|
@@ -604,12 +605,22 @@ export async function loadProjectConfigs(
|
|
|
604
605
|
for (const i of misses) {
|
|
605
606
|
const configPath = prepared[i]!.configPath
|
|
606
607
|
const own = await builtinsChangedBy(configPath)
|
|
608
|
+
// The blaming worker is gone, so this thread reads alone; and a
|
|
609
|
+
// worker that outlived its budget was ended before it could put
|
|
610
|
+
// the umask back.
|
|
611
|
+
restoreUmask(umask)
|
|
607
612
|
if (own.length > 0) throw builtinsChanged(own, configPath)
|
|
608
613
|
}
|
|
609
614
|
throw builtinsChanged(changedInRound)
|
|
610
615
|
}
|
|
611
616
|
if (first !== undefined) throw first.failed
|
|
612
|
-
|
|
617
|
+
// One load in the round, in the worker (a repeat load), is the one
|
|
618
|
+
// that moved it: the worker no longer reads the umask to say so.
|
|
619
|
+
if (changed.length > 0)
|
|
620
|
+
throw builtinsChanged(
|
|
621
|
+
changed,
|
|
622
|
+
misses.length === 1 ? prepared[misses[0]!]!.configPath : undefined,
|
|
623
|
+
)
|
|
613
624
|
return results.map((r) => (r as Loaded).config)
|
|
614
625
|
} finally {
|
|
615
626
|
endRound()
|
|
@@ -906,8 +917,9 @@ function currentUmask(): number {
|
|
|
906
917
|
// Through globalThis, as restoreCwd: the playground's shim has no umask.
|
|
907
918
|
const proc = globalThis.process as { umask?: (mask?: number) => number }
|
|
908
919
|
if (typeof proc.umask !== 'function') return -1
|
|
909
|
-
//
|
|
910
|
-
//
|
|
920
|
+
// Bun reads it by setting 0 and putting it back (probed on 1.4.2: four
|
|
921
|
+
// workers reading at once left the process at 0), so it is read on this
|
|
922
|
+
// thread only, and never while the config worker evaluates.
|
|
911
923
|
return proc.umask()
|
|
912
924
|
}
|
|
913
925
|
|