@vzn/vx 0.0.625 → 0.0.626
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.626",
|
|
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.626",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.626",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.626",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.626"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
|
@@ -59,11 +59,15 @@ const ABORTED = '#f97316' // orange-500 — killed by a shutdown signal
|
|
|
59
59
|
const LOCAL = '#38bdf8' // sky-400 — local cache hit
|
|
60
60
|
const REMOTE = '#2563eb' // blue-600 — remote cache hit
|
|
61
61
|
|
|
62
|
+
/** A piece of a task's output, as written: `err` for stderr. */
|
|
63
|
+
export interface OutputChunk {
|
|
64
|
+
text: string
|
|
65
|
+
err: boolean
|
|
66
|
+
}
|
|
67
|
+
|
|
62
68
|
export interface TaskBlockBody {
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
/** stderr chunks. Renders under `├─ stderr`. */
|
|
66
|
-
stderr?: string
|
|
69
|
+
/** What the task wrote, in order. Renders under `├─ OUTPUT`, stderr lines marked. */
|
|
70
|
+
output?: readonly OutputChunk[]
|
|
67
71
|
/**
|
|
68
72
|
* Characters the capture dropped from the head of each stream. Non-zero
|
|
69
73
|
* only for a task whose output is bounded (a persistent one — nothing
|
|
@@ -92,8 +96,7 @@ export function formatTaskBlock(
|
|
|
92
96
|
// pass already make.
|
|
93
97
|
if (isGroupTask(node)) return ''
|
|
94
98
|
|
|
95
|
-
const
|
|
96
|
-
const stderr = body.stderr ?? ''
|
|
99
|
+
const output = body.output ?? []
|
|
97
100
|
|
|
98
101
|
const idPainted = paintTaskId(node, colors, { bold: true })
|
|
99
102
|
const corner = (s: string) => paint('', s, colors, { dim: true })
|
|
@@ -110,8 +113,7 @@ export function formatTaskBlock(
|
|
|
110
113
|
lines.push('', corner(`$ ${shownCommand(node, forwardArgs)}`), '')
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
|
|
114
|
-
pushStreamSection(lines, stderr, 'STDERR', ERROR, body.droppedStderr ?? 0, colors)
|
|
116
|
+
pushOutputSection(lines, output, (body.droppedStdout ?? 0) + (body.droppedStderr ?? 0), colors)
|
|
115
117
|
|
|
116
118
|
lines.push(...violationSection(outcome, colors))
|
|
117
119
|
|
|
@@ -141,6 +143,48 @@ function pushStreamSection(
|
|
|
141
143
|
lines.push('')
|
|
142
144
|
}
|
|
143
145
|
|
|
146
|
+
/**
|
|
147
|
+
* The one `├─ OUTPUT` section: the task's lines in the order it wrote
|
|
148
|
+
* them, so an error stays beside what it is about (a test runner prints
|
|
149
|
+
* progress to stdout and the failure to stderr). A stderr line is red;
|
|
150
|
+
* without colour it is the task's bytes untouched. A line the task
|
|
151
|
+
* coloured itself keeps its own colours, since a reset inside it would
|
|
152
|
+
* end ours mid-line. A line takes the stream that began it.
|
|
153
|
+
*/
|
|
154
|
+
function pushOutputSection(
|
|
155
|
+
lines: string[],
|
|
156
|
+
output: readonly OutputChunk[],
|
|
157
|
+
lost: number,
|
|
158
|
+
colors: ColorSupport,
|
|
159
|
+
): void {
|
|
160
|
+
if (output.every((c) => c.text.trim().length === 0)) return
|
|
161
|
+
lines.push(sectionLine('OUTPUT', '', colors), '')
|
|
162
|
+
if (lost > 0) {
|
|
163
|
+
lines.push(paint('', `… ${lost} earlier characters dropped`, colors, { dim: true }))
|
|
164
|
+
}
|
|
165
|
+
const body: Array<{ text: string; err: boolean }> = []
|
|
166
|
+
let open: { parts: string[]; err: boolean } | null = null
|
|
167
|
+
for (const c of output) {
|
|
168
|
+
let rest = c.text
|
|
169
|
+
while (rest.length > 0) {
|
|
170
|
+
const nl = rest.indexOf('\n')
|
|
171
|
+
const piece = nl < 0 ? rest : rest.slice(0, nl)
|
|
172
|
+
if (open === null) open = { parts: [], err: c.err }
|
|
173
|
+
open.parts.push(piece)
|
|
174
|
+
if (nl < 0) break
|
|
175
|
+
body.push({ text: open.parts.join(''), err: open.err })
|
|
176
|
+
open = null
|
|
177
|
+
rest = rest.slice(nl + 1)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (open !== null) body.push({ text: open.parts.join(''), err: open.err })
|
|
181
|
+
for (const l of body) {
|
|
182
|
+
if (!l.err || !colors.enabled) lines.push(l.text)
|
|
183
|
+
else lines.push(l.text.includes('\x1b') ? l.text : paint(ERROR, l.text, colors))
|
|
184
|
+
}
|
|
185
|
+
lines.push('')
|
|
186
|
+
}
|
|
187
|
+
|
|
144
188
|
/**
|
|
145
189
|
* Section labels are bold + state-colored (owner design): stdout green,
|
|
146
190
|
* stderr red, sandbox yellow, command plain white — only the command
|
|
@@ -520,7 +564,9 @@ function isPersistentNode(node: TaskNode): boolean {
|
|
|
520
564
|
export function formatPersistentTailBlock(
|
|
521
565
|
node: TaskNode,
|
|
522
566
|
outcome: TaskOutcome,
|
|
523
|
-
|
|
567
|
+
// Two bounded tails, one per stream: their order is lost, so the
|
|
568
|
+
// streams print apart.
|
|
569
|
+
body: { stdout?: string; stderr?: string },
|
|
524
570
|
dropped: { stdout?: number; stderr?: number } = {},
|
|
525
571
|
colors: ColorSupport = NO_COLOR,
|
|
526
572
|
forwardArgs: readonly string[] = [],
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
flakyNote,
|
|
12
12
|
formatTaskHitLine,
|
|
13
13
|
formatTaskSkippedLine,
|
|
14
|
+
type OutputChunk,
|
|
14
15
|
} from './framed-output.js'
|
|
15
16
|
import {
|
|
16
17
|
createOutputWriter,
|
|
@@ -210,8 +211,7 @@ export function defaultLogger(
|
|
|
210
211
|
// appending via `+=`; concatenating N small chunks via `+=` is O(N²)
|
|
211
212
|
// because each `+=` allocates a fresh string of the full accumulated
|
|
212
213
|
// length. Bun-friendly: join('') is a single contiguous allocation.
|
|
213
|
-
const
|
|
214
|
-
const stderrBuffers = new Map<string, string[]>()
|
|
214
|
+
const outputBuffers = new Map<string, OutputChunk[]>()
|
|
215
215
|
// Separator bookkeeping: blocks are blank-line-delimited on BOTH
|
|
216
216
|
// sides — raw (unprefixed) frame content must never collide with a
|
|
217
217
|
// neighbouring one-liner (owner feedback). A block leaves its own
|
|
@@ -222,16 +222,16 @@ export function defaultLogger(
|
|
|
222
222
|
// True while the live stream sits mid-line (chunk without trailing
|
|
223
223
|
// newline) — the frame close must not glue onto partial output.
|
|
224
224
|
let streamMidLine = false
|
|
225
|
-
const pushChunk = (
|
|
226
|
-
const arr =
|
|
227
|
-
if (arr) arr.push(
|
|
228
|
-
else
|
|
225
|
+
const pushChunk = (id: string, text: string, err: boolean): void => {
|
|
226
|
+
const arr = outputBuffers.get(id)
|
|
227
|
+
if (arr) arr.push({ text, err })
|
|
228
|
+
else outputBuffers.set(id, [{ text, err }])
|
|
229
229
|
}
|
|
230
|
-
const takeChunks = (
|
|
231
|
-
const arr =
|
|
232
|
-
if (!arr) return
|
|
233
|
-
|
|
234
|
-
return arr
|
|
230
|
+
const takeChunks = (id: string): OutputChunk[] => {
|
|
231
|
+
const arr = outputBuffers.get(id)
|
|
232
|
+
if (!arr) return []
|
|
233
|
+
outputBuffers.delete(id)
|
|
234
|
+
return arr
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
// `none` is the ONE mode whose contract guarantees the output is
|
|
@@ -682,7 +682,7 @@ export function defaultLogger(
|
|
|
682
682
|
else appendTail(tail.out, chunk)
|
|
683
683
|
return
|
|
684
684
|
}
|
|
685
|
-
pushChunk(
|
|
685
|
+
pushChunk(node.id, chunk, false)
|
|
686
686
|
},
|
|
687
687
|
taskStderr(node, chunk) {
|
|
688
688
|
if (discardsOutput) return
|
|
@@ -698,7 +698,7 @@ export function defaultLogger(
|
|
|
698
698
|
else appendTail(tail.err, chunk)
|
|
699
699
|
return
|
|
700
700
|
}
|
|
701
|
-
pushChunk(
|
|
701
|
+
pushChunk(node.id, chunk, true)
|
|
702
702
|
},
|
|
703
703
|
taskComplete(node, outcome) {
|
|
704
704
|
if (!statusDead) finished.add(node.id)
|
|
@@ -707,8 +707,14 @@ export function defaultLogger(
|
|
|
707
707
|
// there — carrying how much the cap dropped, since a truncated log
|
|
708
708
|
// that reads as complete is worse than one that says what it lost.
|
|
709
709
|
const preReady = persistentTails.get(node.id)
|
|
710
|
-
|
|
711
|
-
|
|
710
|
+
// In the order the task wrote it; a bounded tail kept its streams
|
|
711
|
+
// apart, so its stdout reads first.
|
|
712
|
+
const output: OutputChunk[] = preReady
|
|
713
|
+
? [
|
|
714
|
+
{ text: tailText(preReady.out), err: false },
|
|
715
|
+
{ text: tailText(preReady.err), err: true },
|
|
716
|
+
]
|
|
717
|
+
: takeChunks(node.id)
|
|
712
718
|
const dropped =
|
|
713
719
|
preReady && (preReady.out.dropped > 0 || preReady.err.dropped > 0)
|
|
714
720
|
? { droppedStdout: preReady.out.dropped, droppedStderr: preReady.err.dropped }
|
|
@@ -745,16 +751,9 @@ export function defaultLogger(
|
|
|
745
751
|
emitFrameClose(formatFrameClose(node, outcome, colors))
|
|
746
752
|
return
|
|
747
753
|
}
|
|
748
|
-
if (framed && (
|
|
754
|
+
if (framed && output.some((c) => c.text.length > 0)) {
|
|
749
755
|
emitBlock(
|
|
750
|
-
formatTaskBlock(
|
|
751
|
-
node,
|
|
752
|
-
outcome,
|
|
753
|
-
{ stdout, stderr, ...dropped },
|
|
754
|
-
colors,
|
|
755
|
-
true,
|
|
756
|
-
forwardArgs,
|
|
757
|
-
),
|
|
756
|
+
formatTaskBlock(node, outcome, { output, ...dropped }, colors, true, forwardArgs),
|
|
758
757
|
)
|
|
759
758
|
return
|
|
760
759
|
}
|
|
@@ -847,14 +846,7 @@ export function defaultLogger(
|
|
|
847
846
|
if (outcome.status !== 'failed') return
|
|
848
847
|
emitLine(failureRow(node, outcome))
|
|
849
848
|
deferredFailures.push(
|
|
850
|
-
formatTaskBlock(
|
|
851
|
-
node,
|
|
852
|
-
outcome,
|
|
853
|
-
{ stdout, stderr, ...dropped },
|
|
854
|
-
colors,
|
|
855
|
-
false,
|
|
856
|
-
forwardArgs,
|
|
857
|
-
),
|
|
849
|
+
formatTaskBlock(node, outcome, { output, ...dropped }, colors, false, forwardArgs),
|
|
858
850
|
)
|
|
859
851
|
return
|
|
860
852
|
case 'broad':
|
|
@@ -866,14 +858,7 @@ export function defaultLogger(
|
|
|
866
858
|
// ◼ row now; the full frame replays at runEnd.
|
|
867
859
|
emitLine(failureRow(node, outcome))
|
|
868
860
|
deferredFailures.push(
|
|
869
|
-
formatTaskBlock(
|
|
870
|
-
node,
|
|
871
|
-
outcome,
|
|
872
|
-
{ stdout, stderr, ...dropped },
|
|
873
|
-
colors,
|
|
874
|
-
false,
|
|
875
|
-
forwardArgs,
|
|
876
|
-
),
|
|
861
|
+
formatTaskBlock(node, outcome, { output, ...dropped }, colors, false, forwardArgs),
|
|
877
862
|
)
|
|
878
863
|
} else if (outcome.status === 'success') {
|
|
879
864
|
emitLine(formatTaskExecutedLine(node, outcome, colors))
|
|
@@ -910,28 +895,14 @@ export function defaultLogger(
|
|
|
910
895
|
if (outcome.status === 'failed') {
|
|
911
896
|
emitLine(failureRow(node, outcome))
|
|
912
897
|
deferredFailures.push(
|
|
913
|
-
formatTaskBlock(
|
|
914
|
-
node,
|
|
915
|
-
outcome,
|
|
916
|
-
{ stdout, stderr, ...dropped },
|
|
917
|
-
colors,
|
|
918
|
-
false,
|
|
919
|
-
forwardArgs,
|
|
920
|
-
),
|
|
898
|
+
formatTaskBlock(node, outcome, { output, ...dropped }, colors, false, forwardArgs),
|
|
921
899
|
)
|
|
922
900
|
return
|
|
923
901
|
}
|
|
924
902
|
// forceCommand: a requested task's frame shows `$ cmd`
|
|
925
903
|
// whether it ran or was cached — same frame every run.
|
|
926
904
|
emitBlock(
|
|
927
|
-
formatTaskBlock(
|
|
928
|
-
node,
|
|
929
|
-
outcome,
|
|
930
|
-
{ stdout, stderr, ...dropped },
|
|
931
|
-
colors,
|
|
932
|
-
true,
|
|
933
|
-
forwardArgs,
|
|
934
|
-
),
|
|
905
|
+
formatTaskBlock(node, outcome, { output, ...dropped }, colors, true, forwardArgs),
|
|
935
906
|
)
|
|
936
907
|
return
|
|
937
908
|
}
|
|
@@ -942,14 +913,7 @@ export function defaultLogger(
|
|
|
942
913
|
} else if (outcome.status === 'failed') {
|
|
943
914
|
emitLine(failureRow(node, outcome))
|
|
944
915
|
deferredFailures.push(
|
|
945
|
-
formatTaskBlock(
|
|
946
|
-
node,
|
|
947
|
-
outcome,
|
|
948
|
-
{ stdout, stderr, ...dropped },
|
|
949
|
-
colors,
|
|
950
|
-
false,
|
|
951
|
-
forwardArgs,
|
|
952
|
-
),
|
|
916
|
+
formatTaskBlock(node, outcome, { output, ...dropped }, colors, false, forwardArgs),
|
|
953
917
|
)
|
|
954
918
|
}
|
|
955
919
|
return
|
|
@@ -960,7 +924,7 @@ export function defaultLogger(
|
|
|
960
924
|
// replayed stdout keep their frame (the output is the
|
|
961
925
|
// point); misses/failures are always framed. One-liners
|
|
962
926
|
// stay outside ::group:: — there's nothing to collapse.
|
|
963
|
-
if (isHit &&
|
|
927
|
+
if (isHit && output.every((c) => c.text.trim().length === 0)) {
|
|
964
928
|
emitLine(formatTaskHitLine(node, outcome, colors))
|
|
965
929
|
return
|
|
966
930
|
}
|
|
@@ -971,7 +935,7 @@ export function defaultLogger(
|
|
|
971
935
|
const block = formatTaskBlock(
|
|
972
936
|
node,
|
|
973
937
|
outcome,
|
|
974
|
-
{
|
|
938
|
+
{ output, ...dropped },
|
|
975
939
|
colors,
|
|
976
940
|
false,
|
|
977
941
|
forwardArgs,
|