# ResultStreamer _(no submodules)_ ## Purpose `ResultStreamer` delivers sandbox output incrementally rather than as a single end-of-execution dump. The most common use case is long-running scripts that emit logs over many seconds: subscribers receive `StreamChunk`s as soon as the buffer either reaches `maxBufferSize` or the next `flushIntervalMs` tick fires, instead of waiting for the whole execution to finish. A final `complete` chunk carries the structured result envelope (success / duration / memoryUsed / error) so subscribers can tear down their UI state at the right moment. The streamer enforces a strict completion lifecycle: once `complete()` has fired, any further `pushChunk` raises `SandboxError(STREAM_CLOSED)`. This prevents post-completion writes from corrupting downstream state and surfaces programming errors immediately rather than letting them silently drop chunks. ## Features - Four chunk types: `log`, `result`, `error`, `complete`. - Per-chunk wall-clock timestamp and elapsed-since-start milliseconds. - Configurable `maxBufferSize` (auto-flush threshold) and `flushIntervalMs` (auto-flush cadence). - Subscribe / unsubscribe with returned disposer. - `complete` is idempotent (second call is a no-op). - Strict post-complete `STREAM_CLOSED` enforcement. ## Usage: Basic ```ts import { ResultStreamer } from "@suaveplan/sandbox/result-streamer"; const s = new ResultStreamer(); s.subscribe((c) => console.log(c.type, c.data)); s.start(); s.pushLog("processing..."); s.complete({ success: true, result: 42, error: undefined, duration: 100, memoryUsed: 0, logs: [] }); ``` ## Usage: Advanced Pipe stream chunks straight into a Server-Sent-Events response: ```ts import { ResultStreamer } from "@suaveplan/sandbox/result-streamer"; const s = new ResultStreamer({ flushIntervalMs: 50 }); s.subscribe((c) => { res.write(`event: ${c.type}\ndata: ${c.data}\n\n`); }); s.start(); // ...drive the sandbox, push chunks, await complete ``` ## API Reference - `new ResultStreamer(config?: Partial)`. - `start()`, `stop()`, `destroy()`. - `subscribe(listener): () => void`. - `pushLog(message)`, `pushError(error)`, `pushResult(data)`. - `complete(result: SandboxResult): void`. - `flush()`, `bufferSize()`, `isCompleted()`, `listenerCount()`. - `DEFAULT_STREAMER_CONFIG` — exported defaults. ## Implementation Notes `pushChunk` auto-flushes when the buffer reaches `maxBufferSize`, so callers that don't run the interval flusher (`flushIntervalMs: 0`) still see chunks land at subscribers without manual `flush()` calls — useful for tests that want deterministic delivery. The `elapsed` field is recorded against `startTime`; when the streamer is used without `start()`, `elapsed` carries the value relative to `performance.now() - 0`, which is the host's high-res timer origin — a slight oddity but a tolerable one because the documented contract is to call `start()` first.