first commit
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
# ResultStreamer
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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<ResultStreamerConfig>)`.
|
||||
- `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.
|
||||
Reference in New Issue
Block a user