@vzn/vx 0.0.0 → 0.0.14
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/LICENSE +21 -0
- package/README.md +349 -1
- package/launcher.mjs +92 -0
- package/package.json +44 -3
- package/src/bin.ts +25 -0
- package/src/cache/cache.ts +1832 -0
- package/src/cache/cas-backend.ts +97 -0
- package/src/cache/digest.ts +47 -0
- package/src/cache/index.ts +28 -0
- package/src/cache/inputs.ts +839 -0
- package/src/cache/layered-cache.ts +355 -0
- package/src/cache/tar.ts +343 -0
- package/src/cli/backend.ts +23 -0
- package/src/cli/cache.ts +96 -0
- package/src/cli/format.ts +15 -0
- package/src/cli/help.ts +129 -0
- package/src/cli/index.ts +85 -0
- package/src/cli/info.ts +80 -0
- package/src/cli/lock.ts +135 -0
- package/src/cli/mcp-rpc.ts +292 -0
- package/src/cli/mcp.ts +92 -0
- package/src/cli/migrate-nx.ts +450 -0
- package/src/cli/migrate-turbo.ts +364 -0
- package/src/cli/migrate.ts +219 -0
- package/src/cli/plan-format.ts +148 -0
- package/src/cli/run.ts +683 -0
- package/src/cli/show.ts +201 -0
- package/src/cli/upgrade.ts +103 -0
- package/src/cli/watch.ts +287 -0
- package/src/config.ts +460 -0
- package/src/exec/env.ts +74 -0
- package/src/exec/index.ts +13 -0
- package/src/exec/runner.ts +470 -0
- package/src/exec/sandbox-runtime.ts +578 -0
- package/src/graph/dependency-spec.ts +85 -0
- package/src/graph/index.ts +27 -0
- package/src/graph/scheduler.ts +677 -0
- package/src/graph/task-graph.ts +377 -0
- package/src/index.ts +222 -0
- package/src/orchestrator/colors.ts +63 -0
- package/src/orchestrator/devframe-surface.ts +74 -0
- package/src/orchestrator/events.ts +277 -0
- package/src/orchestrator/execute-task.ts +939 -0
- package/src/orchestrator/framed-output.ts +461 -0
- package/src/orchestrator/history.ts +148 -0
- package/src/orchestrator/index.ts +174 -0
- package/src/orchestrator/local-shortcircuit.ts +147 -0
- package/src/orchestrator/logger.ts +526 -0
- package/src/orchestrator/metrics.ts +1927 -0
- package/src/orchestrator/options.ts +197 -0
- package/src/orchestrator/plan.ts +129 -0
- package/src/orchestrator/plugin-host.ts +185 -0
- package/src/orchestrator/plugin.ts +283 -0
- package/src/orchestrator/predict.ts +106 -0
- package/src/orchestrator/prepare.ts +306 -0
- package/src/orchestrator/protocol.ts +156 -0
- package/src/orchestrator/remote-prefetch.ts +81 -0
- package/src/orchestrator/resources.ts +59 -0
- package/src/orchestrator/run-artifacts.ts +111 -0
- package/src/orchestrator/run-context.ts +205 -0
- package/src/orchestrator/run-report.ts +132 -0
- package/src/orchestrator/run-state.ts +145 -0
- package/src/orchestrator/run.ts +887 -0
- package/src/orchestrator/stable-keys.ts +177 -0
- package/src/orchestrator/status-line.ts +287 -0
- package/src/orchestrator/summary.ts +315 -0
- package/src/orchestrator/tally.ts +69 -0
- package/src/orchestrator/task-hash.ts +253 -0
- package/src/orchestrator/telemetry-host.ts +80 -0
- package/src/orchestrator/telemetry.ts +358 -0
- package/src/orchestrator/upstream.ts +121 -0
- package/src/orchestrator/verify.ts +164 -0
- package/src/orchestrator/wire-render.ts +92 -0
- package/src/orchestrator/wire.ts +212 -0
- package/src/util/errors.ts +11 -0
- package/src/util/hash.ts +25 -0
- package/src/util/index.ts +8 -0
- package/src/util/paths.ts +16 -0
- package/src/util/size.ts +16 -0
- package/src/util/ulid.ts +15 -0
- package/src/version.ts +10 -0
- package/src/workspace/affected.ts +118 -0
- package/src/workspace/filter.ts +155 -0
- package/src/workspace/fingerprint.ts +41 -0
- package/src/workspace/index.ts +27 -0
- package/src/workspace/lockfile.ts +118 -0
- package/src/workspace/nested-dirs.ts +34 -0
- package/src/workspace/package-graph.ts +148 -0
- package/src/workspace/project-loader.ts +512 -0
- package/src/workspace/workspace.ts +176 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// The devframe surface for a vx run: one definition that exposes the run
|
|
2
|
+
// as a live streaming channel (`vx:events`, the raw WireEvent feed) plus a
|
|
3
|
+
// patch-synced shared state (`vx:run`, the reduced RunState aggregate).
|
|
4
|
+
// A host adapter (dev server / MCP — see cli wiring) mounts this same
|
|
5
|
+
// definition; this module owns only the translation from our in-process
|
|
6
|
+
// event bus into devframe's streaming + shared-state primitives.
|
|
7
|
+
//
|
|
8
|
+
// devframe is touched ONLY through type-only imports here, so core `vx`
|
|
9
|
+
// never gains a runtime dependency on it — the host that actually boots a
|
|
10
|
+
// devframe server imports the runtime adapter dynamically and is the
|
|
11
|
+
// optional, opt-in path. See docs/design/event-stream-2026-06.md.
|
|
12
|
+
|
|
13
|
+
import type { DevframeDefinition, DevframeNodeContext, RpcStreamingChannel } from 'devframe'
|
|
14
|
+
import { VERSION } from '../version.js'
|
|
15
|
+
import { toWireEvent, type EventBus, type WireEvent } from './events.js'
|
|
16
|
+
import { initRunState, reduce, type RunState } from './run-state.js'
|
|
17
|
+
|
|
18
|
+
// Type the `vx:run` shared-state key so `ctx.rpc.sharedState.get` is
|
|
19
|
+
// checked against our aggregate shape.
|
|
20
|
+
declare module 'devframe' {
|
|
21
|
+
interface DevframeRpcSharedStates {
|
|
22
|
+
'vx:run': RunState
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface VxSurfaceOptions {
|
|
27
|
+
/** Stream id for the run's event feed. One run → one stream. */
|
|
28
|
+
streamId?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Build a devframe definition that mirrors a single run's bus onto
|
|
33
|
+
* devframe's wire. `setup` runs at host boot (before the run emits), so
|
|
34
|
+
* it subscribes to the bus, forwards every event onto the `vx:events`
|
|
35
|
+
* stream, and folds it into the `vx:run` shared state. The host syncs
|
|
36
|
+
* both to any connected client (web SPA, MCP resource).
|
|
37
|
+
*/
|
|
38
|
+
export function createVxSurface(bus: EventBus, options: VxSurfaceOptions = {}): DevframeDefinition {
|
|
39
|
+
const streamId = options.streamId ?? 'run'
|
|
40
|
+
return {
|
|
41
|
+
id: 'vx',
|
|
42
|
+
name: 'vx',
|
|
43
|
+
version: VERSION,
|
|
44
|
+
async setup(ctx: DevframeNodeContext) {
|
|
45
|
+
const channel: RpcStreamingChannel<WireEvent> = ctx.rpc.streaming.create('vx:events')
|
|
46
|
+
const stream = channel.start({ id: streamId })
|
|
47
|
+
const state = await ctx.rpc.sharedState.get('vx:run', { initialValue: initRunState() })
|
|
48
|
+
|
|
49
|
+
// Local snapshot fed to `reduce`; the shared state is updated by
|
|
50
|
+
// assigning the new snapshot onto its draft (pure reduce → diffable
|
|
51
|
+
// patch). Kept beside the shared state rather than read back from it
|
|
52
|
+
// so reduction never depends on devframe's immutable accessors.
|
|
53
|
+
let snapshot = initRunState()
|
|
54
|
+
// `run()` emits `run:end` more than once (the normal end + a `finally`
|
|
55
|
+
// safety call) plus summary `run:status` lines AFTER the first
|
|
56
|
+
// `run:end`. Once the stream is closed, ignore everything — writing
|
|
57
|
+
// to a closed stream throws (StreamClosedError), and the run is over.
|
|
58
|
+
let closed = false
|
|
59
|
+
|
|
60
|
+
bus.subscribe((event) => {
|
|
61
|
+
if (closed) return
|
|
62
|
+
stream.write(toWireEvent(event))
|
|
63
|
+
snapshot = reduce(snapshot, event)
|
|
64
|
+
state.mutate((draft) => {
|
|
65
|
+
Object.assign(draft, snapshot)
|
|
66
|
+
})
|
|
67
|
+
if (event.kind === 'run:end') {
|
|
68
|
+
stream.close()
|
|
69
|
+
closed = true
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// The run event stream — the substrate every output surface subscribes
|
|
2
|
+
// to. The orchestrator no longer calls a Logger directly; it emits
|
|
3
|
+
// RunEvents through a bus, and the terminal renderer (or a custom
|
|
4
|
+
// embedder logger) is just the default, always-on, in-process
|
|
5
|
+
// subscriber. Off-thread surfaces (web devtool, TUI, MCP) attach as
|
|
6
|
+
// additional subscribers later, so a slow/wedged renderer can never
|
|
7
|
+
// stall task exec. See docs/design/event-stream-2026-06.md.
|
|
8
|
+
|
|
9
|
+
import type { TaskNode, TaskOutcome } from '../graph/index.js'
|
|
10
|
+
import { isGroupTask } from '../graph/index.js'
|
|
11
|
+
import type { Logger } from './logger.js'
|
|
12
|
+
import type { RunContext } from './summary.js'
|
|
13
|
+
|
|
14
|
+
/** Payload of the `run:start` event — mirrors the Logger.runStart hook. */
|
|
15
|
+
export interface RunStartInfo {
|
|
16
|
+
total: number
|
|
17
|
+
concurrency?: number
|
|
18
|
+
requestedCount?: number
|
|
19
|
+
/** Run banner context so a live footer can match the final summary. */
|
|
20
|
+
context?: RunContext
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The in-process event vocabulary. Carries LIVE `TaskNode` / `TaskOutcome`
|
|
25
|
+
* refs — zero-copy, because the default terminal subscriber runs on the
|
|
26
|
+
* same thread and reads those objects directly. Crossing a thread or RPC
|
|
27
|
+
* needs the serializable WireEvent form (see `projectNode` / `toWireEvent`
|
|
28
|
+
* below); that mapping is the boundary's job, not the producer's.
|
|
29
|
+
*/
|
|
30
|
+
export type RunEvent =
|
|
31
|
+
| { kind: 'run:start'; info: RunStartInfo }
|
|
32
|
+
| { kind: 'task:start'; node: TaskNode }
|
|
33
|
+
| { kind: 'task:stdout'; node: TaskNode; chunk: string }
|
|
34
|
+
| { kind: 'task:stderr'; node: TaskNode; chunk: string }
|
|
35
|
+
| { kind: 'task:complete'; node: TaskNode; outcome: TaskOutcome }
|
|
36
|
+
| { kind: 'run:status'; line: string }
|
|
37
|
+
| { kind: 'run:end' }
|
|
38
|
+
|
|
39
|
+
export type RunEventSubscriber = (event: RunEvent) => void
|
|
40
|
+
|
|
41
|
+
export interface EventBus {
|
|
42
|
+
emit(event: RunEvent): void
|
|
43
|
+
/** Register a subscriber; returns a disposer that removes it. */
|
|
44
|
+
subscribe(subscriber: RunEventSubscriber): () => void
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function createEventBus(): EventBus {
|
|
48
|
+
const subscribers: RunEventSubscriber[] = []
|
|
49
|
+
return {
|
|
50
|
+
emit(event) {
|
|
51
|
+
// Synchronous, in-subscription-order fan-out. Ordering is part of
|
|
52
|
+
// the terminal renderer's contract (a stdout chunk must reach it
|
|
53
|
+
// before the task's completion block; block-separator bookkeeping
|
|
54
|
+
// depends on it), so emit must never reorder or defer.
|
|
55
|
+
//
|
|
56
|
+
// A throwing subscriber must NEVER break the run — the whole point
|
|
57
|
+
// of the bus is that a surface (the devframe dev server, a future
|
|
58
|
+
// TUI) is isolated from execution. We swallow per-subscriber so one
|
|
59
|
+
// bad surface can't crash the user's build (mirrors the deleted
|
|
60
|
+
// Observer's makeSafeObserver contract). The terminal renderer is
|
|
61
|
+
// trusted and output-tested, so a real renderer bug still surfaces
|
|
62
|
+
// as wrong output, not a silent hang.
|
|
63
|
+
for (const subscriber of subscribers) {
|
|
64
|
+
try {
|
|
65
|
+
subscriber(event)
|
|
66
|
+
} catch {
|
|
67
|
+
// isolate: a surface fault can't propagate into the orchestrator
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
subscribe(subscriber) {
|
|
72
|
+
subscribers.push(subscriber)
|
|
73
|
+
return () => {
|
|
74
|
+
const i = subscribers.indexOf(subscriber)
|
|
75
|
+
if (i >= 0) subscribers.splice(i, 1)
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* A `Logger`-shaped facade that turns every call into a RunEvent. The
|
|
83
|
+
* orchestrator holds one of these as its `log`, so every existing
|
|
84
|
+
* `log.X(...)` call site emits through the bus unchanged — the concrete
|
|
85
|
+
* renderer subscribes via `terminalSubscriber`.
|
|
86
|
+
*/
|
|
87
|
+
export function busLogger(bus: EventBus): Logger {
|
|
88
|
+
return {
|
|
89
|
+
status: (line) => bus.emit({ kind: 'run:status', line }),
|
|
90
|
+
taskStdout: (node, chunk) => bus.emit({ kind: 'task:stdout', node, chunk }),
|
|
91
|
+
taskStderr: (node, chunk) => bus.emit({ kind: 'task:stderr', node, chunk }),
|
|
92
|
+
taskComplete: (node, outcome) => bus.emit({ kind: 'task:complete', node, outcome }),
|
|
93
|
+
runStart: (info) => bus.emit({ kind: 'run:start', info }),
|
|
94
|
+
taskStart: (node) => bus.emit({ kind: 'task:start', node }),
|
|
95
|
+
runEnd: () => bus.emit({ kind: 'run:end' }),
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Translate events back into `Logger` calls on a concrete renderer (the
|
|
101
|
+
* default terminal logger, or a custom embedder logger). This IS the
|
|
102
|
+
* in-process terminal surface: synchronous, full-fidelity, always-on.
|
|
103
|
+
* Output is byte-identical to calling the renderer directly because the
|
|
104
|
+
* bus fan-out is synchronous and order-preserving.
|
|
105
|
+
*/
|
|
106
|
+
export function terminalSubscriber(sink: Logger): RunEventSubscriber {
|
|
107
|
+
return (event) => {
|
|
108
|
+
switch (event.kind) {
|
|
109
|
+
case 'run:start':
|
|
110
|
+
sink.runStart?.(event.info)
|
|
111
|
+
return
|
|
112
|
+
case 'task:start':
|
|
113
|
+
sink.taskStart?.(event.node)
|
|
114
|
+
return
|
|
115
|
+
case 'task:stdout':
|
|
116
|
+
sink.taskStdout(event.node, event.chunk)
|
|
117
|
+
return
|
|
118
|
+
case 'task:stderr':
|
|
119
|
+
sink.taskStderr(event.node, event.chunk)
|
|
120
|
+
return
|
|
121
|
+
case 'task:complete':
|
|
122
|
+
sink.taskComplete(event.node, event.outcome)
|
|
123
|
+
return
|
|
124
|
+
case 'run:status':
|
|
125
|
+
sink.status(event.line)
|
|
126
|
+
return
|
|
127
|
+
case 'run:end':
|
|
128
|
+
sink.runEnd?.()
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* A bus subscriber that projects each event to its `WireEvent` and hands
|
|
136
|
+
* it to `send` — the shared run→consumer forwarding path (the `vx dev`
|
|
137
|
+
* hub frames each as NDJSON; the `vx serve` protocol wraps each in an
|
|
138
|
+
* envelope). Group-task start/complete events are dropped (no command,
|
|
139
|
+
* pure scheduling noise). `run()` emits run:end twice (normal + finally)
|
|
140
|
+
* plus trailing summary status lines; we forward the run once and then go
|
|
141
|
+
* quiet, so a consumer sees exactly one terminal frame per run. `send`
|
|
142
|
+
* must be fire-and-forget and tolerant of its own failures (a dead
|
|
143
|
+
* consumer can never break the run); the bus already isolates throws.
|
|
144
|
+
*/
|
|
145
|
+
export function wireForwarder(send: (event: WireEvent) => void): RunEventSubscriber {
|
|
146
|
+
// run() emits run:end TWICE (normal + finally), with the summary footer
|
|
147
|
+
// (run:status lines) emitted in BETWEEN. Drop the duplicate run:end, but
|
|
148
|
+
// keep forwarding everything else — including those post-run:end status
|
|
149
|
+
// lines, so a consumer that renders them (the serve client) gets the
|
|
150
|
+
// footer. A consumer that ignores run:status (the dev hub) is unaffected.
|
|
151
|
+
let endForwarded = false
|
|
152
|
+
return (event) => {
|
|
153
|
+
if (event.kind === 'run:end') {
|
|
154
|
+
if (endForwarded) return
|
|
155
|
+
endForwarded = true
|
|
156
|
+
} else if (
|
|
157
|
+
(event.kind === 'task:start' || event.kind === 'task:complete') &&
|
|
158
|
+
isGroupTask(event.node)
|
|
159
|
+
) {
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
send(toWireEvent(event))
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// --- Serializable projections (the off-thread / devframe wire form) ----
|
|
167
|
+
//
|
|
168
|
+
// The in-process bus carries live refs; crossing a worker postMessage or
|
|
169
|
+
// an RPC needs a JSON/structured-clone-safe form. Two concrete blockers
|
|
170
|
+
// make the raw objects un-sendable:
|
|
171
|
+
// 1. `TaskOutcome.wallclockStartNs/EndNs` are bigint — JSON.stringify
|
|
172
|
+
// THROWS on a bigint.
|
|
173
|
+
// 2. `TaskOutcome.node` back-references the whole TaskNode graph (its
|
|
174
|
+
// config + dep nodes), so every event would drag the graph across
|
|
175
|
+
// the boundary.
|
|
176
|
+
// The projection fixes both: ids instead of the node graph, decimal
|
|
177
|
+
// strings instead of bigint. Consumers on the far side rebuild their
|
|
178
|
+
// view-model from the one-time task table in `run:start`.
|
|
179
|
+
|
|
180
|
+
/** Display projection of a TaskNode — exactly the fields renderers read. */
|
|
181
|
+
export interface TaskView {
|
|
182
|
+
id: string
|
|
183
|
+
project: string
|
|
184
|
+
task: string
|
|
185
|
+
isGroup: boolean
|
|
186
|
+
requested: boolean
|
|
187
|
+
surfaced: boolean
|
|
188
|
+
persistent: boolean
|
|
189
|
+
command?: string
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Serializable projection of a TaskOutcome (no node ref, ns as strings). */
|
|
193
|
+
export interface OutcomeView {
|
|
194
|
+
taskId: string
|
|
195
|
+
status: TaskOutcome['status']
|
|
196
|
+
exitCode: number
|
|
197
|
+
durationMs: number
|
|
198
|
+
hash?: string
|
|
199
|
+
cpuMs?: number
|
|
200
|
+
peakRssBytes?: number
|
|
201
|
+
restored?: boolean
|
|
202
|
+
sandboxViolations?: number
|
|
203
|
+
sandboxViolationLines?: string[]
|
|
204
|
+
/** bigint hrtime ns encoded as a decimal string (JSON/RPC-safe). */
|
|
205
|
+
wallclockStartNs?: string
|
|
206
|
+
wallclockEndNs?: string
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function projectNode(node: TaskNode): TaskView {
|
|
210
|
+
const view: TaskView = {
|
|
211
|
+
id: node.id,
|
|
212
|
+
project: node.projectName,
|
|
213
|
+
task: node.taskName,
|
|
214
|
+
isGroup: isGroupTask(node),
|
|
215
|
+
requested: node.requested,
|
|
216
|
+
surfaced: node.surfaced === true,
|
|
217
|
+
persistent: node.config.exec?.persistent !== undefined,
|
|
218
|
+
}
|
|
219
|
+
if (node.config.exec?.command !== undefined) view.command = node.config.exec.command
|
|
220
|
+
return view
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function projectOutcome(outcome: TaskOutcome): OutcomeView {
|
|
224
|
+
const view: OutcomeView = {
|
|
225
|
+
taskId: outcome.node.id,
|
|
226
|
+
status: outcome.status,
|
|
227
|
+
exitCode: outcome.exitCode,
|
|
228
|
+
durationMs: outcome.durationMs,
|
|
229
|
+
}
|
|
230
|
+
if (outcome.hash !== undefined) view.hash = outcome.hash
|
|
231
|
+
if (outcome.cpuMs !== undefined) view.cpuMs = outcome.cpuMs
|
|
232
|
+
if (outcome.peakRssBytes !== undefined) view.peakRssBytes = outcome.peakRssBytes
|
|
233
|
+
if (outcome.restored !== undefined) view.restored = outcome.restored
|
|
234
|
+
if (outcome.sandboxViolations !== undefined) view.sandboxViolations = outcome.sandboxViolations
|
|
235
|
+
if (outcome.sandboxViolationLines !== undefined)
|
|
236
|
+
view.sandboxViolationLines = outcome.sandboxViolationLines
|
|
237
|
+
if (outcome.wallclockStartNs !== undefined)
|
|
238
|
+
view.wallclockStartNs = outcome.wallclockStartNs.toString()
|
|
239
|
+
if (outcome.wallclockEndNs !== undefined) view.wallclockEndNs = outcome.wallclockEndNs.toString()
|
|
240
|
+
return view
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* The serializable form of a RunEvent — JSON / structured-clone safe, the
|
|
245
|
+
* wire contract a non-local consumer (devframe, `vx serve` client) reads.
|
|
246
|
+
* `task:start` carries the full `TaskView` so a consumer can rebuild a
|
|
247
|
+
* node-shaped object incrementally (no upfront table needed); later
|
|
248
|
+
* task events reference the id, which the consumer already holds.
|
|
249
|
+
*/
|
|
250
|
+
export type WireEvent =
|
|
251
|
+
| { kind: 'run:start'; info: RunStartInfo }
|
|
252
|
+
| { kind: 'task:start'; task: TaskView }
|
|
253
|
+
| { kind: 'task:stdout'; taskId: string; chunk: string }
|
|
254
|
+
| { kind: 'task:stderr'; taskId: string; chunk: string }
|
|
255
|
+
| { kind: 'task:complete'; outcome: OutcomeView }
|
|
256
|
+
| { kind: 'run:status'; line: string }
|
|
257
|
+
| { kind: 'run:end' }
|
|
258
|
+
|
|
259
|
+
/** Map an in-process event to its serializable wire form. */
|
|
260
|
+
export function toWireEvent(event: RunEvent): WireEvent {
|
|
261
|
+
switch (event.kind) {
|
|
262
|
+
case 'run:start':
|
|
263
|
+
return { kind: 'run:start', info: event.info }
|
|
264
|
+
case 'task:start':
|
|
265
|
+
return { kind: 'task:start', task: projectNode(event.node) }
|
|
266
|
+
case 'task:stdout':
|
|
267
|
+
return { kind: 'task:stdout', taskId: event.node.id, chunk: event.chunk }
|
|
268
|
+
case 'task:stderr':
|
|
269
|
+
return { kind: 'task:stderr', taskId: event.node.id, chunk: event.chunk }
|
|
270
|
+
case 'task:complete':
|
|
271
|
+
return { kind: 'task:complete', outcome: projectOutcome(event.outcome) }
|
|
272
|
+
case 'run:status':
|
|
273
|
+
return { kind: 'run:status', line: event.line }
|
|
274
|
+
case 'run:end':
|
|
275
|
+
return { kind: 'run:end' }
|
|
276
|
+
}
|
|
277
|
+
}
|