> ## 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.

# Core SDK

> Framework-agnostic streaming protocol handler for Outkit

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:

```bash theme={null}
npm install -g @outkit-dev/cli@latest
outkit login
outkit init
```

To install the package manually:

```bash theme={null}
npm install @outkit-dev/core
```

<Note>
  If you're using React, install `@outkit-dev/react` instead — it wraps `@outkit-dev/core` with React state management automatically.
</Note>

## Quick Start

```typescript theme={null}
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

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
// WebSocket integration
ws.onmessage = (event) => {
  stream.feedEvent(event.data);
};
```

### Tier 4: `feedChunk` / `feedMeta` / `feedDone`

Full manual control:

```typescript theme={null}
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`.

```typescript theme={null}
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.

```typescript theme={null}
stream.destroy();
stream.streamState; // 'destroyed'
```

## Examples

### Vue 3 Composition API

```vue theme={null}
<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

```svelte theme={null}
<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

```javascript theme={null}
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)

```typescript theme={null}
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

```typescript theme={null}
// 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';
```
