Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const alias = {
'devframe/utils/promise': df('devframe/src/utils/promise.ts'),
'devframe/utils/shared-state': df('devframe/src/utils/shared-state.ts'),
'devframe/utils/state': df('devframe/src/utils/state.ts'),
'devframe/utils/streaming-channel': df('devframe/src/utils/streaming-channel.ts'),
'devframe/utils/when': df('devframe/src/utils/when.ts'),
'devframe/adapters/cli': df('devframe/src/adapters/cli.ts'),
'devframe/adapters/dev': df('devframe/src/adapters/dev.ts'),
Expand Down
3 changes: 2 additions & 1 deletion devframe/docs/.vitepress/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default function devframeSidebar(prefix = ''): DefaultTheme.SidebarItem[]
{ text: 'Adapters', link: `${prefix}/guide/adapters` },
{ text: 'RPC', link: `${prefix}/guide/rpc` },
{ text: 'Shared State', link: `${prefix}/guide/shared-state` },
{ text: 'Streaming', link: `${prefix}/guide/streaming` },
{ text: 'Dock System', link: `${prefix}/guide/dock-system` },
{ text: 'Commands', link: `${prefix}/guide/commands` },
{ text: 'When Clauses', link: `${prefix}/guide/when-clauses` },
Expand All @@ -26,7 +27,7 @@ export default function devframeSidebar(prefix = ''): DefaultTheme.SidebarItem[]
text: 'Error Reference',
link: `${prefix}/errors/`,
collapsed: true,
items: Array.from({ length: 28 }, (_, i) => {
items: Array.from({ length: 32 }, (_, i) => {
const code = `DF${String(i + 1).padStart(4, '0')}`
return { text: code, link: `${prefix}/errors/${code}` }
}),
Expand Down
27 changes: 27 additions & 0 deletions devframe/docs/errors/DF0029.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
outline: deep
---

# DF0029: Stream Buffer Overflow

> Package: `devframe`

## Message

> Stream "`{channel}#{id}`" dropped `{dropped}` chunk(s) after exceeding the client high-water mark.

## Cause

A streaming subscriber's queue grew past its `highWaterMark` because the consumer is slower than the producer. The oldest chunks were dropped to keep memory bounded.

This is a soft warning — the stream keeps running and remaining chunks still flow.

## Fix

- Raise `highWaterMark` on `rpc.streaming.subscribe(channel, id, { highWaterMark })` if the consumer can occasionally catch up.
- Slow the producer so it doesn't outpace the wire (e.g. throttle, debounce, or batch chunks server-side).
- Switch to `sharedState` if you only need the latest value rather than every intermediate chunk.

## Source

`packages/devframe/src/client/rpc-streaming.ts`
25 changes: 25 additions & 0 deletions devframe/docs/errors/DF0030.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
outline: deep
---

# DF0030: Unknown Stream ID

> Package: `devframe`

## Message

> Stream "`{channel}#{id}`" is unknown — no producer has called `channel.start({ id: "{id}" })`.

## Cause

A client subscribed to a stream id that the server-side channel doesn't know about. Either the producer never started a stream with that id, the producer already ended it and `replayWindow` is `0`, or the client passed the wrong id.

## Fix

- Make sure the action that returns the stream id runs **before** the client subscribes — typically by awaiting `rpc.call('your-action')` and using the returned id.
- Bump `replayWindow` on `ctx.rpc.streaming.create(name, { replayWindow })` if you need clients to resume after the producer has finished but kept the buffer warm.
- Check the id is propagated correctly across boundaries (action return value → component prop → subscribe call).

## Source

`packages/devframe/src/node/rpc-streaming.ts`
38 changes: 38 additions & 0 deletions devframe/docs/errors/DF0031.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
outline: deep
---

# DF0031: Write to Closed Stream

> Package: `devframe`

## Message

> Cannot write to closed stream "`{channel}#{id}`".

## Cause

`stream.write(chunk)` was called after the stream was closed via `stream.close()` / `stream.error()` or after the consumer cancelled (which aborts `stream.signal`).

## Fix

Producers should poll `stream.signal.aborted` and exit cleanly:

```ts
const stream = channel.start({ id })
try {
for (const chunk of source) {
if (stream.signal.aborted)
return
stream.write(chunk)
}
stream.close()
}
catch (err) {
stream.error(err)
}
```

## Source

`packages/devframe/src/utils/streaming-channel.ts`
24 changes: 24 additions & 0 deletions devframe/docs/errors/DF0032.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
outline: deep
---

# DF0032: Streaming Channel Already Registered

> Package: `devframe`

## Message

> Streaming channel "`{channel}`" is already registered.

## Cause

Two calls to `ctx.rpc.streaming.create(name, ...)` used the same channel name. Each name owns a wire namespace and must be unique within a context.

## Fix

- Reuse the existing channel handle rather than creating a new one with the same name.
- If two devtools want isolated streams, give each a distinct namespaced name (`my-devtool:chat-stream`, `other-devtool:logs-stream`).

## Source

`packages/devframe/src/node/rpc-streaming.ts`
4 changes: 4 additions & 0 deletions devframe/docs/errors/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ Emitted by `devframe` — framework-neutral host / shared-state / auth surface.
| [DF0026](./DF0026) | error | No Dump Match | DTK0006 |
| [DF0027](./DF0027) | error | Invalid Dump Configuration | DTK0007 |
| [DF0028](./DF0028) | error | Snapshot Type Mismatch | DTK0008 |
| [DF0029](./DF0029) | warn | Stream Buffer Overflow | — |
| [DF0030](./DF0030) | error | Unknown Stream ID | — |
| [DF0031](./DF0031) | error | Write to Closed Stream | — |
| [DF0032](./DF0032) | error | Streaming Channel Already Registered | — |
14 changes: 14 additions & 0 deletions devframe/docs/guide/rpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,20 @@ defineDevtool({
| `event` | `boolean` | Fire-and-forget (don't wait for responses). |
| `filter` | `(client) => boolean` | Skip specific clients. |

## Streaming

For chunk-style server→client feeds (chat deltas, log lines, build progress), reach for [streaming channels](./streaming) instead of hand-rolling `action + delta/end events`. The streaming API gives you stream IDs, cancellation, replay, and Web Streams interop for free:

```ts
const channel = ctx.rpc.streaming.create<string>('my-devtool:chat', {
replayWindow: 256,
})
const stream = channel.start()
sourceReadable.pipeTo(stream.writable)
```

See the [Streaming guide](./streaming) for the full API.

## Local Invocation

`ctx.rpc.invokeLocal` calls a registered server function directly without going through a transport — useful for cross-function composition on the server side:
Expand Down
Loading
Loading