@vzn/vx 0.0.591 → 0.0.594
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/launcher.cjs +9 -2
- package/package.json +5 -5
- package/src/cache/archive.ts +16 -5
- package/src/cache/cache.ts +41 -11
- package/src/cache/git-inputs.ts +14 -9
- package/src/cache/inputs.ts +47 -17
- package/src/cache/tar-stream.ts +3 -1
- package/src/cli/cache.ts +3 -5
- package/src/cli/foreign-flags.ts +33 -4
- package/src/cli/help.ts +22 -5
- package/src/cli/info.ts +4 -6
- package/src/cli/init.ts +3 -3
- package/src/cli/last.ts +49 -20
- package/src/cli/lock.ts +3 -3
- package/src/cli/run.ts +65 -37
- package/src/cli/select.ts +14 -2
- package/src/cli/show.ts +5 -6
- package/src/cli/upgrade.ts +2 -3
- package/src/cli/watch-filter.ts +7 -6
- package/src/cli/watch-fs.ts +17 -1
- package/src/cli/watch.ts +22 -4
- package/src/cli/why.ts +4 -13
- package/src/cli/workspace-config.ts +1 -1
- package/src/exec/env.ts +3 -5
- package/src/exec/runner.ts +28 -7
- package/src/exec/sandbox-deny-scan.ts +2 -2
- package/src/exec/sandbox-runtime.ts +69 -19
- package/src/exec/sandbox-violations.ts +23 -9
- package/src/graph/scheduler.ts +13 -3
- package/src/graph/task-graph.ts +3 -2
- package/src/index.ts +3 -0
- package/src/orchestrator/affected-tasks.ts +34 -10
- package/src/orchestrator/doctor.ts +2 -1
- package/src/orchestrator/execute-task.ts +14 -3
- package/src/orchestrator/hit-restore.ts +3 -2
- package/src/orchestrator/index.ts +1 -0
- package/src/orchestrator/lockfile-claim.ts +2 -3
- package/src/orchestrator/logger.ts +4 -1
- package/src/orchestrator/miss-save.ts +10 -3
- package/src/orchestrator/placement.ts +17 -3
- package/src/orchestrator/plugin-host.ts +16 -0
- package/src/orchestrator/plugin.ts +9 -13
- package/src/orchestrator/projects.ts +18 -9
- package/src/orchestrator/run-context.ts +2 -2
- package/src/orchestrator/run-lock.ts +14 -4
- package/src/orchestrator/run.ts +42 -8
- package/src/orchestrator/sandbox-request.ts +6 -10
- package/src/workspace/affected.ts +17 -11
- package/src/workspace/config-eval.ts +4 -0
- package/src/workspace/config-imports.ts +1 -1
- package/src/workspace/config-schema.ts +2 -7
- package/src/workspace/filter.ts +5 -4
- package/src/workspace/migrate-scripts.ts +22 -5
- package/src/workspace/project-loader.ts +17 -2
- package/src/workspace/workspace.ts +40 -4
|
@@ -269,6 +269,9 @@ interface BuildPosition {
|
|
|
269
269
|
column?: number
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
/** A stack frame naming a file path, as a config's own throw has. */
|
|
273
|
+
const NAMED_FRAME = /\n\s+at .*[\\/]/
|
|
274
|
+
|
|
272
275
|
/**
|
|
273
276
|
* Turn the two errors Bun's own loader throws into user errors naming the
|
|
274
277
|
* file the user wrote; every other throw is the config's own and already
|
|
@@ -301,6 +304,13 @@ export function configLoadError(err: unknown, configPath: string, kind: string):
|
|
|
301
304
|
return configLoadError(errors[0], configPath, kind)
|
|
302
305
|
}
|
|
303
306
|
if (typeof message !== 'string') return null
|
|
307
|
+
// Bun's JSON loader throws a `SyntaxError` whose stack, when it has one,
|
|
308
|
+
// holds no file frame: a malformed `import data from './data.json'`
|
|
309
|
+
// printed `vx: JSON Parse error: Expected '}'` and named nothing. A
|
|
310
|
+
// config's own `JSON.parse` has its line in the stack and passes through.
|
|
311
|
+
if (name === 'SyntaxError' && !NAMED_FRAME.test(String((err as { stack?: unknown }).stack))) {
|
|
312
|
+
return new UserError(`${kind} config ${configPath}: an import does not parse: ${message}`)
|
|
313
|
+
}
|
|
304
314
|
if (name === 'ResolveMessage') {
|
|
305
315
|
const spec = /Cannot find (?:package|module) ['"]([^'"]+)['"]/.exec(message)?.[1]
|
|
306
316
|
const what = spec === undefined ? message.replace(BUST_QUERY, '') : `cannot find '${spec}'`
|
|
@@ -665,14 +675,19 @@ export async function loadProjectConfigs(
|
|
|
665
675
|
*/
|
|
666
676
|
// `Bun` and `Bun.hash` are what vx itself runs on: `Bun.hash.xxHash3 = ()
|
|
667
677
|
// => 7n` gave every task the key 00000000, and a changed command replayed
|
|
668
|
-
// the old output (D-75).
|
|
678
|
+
// the old output (D-75). `Bun` is read by its identifier, not through
|
|
679
|
+
// globalThis: the docs playground bundles this file with the identifier
|
|
680
|
+
// rewritten to its shim, and a browser has no global `Bun`.
|
|
669
681
|
const WATCHED_BUILTINS: ReadonlyArray<readonly [string, object]> = WATCHED_BUILTIN_NAMES.map(
|
|
670
682
|
(name) =>
|
|
671
683
|
[
|
|
672
684
|
name,
|
|
673
685
|
name
|
|
674
686
|
.split('.')
|
|
675
|
-
.reduce<unknown>(
|
|
687
|
+
.reduce<unknown>(
|
|
688
|
+
(o, k) => (o === globalThis && k === 'Bun' ? Bun : (o as Record<string, unknown>)[k]),
|
|
689
|
+
globalThis,
|
|
690
|
+
) as object,
|
|
676
691
|
] as const,
|
|
677
692
|
)
|
|
678
693
|
|
|
@@ -115,8 +115,12 @@ export async function findWorkspaceRoot(
|
|
|
115
115
|
// them: `packages/**` holding `packages/inner` (with `workspaces:
|
|
116
116
|
// ['sub']`) and `packages/inner/sub` resolved `sub` to `inner` and
|
|
117
117
|
// `inner` to the outer root, two roots and two keys for one tree.
|
|
118
|
-
if (
|
|
119
|
-
|
|
118
|
+
if (
|
|
119
|
+
(await claimsMember(dir, [claimed], globs)) &&
|
|
120
|
+
(await claimsMember(dir, members, globs))
|
|
121
|
+
)
|
|
122
|
+
claimed = dir
|
|
123
|
+
} else if (await claimsMember(dir, inner === null ? below : [inner], globs)) {
|
|
120
124
|
claimed = dir
|
|
121
125
|
members = inner === null ? [...below] : [inner]
|
|
122
126
|
}
|
|
@@ -152,7 +156,11 @@ export async function findWorkspaceRoot(
|
|
|
152
156
|
}
|
|
153
157
|
|
|
154
158
|
/** True when one of `below` (dirs under `root`, toward `start`) is a member. */
|
|
155
|
-
function claimsMember(
|
|
159
|
+
async function claimsMember(
|
|
160
|
+
root: string,
|
|
161
|
+
below: readonly string[],
|
|
162
|
+
globs: readonly string[],
|
|
163
|
+
): Promise<boolean> {
|
|
156
164
|
if (below.length === 0 || globs.length === 0) return false
|
|
157
165
|
const { positive, negative } = splitPackageGlobs(globs)
|
|
158
166
|
const rels = below.map((d) => relPosix(root, d)).filter((rel) => !excludedBy(rel, negative))
|
|
@@ -161,11 +169,21 @@ function claimsMember(root: string, below: readonly string[], globs: readonly st
|
|
|
161
169
|
// `.` means the root itself is the project — never a directory below it.
|
|
162
170
|
if (normalized === '' || normalized === '.') continue
|
|
163
171
|
const glob = new Bun.Glob(normalized)
|
|
164
|
-
|
|
172
|
+
for (const rel of rels) {
|
|
173
|
+
if (!glob.match(rel)) continue
|
|
174
|
+
// `match` has no `dot: false`: `packages/*` matched `packages/.tpl`,
|
|
175
|
+
// which discovery skips, and a run from inside it found a workspace
|
|
176
|
+
// that does not list it. Such a path asks discovery's own walk.
|
|
177
|
+
if (!SKIPPED_SEGMENT.test(rel)) return true
|
|
178
|
+
const dirs = await memberDirs(root, pattern)
|
|
179
|
+
if (dirs.includes(path.join(root, rel))) return true
|
|
180
|
+
}
|
|
165
181
|
}
|
|
166
182
|
return false
|
|
167
183
|
}
|
|
168
184
|
|
|
185
|
+
const SKIPPED_SEGMENT = /(^|\/)(\.|node_modules(\/|$))/
|
|
186
|
+
|
|
169
187
|
/**
|
|
170
188
|
* pnpm, npm, yarn and Bun all take `!packages/fixtures` in the package
|
|
171
189
|
* list. Handed to `Bun.Glob` raw, a leading `!` negates the WHOLE pattern,
|
|
@@ -374,6 +392,11 @@ function extglobRefusal(pattern: string, file: string, field: string): UserError
|
|
|
374
392
|
*/
|
|
375
393
|
export function resolveCacheDir(root: string, config: WorkspaceConfig | null): string {
|
|
376
394
|
const rel = config?.cacheDir ?? (process.env['VX_CACHE_DIR'] || path.join('.vx', 'cache'))
|
|
395
|
+
// No shell expands `~` in a config string or a quoted variable, and
|
|
396
|
+
// `'~/.cache/vx'` made a directory named `~` in the workspace.
|
|
397
|
+
if (rel.startsWith('~/')) {
|
|
398
|
+
return path.join(process.env['HOME'] || homedir(), rel.slice(1))
|
|
399
|
+
}
|
|
377
400
|
return path.resolve(root, rel)
|
|
378
401
|
}
|
|
379
402
|
|
|
@@ -809,6 +832,19 @@ export async function namedProject(
|
|
|
809
832
|
if (typeof named?.name !== 'string' || named.name === '' || typeof named.dir !== 'string') {
|
|
810
833
|
throw new UserError(`${where} ${JSON.stringify(named)}: expected { dir: string, name: string }`)
|
|
811
834
|
}
|
|
835
|
+
// The rules `parsePackageJson` holds a manifest's name to: a plugin's name
|
|
836
|
+
// for a directory with no `name` of its own skipped them, and `a#b` planned
|
|
837
|
+
// under `--all` while no run spec or dependsOn could reach it.
|
|
838
|
+
if (named.name.trim() !== named.name) {
|
|
839
|
+
throw new UserError(
|
|
840
|
+
`${where} ${JSON.stringify(named.name)}: the name has surrounding whitespace`,
|
|
841
|
+
)
|
|
842
|
+
}
|
|
843
|
+
if (named.name.includes('#')) {
|
|
844
|
+
throw new UserError(
|
|
845
|
+
`${where} "${named.name}": the name holds "#" — vx addresses a task as <name>#<task>`,
|
|
846
|
+
)
|
|
847
|
+
}
|
|
812
848
|
const dir = path.resolve(workspace.root, named.dir)
|
|
813
849
|
const rel = relPosix(workspace.root, dir)
|
|
814
850
|
if (rel === '..' || rel.startsWith('../') || path.isAbsolute(rel)) {
|