@vzn/vx 0.0.411 → 0.0.413
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.
|
|
3
|
+
"version": "0.0.413",
|
|
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.413",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.413",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.413",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.413"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
|
@@ -10,7 +10,7 @@ import { unlink } from 'node:fs/promises'
|
|
|
10
10
|
import path from 'node:path'
|
|
11
11
|
import { executablePath, relPosix, UserError } from '../util/index.js'
|
|
12
12
|
import type { ProjectMeta } from './workspace.js'
|
|
13
|
-
import { loadWorkspace, unreachedHint, unreachedPackages } from './workspace.js'
|
|
13
|
+
import { loadWorkspace, reachesManifest, unreachedHint, unreachedPackages } from './workspace.js'
|
|
14
14
|
import { WORKSPACE_CONFIG_FILENAMES } from './project-loader.js'
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -357,8 +357,10 @@ export async function applyMigration(args: ApplyMigrationArgs): Promise<number>
|
|
|
357
357
|
// Globs that reach no package: "no scripts" was true and named nothing
|
|
358
358
|
// to fix (M-42).
|
|
359
359
|
const globs = workspace?.packageGlobs.filter((g) => g !== '.') ?? []
|
|
360
|
+
// Asked of the globs, not of `metas`: a nameless or shared-name member
|
|
361
|
+
// is matched yet absent from `metas` (M-46).
|
|
360
362
|
const noMembers =
|
|
361
|
-
globs.length > 0 &&
|
|
363
|
+
workspace !== undefined && globs.length > 0 && !(await reachesManifest(workspace))
|
|
362
364
|
? [
|
|
363
365
|
`the workspace globs (${globs.map((g) => `"${g}"`).join(', ')}) match no package.json: add a package under one, or fix the glob`,
|
|
364
366
|
]
|
|
@@ -456,6 +456,7 @@ export async function loadProjectConfigs(
|
|
|
456
456
|
// More than one evaluation in flight: a change seen after one load may
|
|
457
457
|
// be another's.
|
|
458
458
|
let overlapping = false
|
|
459
|
+
let tainted = false
|
|
459
460
|
const loadOne = async (entry: (typeof prepared)[number]): Promise<Loaded> => {
|
|
460
461
|
const { configPath, cacheKey } = entry
|
|
461
462
|
// A fast key that missed: the closure is stale or the file changed.
|
|
@@ -500,7 +501,7 @@ export async function loadProjectConfigs(
|
|
|
500
501
|
const changed = repeat
|
|
501
502
|
? []
|
|
502
503
|
: [
|
|
503
|
-
...restoreBuiltins(builtins),
|
|
504
|
+
...restoreBuiltins(builtins, overlapping),
|
|
504
505
|
...restoreEnv(env),
|
|
505
506
|
...restoreCwd(cwd),
|
|
506
507
|
...restoreUmask(umask),
|
|
@@ -585,20 +586,30 @@ export async function loadProjectConfigs(
|
|
|
585
586
|
...restoreUmask(umask),
|
|
586
587
|
]
|
|
587
588
|
: []
|
|
588
|
-
|
|
589
|
+
// Overlapping loads check most built-ins only here, so a change one
|
|
590
|
+
// config made may have broken another's load: it is refused first, and
|
|
591
|
+
// nothing the round evaluated is stored.
|
|
592
|
+
const changedInRound =
|
|
593
|
+
first?.failed instanceof ChangedInRound
|
|
594
|
+
? first.failed.changed
|
|
595
|
+
: overlapping && changed.length > 0
|
|
596
|
+
? changed
|
|
597
|
+
: undefined
|
|
598
|
+
if (changedInRound !== undefined) {
|
|
599
|
+
tainted = true
|
|
589
600
|
for (const i of misses) {
|
|
590
601
|
const configPath = prepared[i]!.configPath
|
|
591
602
|
const own = await builtinsChangedBy(configPath)
|
|
592
603
|
if (own.length > 0) throw builtinsChanged(own, configPath)
|
|
593
604
|
}
|
|
594
|
-
throw builtinsChanged(
|
|
605
|
+
throw builtinsChanged(changedInRound)
|
|
595
606
|
}
|
|
596
607
|
if (first !== undefined) throw first.failed
|
|
597
608
|
if (changed.length > 0) throw builtinsChanged(changed)
|
|
598
609
|
return results.map((r) => (r as Loaded).config)
|
|
599
610
|
} finally {
|
|
600
611
|
endRound()
|
|
601
|
-
if (store !== undefined) {
|
|
612
|
+
if (store !== undefined && !tainted) {
|
|
602
613
|
if (evals.length > 0) {
|
|
603
614
|
if (store.putConfigEvals !== undefined) store.putConfigEvals(evals)
|
|
604
615
|
else for (const [k, json] of evals) store.putConfigEval(k, json)
|
|
@@ -655,10 +666,11 @@ interface OwnProperties {
|
|
|
655
666
|
|
|
656
667
|
interface BuiltinSnapshot {
|
|
657
668
|
readonly prims: Primitives
|
|
658
|
-
|
|
669
|
+
/** Undefined for a watched name the snapshot leaves out. */
|
|
670
|
+
readonly props: readonly (OwnProperties | undefined)[]
|
|
659
671
|
}
|
|
660
672
|
|
|
661
|
-
function builtinSnapshot(): BuiltinSnapshot {
|
|
673
|
+
function builtinSnapshot(leaveOut?: string): BuiltinSnapshot {
|
|
662
674
|
const prims: Primitives = {
|
|
663
675
|
ownKeys: Reflect.ownKeys,
|
|
664
676
|
descriptorOf: Object.getOwnPropertyDescriptor,
|
|
@@ -668,8 +680,12 @@ function builtinSnapshot(): BuiltinSnapshot {
|
|
|
668
680
|
same: Object.is,
|
|
669
681
|
keyName: String,
|
|
670
682
|
}
|
|
671
|
-
const props: OwnProperties[] = []
|
|
683
|
+
const props: (OwnProperties | undefined)[] = []
|
|
672
684
|
for (let i = 0; i < WATCHED_BUILTINS.length; i++) {
|
|
685
|
+
if (WATCHED_BUILTINS[i]![0] === leaveOut) {
|
|
686
|
+
props[i] = undefined
|
|
687
|
+
continue
|
|
688
|
+
}
|
|
673
689
|
const proto = WATCHED_BUILTINS[i]![1]
|
|
674
690
|
const keys = prims.ownKeys(proto)
|
|
675
691
|
const descriptors: PropertyDescriptor[] = []
|
|
@@ -727,15 +743,36 @@ function builtinsChanged(changed: readonly string[], configPath?: string): UserE
|
|
|
727
743
|
)
|
|
728
744
|
}
|
|
729
745
|
|
|
746
|
+
/**
|
|
747
|
+
* The built-ins checked after each of several overlapping loads: the ones
|
|
748
|
+
* the loader itself reads through between loads (its promises, maps, sets,
|
|
749
|
+
* JSON and keys). The rest are checked once, at the round's end: all of
|
|
750
|
+
* them after every load was ~73 µs a config, a quarter of a cold load of
|
|
751
|
+
* 1,000 (2026-10-03). A lone load checks them all. By index, decided at
|
|
752
|
+
* module load: a lookup through a `Set` is one a config can replace (D-124).
|
|
753
|
+
*/
|
|
754
|
+
const EVERY_LOAD: readonly boolean[] = WATCHED_BUILTINS.map(([name]) =>
|
|
755
|
+
[
|
|
756
|
+
'Object.prototype',
|
|
757
|
+
'JSON',
|
|
758
|
+
'Promise.prototype',
|
|
759
|
+
'Map.prototype',
|
|
760
|
+
'Set.prototype',
|
|
761
|
+
'Bun.hash',
|
|
762
|
+
].includes(name),
|
|
763
|
+
)
|
|
764
|
+
|
|
730
765
|
/** Puts back what changed since `before`, naming each property it put back. */
|
|
731
|
-
function restoreBuiltins(before: BuiltinSnapshot | undefined): string[] {
|
|
766
|
+
function restoreBuiltins(before: BuiltinSnapshot | undefined, everyLoadOnly = false): string[] {
|
|
732
767
|
const changed: string[] = []
|
|
733
768
|
if (before === undefined) return changed
|
|
734
769
|
const p = before.prims
|
|
735
770
|
for (let i = 0; i < WATCHED_BUILTINS.length; i++) {
|
|
736
771
|
const name = WATCHED_BUILTINS[i]![0]
|
|
772
|
+
if (everyLoadOnly && !EVERY_LOAD[i]) continue
|
|
737
773
|
const proto = WATCHED_BUILTINS[i]![1]
|
|
738
|
-
const was = before.props[i]
|
|
774
|
+
const was = before.props[i]
|
|
775
|
+
if (was === undefined) continue
|
|
739
776
|
const keys = p.ownKeys(proto)
|
|
740
777
|
if (unchanged(p, proto, keys, was)) continue
|
|
741
778
|
// Slow path, a change only: a linear lookup keeps it off `Map`.
|
|
@@ -890,7 +927,29 @@ export async function loadWorkspaceConfig(root: string): Promise<WorkspaceConfig
|
|
|
890
927
|
const since = workspaceLoadedAt.get(configPath)
|
|
891
928
|
if (since !== undefined) await refuseStaleWorkspaceImports(configPath, since)
|
|
892
929
|
const startedAt = Date.now()
|
|
893
|
-
|
|
930
|
+
// The same guard a project config's first load has: the workspace config
|
|
931
|
+
// runs in this process too, and its `Object.prototype.exec` ran in a
|
|
932
|
+
// project's task under a key that never saw it (D-126). Its globals are
|
|
933
|
+
// left out: it loads first in every run, filtered or not, so none
|
|
934
|
+
// depends on what else loaded (D-122's case), and plugins' tests and
|
|
935
|
+
// tools hand state through them.
|
|
936
|
+
const builtins = builtinSnapshot('globalThis')
|
|
937
|
+
const env = { ...process.env }
|
|
938
|
+
const cwd = process.cwd()
|
|
939
|
+
const umask = currentUmask()
|
|
940
|
+
let mod: WorkspaceConfig
|
|
941
|
+
try {
|
|
942
|
+
mod = (await loadDefaultExport(configPath, 'Workspace', bytes)) as WorkspaceConfig
|
|
943
|
+
} finally {
|
|
944
|
+
const changed = [
|
|
945
|
+
...restoreBuiltins(builtins),
|
|
946
|
+
...restoreEnv(env),
|
|
947
|
+
...restoreCwd(cwd),
|
|
948
|
+
...restoreUmask(umask),
|
|
949
|
+
]
|
|
950
|
+
// eslint-disable-next-line no-unsafe-finally -- the refusal outranks the load's own error
|
|
951
|
+
if (changed.length > 0) throw builtinsChanged(changed, configPath)
|
|
952
|
+
}
|
|
894
953
|
// Checked again once awaited, as a project config is: a Promise default
|
|
895
954
|
// passed the first check, and `Promise.resolve(null)` crashed the
|
|
896
955
|
// validator with a stack while `Promise.resolve(42)` loaded as no
|
|
@@ -395,6 +395,28 @@ export function memberBaseDirs(workspace: Workspace): string[] {
|
|
|
395
395
|
return [...bases]
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
+
/**
|
|
399
|
+
* True when a member glob reaches a `package.json` other than the root's,
|
|
400
|
+
* addressable or not: a nameless manifest, or two sharing a name, is
|
|
401
|
+
* matched and left out, not unmatched (M-46).
|
|
402
|
+
*/
|
|
403
|
+
export async function reachesManifest(workspace: Workspace): Promise<boolean> {
|
|
404
|
+
const { positive, negative } = splitPackageGlobs(workspace.packageGlobs)
|
|
405
|
+
const root = path.resolve(workspace.root)
|
|
406
|
+
for (const pattern of positive) {
|
|
407
|
+
for (const dir of await memberDirs(workspace.root, pattern)) {
|
|
408
|
+
if (path.resolve(dir) === root) continue
|
|
409
|
+
if (negative.length > 0 && excludedBy(relPosix(workspace.root, dir), negative)) continue
|
|
410
|
+
try {
|
|
411
|
+
if ((await stat(path.join(dir, 'package.json'))).isFile()) return true
|
|
412
|
+
} catch (err) {
|
|
413
|
+
absent(err, undefined)
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return false
|
|
418
|
+
}
|
|
419
|
+
|
|
398
420
|
/**
|
|
399
421
|
* The directories a workspace glob names. For the `<dir>/*` shape this is
|
|
400
422
|
* one readdir of `<dir>` — the same answer `Bun.Glob` gives, at a third of
|