@vzn/vx 0.0.178 → 0.0.180

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.178",
3
+ "version": "0.0.180",
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.178",
21
- "@vzn/vx-linux-arm64": "0.0.178",
22
- "@vzn/vx-darwin-x64": "0.0.178",
23
- "@vzn/vx-darwin-arm64": "0.0.178"
20
+ "@vzn/vx-linux-x64": "0.0.180",
21
+ "@vzn/vx-linux-arm64": "0.0.180",
22
+ "@vzn/vx-darwin-x64": "0.0.180",
23
+ "@vzn/vx-darwin-arm64": "0.0.180"
24
24
  },
25
25
  "dependencies": {
26
26
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
@@ -39,9 +39,38 @@ import path from 'node:path'
39
39
  import { MAX_TIMEOUT_MS, UserError } from '../util/index.js'
40
40
  import { nonJsonMessage, nonJsonPaths, type NonJsonValue } from './json-data.js'
41
41
 
42
+ /** Why a config's `process.exit` throws, on both load paths (D-65). */
43
+ export const CONFIG_EXIT = 'a config exports its object; it cannot end the run'
44
+
42
45
  const WORKER_SRC = `
43
46
  const nonJsonPaths = ${nonJsonPaths.toString()}
47
+ // The parent's stdout is a verb's JSON or vx mcp's JSON-RPC stream, and a
48
+ // config printing on a repeat load wrote into it: the server's own redirect
49
+ // covers the parent thread only (D-64). This worker only evaluates configs,
50
+ // so every route to fd 1 is stderr's, Bun's own included (item 1069).
51
+ const quiet = (async () => {
52
+ const P = globalThis.process
53
+ const B = globalThis.Bun
54
+ const { Console } = await import('node:console')
55
+ globalThis.console = Object.assign(new Console(P.stderr, P.stderr), {
56
+ write: (...data) => {
57
+ const text = data.join('')
58
+ P.stderr.write(text)
59
+ return text.length
60
+ },
61
+ })
62
+ P.stdout.write = (...args) => P.stderr.write(...args)
63
+ const ownBunWrite = B.write
64
+ B.write = (dest, ...rest) => ownBunWrite(dest === B.stdout ? B.stderr : dest, ...rest)
65
+ B.stdout.writer = (...args) => B.stderr.writer(...args)
66
+ // An exit here ended the worker unheard, and the load waited out its
67
+ // whole deadline to say the worker did not answer (D-65).
68
+ P.exit = (code) => {
69
+ throw new Error('process.exit(' + (code ?? '') + ') in a config: ' + ${JSON.stringify(CONFIG_EXIT)})
70
+ }
71
+ })()
44
72
  self.onmessage = async (e) => {
73
+ await quiet
45
74
  const { id, path, env } = e.data
46
75
  // A Worker starts with the process's STARTUP environment, not the
47
76
  // parent's process.env as written since (probed): a config reading an
@@ -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, evaluateConfigFresh } from './config-eval.js'
6
+ import { beginEvalRound, CONFIG_EXIT, evaluateConfigFresh } from './config-eval.js'
7
7
  import { hasEsmExport, unprovidedBareImports } from './config-imports.js'
8
8
  import {
9
9
  configEvalKey,
@@ -136,6 +136,7 @@ async function loadDefaultExport(
136
136
  holdSource(hash, source)
137
137
  }
138
138
  let ns: { default?: unknown }
139
+ const unguard = guardExit()
139
140
  try {
140
141
  ns = (await import(specifier)) as { default?: unknown }
141
142
  } catch (err) {
@@ -145,6 +146,7 @@ async function loadDefaultExport(
145
146
  }
146
147
  throw configLoadError(err, configPath, kind) ?? err
147
148
  } finally {
149
+ unguard()
148
150
  if (source !== null) releaseSource(hash)
149
151
  }
150
152
  const mod = ns?.default
@@ -152,6 +154,28 @@ async function loadDefaultExport(
152
154
  return mod
153
155
  }
154
156
 
157
+ /**
158
+ * A config's `process.exit` ended vx mid-load: `exit(0)` was a green run
159
+ * that ran nothing and printed nothing (D-65). While any config evaluates
160
+ * in process it throws instead, at the config's line. vx's own exit is
161
+ * signal forwarding's, which a run installs after its load and removes
162
+ * when it ends.
163
+ */
164
+ let evaluating = 0
165
+ let ownExit: typeof process.exit = process.exit
166
+ function guardExit(): () => void {
167
+ if (evaluating++ === 0) {
168
+ ownExit = process.exit
169
+ process.exit = configExit
170
+ }
171
+ return () => {
172
+ if (--evaluating === 0) process.exit = ownExit
173
+ }
174
+ }
175
+ const configExit = ((code?: number | string | null) => {
176
+ throw new Error(`process.exit(${code ?? ''}) in a config: ${CONFIG_EXIT}`)
177
+ }) as typeof process.exit
178
+
155
179
  /**
156
180
  * A bare import nothing above the config provides is refused BEFORE the
157
181
  * evaluation: left to Bun, a workspace with no `node_modules` would have