Documentation Index
Fetch the complete documentation index at: https://docs.outkit.dev/llms.txt
Use this file to discover all available pages before exploring further.
The @outkit-dev/core package provides OutkitStream — a framework-agnostic streaming protocol handler. Use it in Vue, Svelte, vanilla JavaScript, Node.js, or any environment where @outkit-dev/react doesn’t fit.
Zero dependencies. 3 KB gzipped.
Install
The recommended way is the CLI — it detects Vue, Svelte, Astro, vanilla JS, or Node.js and
wires the right setup automatically:
npm install -g @outkit-dev/cli@latest
outkit login
outkit init
To install the package manually:
npm install @outkit-dev/core
If you’re using React, install @outkit-dev/react instead — it wraps @outkit-dev/core with React state management automatically.
Quick Start
import { OutkitStream } from '@outkit-dev/core';
const stream = new OutkitStream({
onBlocks: (blocks) => renderBlocks(blocks),
onDesign: (tokens) => applyDesignTokens(tokens),
onComplete: () => console.log('Stream complete'),
onError: (err) => console.error(err),
});
// Tier 1: One line — feed a fetch Response
const response = await fetch('/api/enhance/123');
await stream.feedResponse(response);
That’s it. feedResponse reads the SSE stream, extracts design tokens, parses blocks incrementally, and signals completion.
OutkitStream
Constructor
const stream = new OutkitStream(callbacks);
| Callback | Type | Description |
|---|
onBlocks | (blocks: ContentBlock[]) => void | Called whenever new blocks are parsed. Receives the full current array. |
onDesign | (tokens: Record<string, string>) => void | Called when design tokens arrive from a meta event. |
onComplete | () => void | Called when the stream finishes (either [DONE] or reader exhausted). |
onError | (error: Error) => void | Called on stream errors. Optional. |
onStateChange | (state: OutkitStreamState) => void | Called on every lifecycle transition. Optional. |
Properties
| Property | Type | Description |
|---|
isStreaming | boolean | Whether the stream is actively receiving data |
streamState | OutkitStreamState | Current lifecycle: 'idle' | 'streaming' | 'done' | 'error' | 'destroyed' |
Four-Tier Feed API
Pick the tier that matches your transport:
feedResponse(res) ← "Here's a fetch Response, you do everything"
↓ internally calls
feedSSE(rawText) ← "Here are raw bytes in SSE format"
↓ internally calls
feedEvent(data) ← "Here's one unwrapped event payload"
↓ internally calls
feedChunk / feedMeta / feedDone ← "I control everything"
Tier 1: feedResponse(response: Response)
Reads a fetch Response body as an SSE stream. Handles everything end-to-end.
const response = await fetch('/api/enhance/123');
const { blocks, design } = await stream.feedResponse(response);
console.log(`Rendered ${blocks.length} blocks`);
- Returns
Promise<{ blocks: ContentBlock[], design: Record<string, string> }> — the final state after stream completion
- Auto-resets if a previous stream is in progress
- Throws on non-ok HTTP responses (status >= 400)
- Abortable via
stream.reset() or stream.destroy()
Tier 2: feedSSE(rawText: string)
Feed raw SSE-formatted bytes. Handles \n\n splitting, data: prefix stripping, and fragment buffering.
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
stream.feedSSE(decoder.decode(value, { stream: true }));
}
Tier 3: feedEvent(data: string)
Feed one unwrapped SSE event payload (the value after data:). Handles [DONE] detection and meta event routing.
// WebSocket integration
ws.onmessage = (event) => {
stream.feedEvent(event.data);
};
Full manual control:
stream.feedChunk(rawLLMToken); // accumulate + parse
stream.feedMeta({ '--outkit-primary': '#b30069' }); // set design tokens
stream.feedDone(); // finalize
Lifecycle
idle → streaming → done
→ error
reset()
Returns to idle. Clears all accumulated state, buffered blocks, and design tokens. Aborts any in-flight feedResponse.
stream.reset();
// stream is now ready for a new feedResponse/feedSSE/etc.
destroy()
Permanently shuts down the instance. All feed methods become no-ops. Use in cleanup/unmount code.
stream.destroy();
stream.streamState; // 'destroyed'
Examples
Vue 3 Composition API
<script setup lang="ts">
import { ref, onUnmounted } from 'vue';
import { OutkitStream } from '@outkit-dev/core';
const blocks = ref([]);
const design = ref({});
const isStreaming = ref(false);
const stream = new OutkitStream({
onBlocks: (b) => { blocks.value = b; isStreaming.value = true; },
onDesign: (d) => { design.value = d; },
onComplete: () => { isStreaming.value = false; },
});
onUnmounted(() => stream.destroy());
async function enhance(messageId: string) {
stream.reset();
blocks.value = [];
const response = await fetch(`/api/enhance/${messageId}`);
await stream.feedResponse(response);
}
</script>
Svelte
<script>
import { OutkitStream } from '@outkit-dev/core';
import { onDestroy } from 'svelte';
let blocks = $state([]);
let design = $state({});
let isStreaming = $state(false);
const stream = new OutkitStream({
onBlocks: (b) => { blocks = b; isStreaming = true; },
onDesign: (d) => { design = d; },
onComplete: () => { isStreaming = false; },
});
onDestroy(() => stream.destroy());
async function enhance(messageId) {
stream.reset();
blocks = [];
const response = await fetch(`/api/enhance/${messageId}`);
await stream.feedResponse(response);
}
</script>
Vanilla JavaScript
import { OutkitStream } from '@outkit-dev/core';
const container = document.getElementById('output');
const stream = new OutkitStream({
onBlocks: (blocks) => {
container.innerHTML = blocks
.map(block => `<div>${JSON.stringify(block)}</div>`)
.join('');
},
onDesign: (tokens) => {
for (const [key, value] of Object.entries(tokens)) {
container.style.setProperty(key, value);
}
},
onComplete: () => console.log('Done'),
});
document.getElementById('btn').onclick = async () => {
stream.reset();
const response = await fetch('/api/enhance/123');
await stream.feedResponse(response);
};
Node.js (Server-Side Processing)
import { OutkitStream } from '@outkit-dev/core';
const blocks = [];
let design = {};
const stream = new OutkitStream({
onBlocks: (b) => { blocks.length = 0; blocks.push(...b); },
onDesign: (d) => { design = d; },
onComplete: () => {
console.log(`Received ${blocks.length} blocks`);
console.log('Design tokens:', design);
},
});
const response = await fetch('https://api.outkit.dev/render/enhance', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-outkit-api-key': process.env.OUTKIT_API_KEY,
},
body: JSON.stringify({ content: aiText }),
});
await stream.feedResponse(response);
stream.destroy();
Exports
// Class
import { OutkitStream } from '@outkit-dev/core';
// Types
import type {
OutkitStreamCallbacks,
OutkitStreamResult,
OutkitStreamState,
ContentBlock,
ComponentBlock,
SkeletonBlock,
} from '@outkit-dev/core';
// Streaming utilities
import {
parseStreamingBlocks,
completePartialJson,
} from '@outkit-dev/core';