@vzn/vx 0.0.417 → 0.0.419
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-filter.ts +32 -0
- package/src/cli/watch-judge.ts +15 -3
- package/src/cli/watch.ts +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.419",
|
|
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.419",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.419",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.419",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.419"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cli/watch-filter.ts
CHANGED
|
@@ -290,3 +290,35 @@ export function isWorkspaceConfigFile(name: string): boolean {
|
|
|
290
290
|
* workspace is the one it ignores.
|
|
291
291
|
*/
|
|
292
292
|
const FINGERPRINT_FILES: ReadonlySet<string> = new Set(WORKSPACE_FINGERPRINT_FILES)
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* The files under the workspace git lists, tracked and untracked, ignored
|
|
296
|
+
* ones aside, and every directory above one (git lists no directory, and
|
|
297
|
+
* a project moved away whole is one event on its directory): what existed
|
|
298
|
+
* when watch armed, so a path born and gone since is told from a deletion
|
|
299
|
+
* (watch-judge.ts). Undefined when git cannot answer: no inventory, every
|
|
300
|
+
* gone path is a deletion as before.
|
|
301
|
+
*/
|
|
302
|
+
export function gitFiles(workspaceRoot: string): Set<string> | undefined {
|
|
303
|
+
let proc: ReturnType<typeof Bun.spawnSync>
|
|
304
|
+
try {
|
|
305
|
+
proc = Bun.spawnSync({
|
|
306
|
+
cmd: [executablePath('git'), 'ls-files', '-z', '--cached', '--others', '--exclude-standard'],
|
|
307
|
+
cwd: workspaceRoot,
|
|
308
|
+
stdout: 'pipe',
|
|
309
|
+
stderr: 'ignore',
|
|
310
|
+
})
|
|
311
|
+
} catch {
|
|
312
|
+
return undefined
|
|
313
|
+
}
|
|
314
|
+
if (proc.exitCode !== 0) return undefined
|
|
315
|
+
const files = new Set<string>()
|
|
316
|
+
for (const p of new TextDecoder().decode(proc.stdout).split('\0')) {
|
|
317
|
+
if (p.length === 0) continue
|
|
318
|
+
let abs = path.join(workspaceRoot, p)
|
|
319
|
+
files.add(abs)
|
|
320
|
+
for (abs = path.dirname(abs); abs !== workspaceRoot && !files.has(abs); abs = path.dirname(abs))
|
|
321
|
+
files.add(abs)
|
|
322
|
+
}
|
|
323
|
+
return files
|
|
324
|
+
}
|
package/src/cli/watch-judge.ts
CHANGED
|
@@ -53,6 +53,8 @@ export interface JudgeContext {
|
|
|
53
53
|
held(): boolean
|
|
54
54
|
/** Projects whose uncached task may read a git-ignored file. */
|
|
55
55
|
uncached(): ReadonlySet<string>
|
|
56
|
+
/** The files git listed at the arm; absent when git could not answer. */
|
|
57
|
+
existedAtArm?: ReadonlySet<string>
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
export class ChangeJudge {
|
|
@@ -108,12 +110,22 @@ export class ChangeJudge {
|
|
|
108
110
|
// and a first sighting used to pass unconditionally; the path's mtime
|
|
109
111
|
// says which side of the arm it belongs to. A path already gone is a
|
|
110
112
|
// change: a deletion has no date to read.
|
|
111
|
-
|
|
113
|
+
// One exception: a path git did not list at the arm, and gone now, was
|
|
114
|
+
// born and removed since (vim's `4913` write probe, a tool's lock file);
|
|
115
|
+
// it started a cycle with nothing changed. The blind spot: one born in
|
|
116
|
+
// the moment between a judgement and that cycle's keys, and gone by the
|
|
117
|
+
// next judgement, was read and its deletion re-runs nothing. Git lists
|
|
118
|
+
// no ignored path, so one of those is a deletion as before.
|
|
119
|
+
private sameState(abs: string, ignored = false): boolean {
|
|
112
120
|
const state = settledState(abs)
|
|
113
121
|
const prev = this.lastState.get(abs)
|
|
114
122
|
this.lastState.set(abs, state)
|
|
115
123
|
if (prev !== undefined) return prev === state
|
|
116
|
-
|
|
124
|
+
if (state === ABSENT) {
|
|
125
|
+
const listed = this.ctx.existedAtArm
|
|
126
|
+
return !ignored && listed !== undefined && !listed.has(abs)
|
|
127
|
+
}
|
|
128
|
+
return modifiedBefore(abs, this.ctx.armedAt)
|
|
117
129
|
}
|
|
118
130
|
|
|
119
131
|
private writtenDuringLastCycle(abs: string, openWhileHeld = false): boolean {
|
|
@@ -158,7 +170,7 @@ export class ChangeJudge {
|
|
|
158
170
|
let gone: [label: string, abs: string] | undefined
|
|
159
171
|
for (const [p, l] of this.pending) {
|
|
160
172
|
if (ignored.has(p) && !editForUncached(p)) continue
|
|
161
|
-
if (this.sameState(p) || first !== undefined) continue
|
|
173
|
+
if (this.sameState(p, ignored.has(p)) || first !== undefined) continue
|
|
162
174
|
if (!fs.existsSync(p)) {
|
|
163
175
|
gone ??= [l, p]
|
|
164
176
|
continue
|
package/src/cli/watch.ts
CHANGED
|
@@ -35,6 +35,7 @@ import { type CliLoadOptions, discoverCliProjects, loadCliWorkspace } from './wo
|
|
|
35
35
|
import {
|
|
36
36
|
isIgnoredWatchPath,
|
|
37
37
|
isWorkspaceConfigFile,
|
|
38
|
+
gitFiles,
|
|
38
39
|
isWorkspaceFingerprintFile,
|
|
39
40
|
makeRootEventFilter,
|
|
40
41
|
makeWatchIgnore,
|
|
@@ -416,12 +417,15 @@ async function runWatchLoop(args: WatchLoopArgs): Promise<number> {
|
|
|
416
417
|
|
|
417
418
|
/** The instant the watchers go live, on the mtime clock (see `fsClockNow`): a path last modified before it is the initial run's, not an edit. */
|
|
418
419
|
const armedAt = fsClockNow(cacheDir)
|
|
420
|
+
/** What existed at the arm, so a file born and gone since is no deletion (watch-judge.ts). */
|
|
421
|
+
const existedAtArm = gitFiles(workspaceRoot)
|
|
419
422
|
/** Which settled paths are changes (watch-judge.ts); `pending` holds what fired since. */
|
|
420
423
|
const changes = new ChangeJudge({
|
|
421
424
|
workspaceRoot,
|
|
422
425
|
armedAt,
|
|
423
426
|
held: () => held !== undefined,
|
|
424
427
|
uncached: () => uncached,
|
|
428
|
+
...(existedAtArm !== undefined ? { existedAtArm } : {}),
|
|
425
429
|
})
|
|
426
430
|
/** Per-project arms by directory, so `rearm` can add and drop them. */
|
|
427
431
|
const perProject = new Map<string, WatchHandle>()
|