@vzn/vx 0.0.123 → 0.0.125

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.123",
3
+ "version": "0.0.125",
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.123",
21
- "@vzn/vx-linux-arm64": "0.0.123",
22
- "@vzn/vx-darwin-x64": "0.0.123",
23
- "@vzn/vx-darwin-arm64": "0.0.123"
20
+ "@vzn/vx-linux-x64": "0.0.125",
21
+ "@vzn/vx-linux-arm64": "0.0.125",
22
+ "@vzn/vx-darwin-x64": "0.0.125",
23
+ "@vzn/vx-darwin-arm64": "0.0.125"
24
24
  },
25
25
  "dependencies": {
26
26
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
@@ -40,7 +40,9 @@ export {
40
40
  type WorkspaceFilesCache,
41
41
  cleanOutputPaths,
42
42
  ownOutputsSince,
43
+ ownWorkspaceOutputsSince,
43
44
  stampOutputs,
45
+ stampWorkspaceOutputs,
44
46
  type OutputStamp,
45
47
  } from './inputs.js'
46
48
  export {
@@ -643,7 +643,18 @@ export async function stampOutputs(args: {
643
643
  outputs: string[]
644
644
  nestedProjectDirs: string[]
645
645
  }): Promise<Map<string, OutputStamp>> {
646
- const files = await resolveOutputs(args)
646
+ return stampFiles(await resolveOutputs(args))
647
+ }
648
+
649
+ /** `stampOutputs` for root-anchored `cache.outputs.workspaceFiles` (A-43). */
650
+ export async function stampWorkspaceOutputs(args: {
651
+ workspaceRoot: string
652
+ outputs: string[]
653
+ }): Promise<Map<string, OutputStamp>> {
654
+ return stampFiles(await resolveWorkspaceOutputs(args))
655
+ }
656
+
657
+ function stampFiles(files: readonly string[]): Map<string, OutputStamp> {
647
658
  const out = new Map<string, OutputStamp>()
648
659
  for (const f of files) {
649
660
  try {
@@ -668,7 +679,21 @@ export async function ownOutputsSince(
668
679
  args: { projectDir: string; outputs: string[]; nestedProjectDirs: string[] },
669
680
  before: ReadonlyMap<string, OutputStamp>,
670
681
  ): Promise<string[]> {
671
- const after = await resolveOutputs(args)
682
+ return changedSince(await resolveOutputs(args), before)
683
+ }
684
+
685
+ /** `ownOutputsSince` for root-anchored `cache.outputs.workspaceFiles` (A-43). */
686
+ export async function ownWorkspaceOutputsSince(
687
+ args: { workspaceRoot: string; outputs: string[] },
688
+ before: ReadonlyMap<string, OutputStamp>,
689
+ ): Promise<string[]> {
690
+ return changedSince(await resolveWorkspaceOutputs(args), before)
691
+ }
692
+
693
+ function changedSince(
694
+ after: readonly string[],
695
+ before: ReadonlyMap<string, OutputStamp>,
696
+ ): string[] {
672
697
  const own: string[] = []
673
698
  for (const f of after) {
674
699
  const was = before.get(f)
@@ -9,7 +9,9 @@ import {
9
9
  cleanWorkspaceOutputs,
10
10
  type OutputStamp,
11
11
  ownOutputsSince,
12
+ ownWorkspaceOutputsSince,
12
13
  stampOutputs,
14
+ stampWorkspaceOutputs,
13
15
  FULL_CACHE_POLICY,
14
16
  type GitFilesCache,
15
17
  } from '../cache/index.js'
@@ -616,6 +618,7 @@ async function executeCachedTask(args: ExecuteArgs): Promise<TaskOutcome> {
616
618
  // earlier run are its command's to clean, as they are under Turbo.
617
619
  const additive = (node.addsToOutputsOf?.length ?? 0) > 0
618
620
  let stampedBefore: ReadonlyMap<string, OutputStamp> | undefined
621
+ let wsStampedBefore: ReadonlyMap<string, OutputStamp> | undefined
619
622
 
620
623
  // Cache lookup. On hit, time the user-perceived restore op
621
624
  // (clean+restore+log-replay) — that's what the framed-block footer
@@ -756,7 +759,12 @@ async function executeCachedTask(args: ExecuteArgs): Promise<TaskOutcome> {
756
759
  endClean()
757
760
  args.gitFilesCache?.markOutputsChanged(node.projectDir, cleanedRels)
758
761
  }
759
- if (willWrite && !deferralRequested && wsOutputs.length > 0) {
762
+ if (additive && willWrite && !deferralRequested && wsOutputs.length > 0) {
763
+ // A root-anchored addition is stamped as the project one above: a
764
+ // glob clean deleted the upstream's files before this task, which
765
+ // reads them, ran (A-43).
766
+ wsStampedBefore ??= await stampWorkspaceOutputs(wsCleanArgs)
767
+ } else if (willWrite && !deferralRequested && wsOutputs.length > 0) {
760
768
  // Root-anchored deletions can land in other projects' dirs; mark
761
769
  // them so stale per-project git snapshots can't survive the wipe.
762
770
  const cleanedWsRels = await cleanWorkspaceOutputs(wsCleanArgs)
@@ -1028,8 +1036,13 @@ async function executeCachedTask(args: ExecuteArgs): Promise<TaskOutcome> {
1028
1036
  additive && stampedBefore !== undefined
1029
1037
  ? await ownOutputsSince(cleanArgs, stampedBefore)
1030
1038
  : undefined
1039
+ const ownWsOutputFiles =
1040
+ additive && wsStampedBefore !== undefined
1041
+ ? await ownWorkspaceOutputsSince(wsCleanArgs, wsStampedBefore)
1042
+ : undefined
1031
1043
  const { landed } = await saveMiss({
1032
1044
  ...(ownOutputFiles !== undefined ? { ownOutputFiles } : {}),
1045
+ ...(ownWsOutputFiles !== undefined ? { ownWsOutputFiles } : {}),
1033
1046
  node,
1034
1047
  hash,
1035
1048
  cache,
@@ -71,6 +71,8 @@ export interface SaveMissArgs {
71
71
  * would also take the upstream's files the task adds beside.
72
72
  */
73
73
  ownOutputFiles?: string[] | undefined
74
+ /** `ownOutputFiles` for `cache.outputs.workspaceFiles` (A-43). */
75
+ ownWsOutputFiles?: string[] | undefined
74
76
  /** The Tier-3 input fingerprint rows captured by the pre-exec describe. */
75
77
  captured: readonly TaskInputComponent[]
76
78
  command: string
@@ -101,10 +103,12 @@ export async function saveMiss(a: SaveMissArgs): Promise<{ landed: Promise<void>
101
103
  outputs: a.outputs,
102
104
  nestedProjectDirs: a.nestedProjectDirs,
103
105
  }))
104
- const wsOutputFiles = await resolveWorkspaceOutputs({
105
- workspaceRoot: a.workspaceRoot,
106
- outputs: a.wsOutputs,
107
- })
106
+ const wsOutputFiles =
107
+ a.ownWsOutputFiles ??
108
+ (await resolveWorkspaceOutputs({
109
+ workspaceRoot: a.workspaceRoot,
110
+ outputs: a.wsOutputs,
111
+ }))
108
112
  endResolve()
109
113
  // The mirror of the input warning: declared outputs that resolve to
110
114
  // nothing save an empty artifact, and the next hit "restores" it —
@@ -142,9 +142,11 @@ const MAX_CLOSURE_FILES = 32
142
142
  // defeat it. `random` is denied as a word, not as `Math.random`: a
143
143
  // destructured `const { random } = Math` was cached as pure (item 1036).
144
144
  // `prompt`, `confirm` and `alert` read the user's terminal, and a cached
145
- // evaluation replayed the first answer (D-25).
145
+ // evaluation replayed the first answer (D-25). A CommonJS config reaches
146
+ // `require` without naming it as `arguments[1]`, and one reading a file
147
+ // outside its closure so replayed the old bytes (D-36).
146
148
  const IMPURE_RE =
147
- /\b(?:process|Bun|globalThis|global|self|fetch|Date|Temporal|Intl|crypto|performance|navigator|require|eval|Function|constructor|localeCompare|await|toLocale\w*|Reflect|getPrototypeOf|setPrototypeOf|getOwnPropertyNames|getOwnPropertyDescriptor|getOwnPropertyDescriptors|__proto__|prototype|__defineGetter__|__defineSetter__|__lookupGetter__|__lookupSetter__|random|prompt|confirm|alert)\b|import\s*\.\s*meta|\bimport\s*\(/
149
+ /\b(?:process|Bun|globalThis|global|self|fetch|Date|Temporal|Intl|crypto|performance|navigator|require|eval|Function|constructor|localeCompare|await|toLocale\w*|Reflect|getPrototypeOf|setPrototypeOf|getOwnPropertyNames|getOwnPropertyDescriptor|getOwnPropertyDescriptors|__proto__|prototype|__defineGetter__|__defineSetter__|__lookupGetter__|__lookupSetter__|random|prompt|confirm|alert|arguments)\b|import\s*\.\s*meta|\bimport\s*\(/
148
150
 
149
151
  /** Literal text that names a way to `Function` when used as a computed key. */
150
152
  const IMPURE_LITERAL_RE = /constructor|__proto__|prototype/