@vzn/vx 0.0.105 → 0.0.107
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/watch.ts +11 -12
- package/src/exec/kill-tree.ts +22 -0
- package/src/workspace/config-imports.ts +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.107",
|
|
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.107",
|
|
21
|
+
"@vzn/vx-linux-arm64": "0.0.107",
|
|
22
|
+
"@vzn/vx-darwin-x64": "0.0.107",
|
|
23
|
+
"@vzn/vx-darwin-arm64": "0.0.107"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cli/watch.ts
CHANGED
|
@@ -54,6 +54,11 @@ import {
|
|
|
54
54
|
} from './watch-fs.js'
|
|
55
55
|
import { memberEntries, sameMembers, sweepConfigs, watchedProjects } from './watch-set.js'
|
|
56
56
|
|
|
57
|
+
/** One line for a watcher or re-read the OS refused; the loop goes on without it. */
|
|
58
|
+
function sayCannot(what: string, err: unknown): void {
|
|
59
|
+
process.stderr.write(`vx watch: ${what}: ${err instanceof Error ? err.message : String(err)}\n`)
|
|
60
|
+
}
|
|
61
|
+
|
|
57
62
|
/** Wait this long after the last filesystem event before re-running. */
|
|
58
63
|
const DEBOUNCE_MS = 150
|
|
59
64
|
|
|
@@ -643,8 +648,7 @@ async function runWatchLoop(args: WatchLoopArgs): Promise<number> {
|
|
|
643
648
|
})
|
|
644
649
|
perProject.set(proj.dir, handle)
|
|
645
650
|
} catch (err) {
|
|
646
|
-
|
|
647
|
-
process.stderr.write(`vx watch: cannot watch ${proj.dir}: ${msg}\n`)
|
|
651
|
+
sayCannot(`cannot watch ${proj.dir}`, err)
|
|
648
652
|
}
|
|
649
653
|
}
|
|
650
654
|
const armRoot = (): void => {
|
|
@@ -677,8 +681,7 @@ async function runWatchLoop(args: WatchLoopArgs): Promise<number> {
|
|
|
677
681
|
}
|
|
678
682
|
})
|
|
679
683
|
} catch (err) {
|
|
680
|
-
|
|
681
|
-
process.stderr.write(`vx watch: cannot watch workspace root: ${msg}\n`)
|
|
684
|
+
sayCannot(`cannot watch workspace root`, err)
|
|
682
685
|
}
|
|
683
686
|
}
|
|
684
687
|
// One recursive watcher per project, unless workspace-wide: each project
|
|
@@ -733,8 +736,7 @@ async function runWatchLoop(args: WatchLoopArgs): Promise<number> {
|
|
|
733
736
|
}),
|
|
734
737
|
)
|
|
735
738
|
} catch (err) {
|
|
736
|
-
|
|
737
|
-
process.stderr.write(`vx watch: cannot watch ${dir}: ${msg}\n`)
|
|
739
|
+
sayCannot(`cannot watch ${dir}`, err)
|
|
738
740
|
continue
|
|
739
741
|
}
|
|
740
742
|
if (afterReread && fs.existsSync(manifest)) arrived()
|
|
@@ -791,8 +793,7 @@ async function runWatchLoop(args: WatchLoopArgs): Promise<number> {
|
|
|
791
793
|
}),
|
|
792
794
|
)
|
|
793
795
|
} catch (err) {
|
|
794
|
-
|
|
795
|
-
process.stderr.write(`vx watch: cannot watch ${dir}: ${msg}\n`)
|
|
796
|
+
sayCannot(`cannot watch ${dir}`, err)
|
|
796
797
|
}
|
|
797
798
|
}
|
|
798
799
|
}
|
|
@@ -807,8 +808,7 @@ async function runWatchLoop(args: WatchLoopArgs): Promise<number> {
|
|
|
807
808
|
try {
|
|
808
809
|
next = await args.rediscover()
|
|
809
810
|
} catch (err) {
|
|
810
|
-
|
|
811
|
-
process.stderr.write(`vx watch: cannot re-read the workspace: ${msg}\n`)
|
|
811
|
+
sayCannot(`cannot re-read the workspace`, err)
|
|
812
812
|
return
|
|
813
813
|
}
|
|
814
814
|
projectDirs = next.projects.map((p) => p.dir)
|
|
@@ -893,8 +893,7 @@ async function runWatchLoop(args: WatchLoopArgs): Promise<number> {
|
|
|
893
893
|
}),
|
|
894
894
|
)
|
|
895
895
|
} catch (err) {
|
|
896
|
-
|
|
897
|
-
process.stderr.write(`vx watch: cannot watch ${base}: ${msg}\n`)
|
|
896
|
+
sayCannot(`cannot watch ${base}`, err)
|
|
898
897
|
}
|
|
899
898
|
}
|
|
900
899
|
}
|
package/src/exec/kill-tree.ts
CHANGED
|
@@ -15,6 +15,25 @@ import { executablePath, procfsIsOwn } from '../util/index.js'
|
|
|
15
15
|
|
|
16
16
|
export type Child = ReturnType<typeof Bun.spawn>
|
|
17
17
|
|
|
18
|
+
const WIN32 = process.platform === 'win32'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Windows has no process groups (`kill(-pid)` is ESRCH, which read as
|
|
22
|
+
* "gone" and killed nothing) and no polite signal a console task hears
|
|
23
|
+
* from another process, so every stop is `taskkill /T /F`: the child and
|
|
24
|
+
* every descendant it still parents. A grandchild whose parent already
|
|
25
|
+
* exited is out of the tree's reach (docs/design/windows-2026-09.md).
|
|
26
|
+
*/
|
|
27
|
+
function killTreeWin32(child: Child): void {
|
|
28
|
+
try {
|
|
29
|
+
Bun.spawn(['taskkill', '/T', '/F', '/PID', String(child.pid)], {
|
|
30
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
31
|
+
})
|
|
32
|
+
} catch {
|
|
33
|
+
child.kill()
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
18
37
|
/**
|
|
19
38
|
* A child whose polite signals go down a pipe instead of to its group: a
|
|
20
39
|
* Linux sandboxed task. The group there is bwrap's, whose monitor dies of
|
|
@@ -208,6 +227,7 @@ export function killTree(child: Child, signal: 'SIGINT' | 'SIGTERM' | 'SIGKILL')
|
|
|
208
227
|
// A pid of 0 would name OUR group (kill(0)): a child that never
|
|
209
228
|
// spawned has nothing to kill.
|
|
210
229
|
if (!(child.pid > 0) || goneGroups.has(child)) return
|
|
230
|
+
if (WIN32) return killTreeWin32(child)
|
|
211
231
|
const fd = signal === 'SIGKILL' ? undefined : channels.get(child)
|
|
212
232
|
if (fd !== undefined) {
|
|
213
233
|
try {
|
|
@@ -277,6 +297,8 @@ export async function untilGroupsGone(
|
|
|
277
297
|
*/
|
|
278
298
|
function groupAlive(child: Child): boolean {
|
|
279
299
|
if (!(child.pid > 0) || goneGroups.has(child)) return false
|
|
300
|
+
// No groups: the tree kill is `taskkill /T`, and the leader is what is left to ask.
|
|
301
|
+
if (WIN32) return child.exitCode === null && child.signalCode === null
|
|
280
302
|
try {
|
|
281
303
|
process.kill(-child.pid, 0)
|
|
282
304
|
} catch (err) {
|
|
@@ -71,7 +71,10 @@ const SPECIFIER =
|
|
|
71
71
|
|
|
72
72
|
/** Whether a specifier is one the walk would have to look up at all. */
|
|
73
73
|
function needsLookup(spec: string): boolean {
|
|
74
|
-
|
|
74
|
+
// `#x` is a package.json `imports` entry: Bun maps it inside the
|
|
75
|
+
// package and never asks the registry, as no npm name opens with `#`
|
|
76
|
+
// (D-28).
|
|
77
|
+
if (spec.startsWith('#') || spec.includes(':') || BUILTINS.has(spec)) return false
|
|
75
78
|
return !(spec === '@vzn/vx' || spec.startsWith('@vzn/vx/'))
|
|
76
79
|
}
|
|
77
80
|
|