void 0.7.2 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,187 @@
1
+ ---
2
+ outline: deep
3
+ ---
4
+
5
+ # Server-Sent Events
6
+
7
+ Void provides `void/sse` for producing and consuming Server-Sent Events from ordinary route handlers. It handles event formatting, response headers, keepalives, stream closure, request aborts, and browser `EventSource` parsing.
8
+
9
+ Use SSE when one HTTP request owns the producer: AI token streaming, progress updates, command output, deployment logs, or incremental status messages. Use `void/live` or an application-level Durable Object when multiple requests need shared fanout, replay, or subscription state.
10
+
11
+ ## Server streams
12
+
13
+ Return `eventStream()` from a route handler:
14
+
15
+ ```ts
16
+ // routes/api/events.ts
17
+ import { defineHandler } from 'void';
18
+ import { eventStream } from 'void/sse';
19
+
20
+ export const GET = defineHandler((c) => {
21
+ return eventStream(
22
+ async (stream) => {
23
+ await stream.comment('connected');
24
+ await stream.send({
25
+ event: 'ready',
26
+ data: { now: Date.now() },
27
+ });
28
+
29
+ const timer = setInterval(() => {
30
+ void stream.send({ event: 'tick', data: { now: Date.now() } }).catch(() => stream.close());
31
+ }, 1000);
32
+
33
+ await stream.closed;
34
+ clearInterval(timer);
35
+ },
36
+ {
37
+ signal: c.req.raw.signal,
38
+ },
39
+ );
40
+ });
41
+ ```
42
+
43
+ `eventStream()` returns a `Response` immediately with these default headers:
44
+
45
+ ```http
46
+ Content-Type: text/event-stream; charset=utf-8
47
+ Cache-Control: no-cache, no-transform
48
+ X-Accel-Buffering: no
49
+ ```
50
+
51
+ When the `start` callback returns, the stream closes. Long-lived streams should wait on `stream.closed`, a producer promise, or an application cancellation signal before returning.
52
+
53
+ ## Sending events
54
+
55
+ Use `send()` for SSE messages and `comment()` for comments:
56
+
57
+ ```ts
58
+ await stream.send({
59
+ id: 'evt_1',
60
+ event: 'post.update',
61
+ retry: 1000,
62
+ data: { id: 'post_123', likes: 10 },
63
+ });
64
+
65
+ await stream.comment('still connected');
66
+ ```
67
+
68
+ `data` may be a string or JSON-serializable value. Strings are sent as-is; other values are serialized with `JSON.stringify()`. Multi-line strings are split into multiple `data:` lines. Binary data is rejected because SSE is text-only.
69
+
70
+ `event`, `id`, and `retry` are validated before writing so accidental frame injection is rejected. Writes after close reject with `SseStreamClosedError`.
71
+
72
+ If you already serialized the payload, use `formatSseText()` for lower-level formatting while keeping the same `id`, `event`, and `retry` validation:
73
+
74
+ ```ts
75
+ import { formatSseText } from 'void/sse';
76
+
77
+ const frame = formatSseText({
78
+ event: 'post.update',
79
+ data: JSON.stringify({ id: 'post_123', likes: 10 }),
80
+ });
81
+ ```
82
+
83
+ ## Keepalives
84
+
85
+ Keepalives are enabled by default every 15 seconds:
86
+
87
+ ```ts
88
+ return eventStream(start);
89
+ ```
90
+
91
+ Disable them or customize the interval and comment text:
92
+
93
+ ```ts
94
+ return eventStream(start, { keepAlive: false });
95
+
96
+ return eventStream(start, {
97
+ keepAlive: { intervalMs: 5000, comment: 'ping' },
98
+ });
99
+ ```
100
+
101
+ The interval must be a positive finite number.
102
+
103
+ ## Last Event ID
104
+
105
+ Browsers send `Last-Event-ID` when reconnecting after an event with an `id` field. Use `getLastEventId()` to resume from your own storage:
106
+
107
+ ```ts
108
+ import { getLastEventId } from 'void/sse';
109
+
110
+ export const GET = defineHandler((c) => {
111
+ const lastId = getLastEventId(c.req.raw);
112
+ return eventStream(async (stream) => {
113
+ await stream.send({ id: nextId(lastId), data: await loadNextItem(lastId) });
114
+ });
115
+ });
116
+ ```
117
+
118
+ `void/sse` does not store or replay events. Persist event offsets in your own database, queue, or Durable Object when replay matters.
119
+
120
+ ## Client
121
+
122
+ Use `connectEventStream()` from the browser-only `void/sse/client` subpath:
123
+
124
+ ```ts
125
+ import { connectEventStream } from 'void/sse/client';
126
+
127
+ const stream = connectEventStream<{
128
+ ready: { now: number };
129
+ tick: { now: number };
130
+ }>('/api/events', {
131
+ withCredentials: true,
132
+ });
133
+
134
+ const offReady = stream.on('ready', (event) => {
135
+ console.log(event.data.now);
136
+ });
137
+
138
+ stream.on('tick', (event) => {
139
+ console.log(event.data.now);
140
+ });
141
+
142
+ offReady();
143
+ stream.close();
144
+ ```
145
+
146
+ The client wraps native `EventSource`. It supports cookie credentials and typed event handlers, but it does not support request bodies or arbitrary headers. Use cookie auth, signed URLs, or a fetch-based streaming route when you need custom headers.
147
+
148
+ JSON parsing is the default. Set `parse: 'text'` or pass a custom parser for non-JSON payloads:
149
+
150
+ ```ts
151
+ const logs = connectEventStream('/api/logs', { parse: 'text' });
152
+
153
+ logs.on('line', (event) => {
154
+ console.log(event.data);
155
+ });
156
+ ```
157
+
158
+ ## Auth
159
+
160
+ SSE routes are ordinary route handlers, so use the same auth checks you use for JSON routes:
161
+
162
+ ```ts
163
+ import { requireAuth } from 'void/auth';
164
+
165
+ export const GET = defineHandler(async (c) => {
166
+ const user = await requireAuth(c);
167
+
168
+ return eventStream(async (stream) => {
169
+ await stream.send({ event: 'ready', data: { userId: user.id } });
170
+ await stream.closed;
171
+ });
172
+ });
173
+ ```
174
+
175
+ Native `EventSource` can send cookies with `withCredentials: true`. For non-cookie auth, generate a short-lived signed URL and validate it in the route handler.
176
+
177
+ ## When to use SSE
178
+
179
+ Plain SSE is enough when the producer belongs to the same request that opened the stream:
180
+
181
+ - AI token streaming
182
+ - One-off progress updates
183
+ - Command output
184
+ - Per-request deployment or build logs
185
+ - Incremental status for a long-running action
186
+
187
+ Use a higher-level realtime primitive when you need cross-request fanout, rooms, replay buffers, subscriptions, database change streams, or multi-region coordination.
@@ -4,40 +4,37 @@ theme: dark
4
4
 
5
5
  hero:
6
6
  name: Void.
7
- text: Ship full-stack Vite apps at warp speed
8
- tagline: Void is a deployment platform designed for Vite - with a powerful backend SDK that makes your Vite apps truly full-stack.
7
+ text: Ship Vite apps at warp speed
8
+ tagline: A deployment platform designed for Vite. A powerful backend SDK to make your Vite apps truly full-stack.
9
9
  actions:
10
10
  - theme: brand
11
11
  text: Get Started
12
12
  link: ./guide/
13
- - theme: alt
14
- text: Join Early Access
15
- link: https://tally.so/r/D4VE85
16
13
  image:
17
14
  src: /hero.svg
18
15
  alt: Void deployment platform
19
16
 
20
17
  features:
18
+ - iconify: lucide:terminal
19
+ title: One Command to Production
20
+ details: '`void deploy` builds your app, runs migrations, provisions resources, and deploys it.'
21
21
  - iconify: lucide:layers
22
22
  title: Truly Full-Stack
23
- details: Database, KV storage, object storage, AI inference, authentication, queues, and cron jobs are built in. Import what you need and ignore the rest.
23
+ details: Database, KV storage, object storage, AI inference, authentication, queues, and cron jobs. All built-in. Import what you need, skip what you don't.
24
24
  - iconify: lucide:wand-sparkles
25
25
  title: Your Code is Your Infra
26
- details: Void scans your source code, detects what you use, and provisions resources automatically. No config files or dashboard clicks, either locally or in the cloud.
26
+ details: Void scans your source code, detects what you use, and automatically provisions every resource. No config files. No dashboard clicks. Locally and in the cloud.
27
27
  - iconify: lucide:shield-check
28
- title: End-to-End Type Safety
29
- details: Types flow from Drizzle schema through route handlers to page component props and the frontend fetch client. One schema validates at runtime and infers types at build time.
28
+ title: Performant and Reliable
29
+ details: Built on Cloudflare's battle tested, global network. Fast, secure, and always available from day one.
30
30
  - iconify: lucide:blocks
31
- title: Any Framework, Any Rendering
32
- details: React, Vue, Svelte, Solid, plus Vite-based meta-frameworks. Use SSR, SSG, ISR, islands, and markdown where they fit.
31
+ title: Your Framework, Your Rendering
32
+ details: React, Vue, Svelte, Solid, Vite-based meta-frameworks. SSR, SSG, ISR, islands with partial hydration, and markdown.
33
33
  - iconify: lucide:bot
34
34
  title: AI-Native
35
35
  details: Built-in skills, MCP support, and reference prompts let coding agents scaffold and ship full-stack apps in a single prompt.
36
- - iconify: lucide:terminal
37
- title: One Command to Production
38
- details: '`void deploy` builds your app, runs migrations, provisions resources, and deploys to Cloudflare Workers, without requiring a Cloudflare account or knowledge about the infra.'
39
36
 
40
- footer_heading: Build and Deploy at Warp Speed
37
+ footer_heading: Deploy at Warp Speed
41
38
  footer_subheading: Vite. Optimized. Isomorphic. Deploy.
42
39
  ---
43
40
 
@@ -902,6 +902,10 @@ Solid `Link` GET `data` is merged into the rendered `href` query string. Primiti
902
902
  | `void/auth` | `defineAuth`, `getUser`, `getSession`, `requireAuth`, `AuthUser`, `AuthSession`, `AuthState` |
903
903
  | `void/client` | `fetch`, `fetchStream`, `FetchError`, `auth`, `createAuthClient`, `AuthUser`, `AuthSession`, `AuthState` |
904
904
  | `void/ws` | `defineRoom`, `defineWebSocket`, `connect`, WebSocket context and connection types |
905
+ | `void/sse` | `eventStream`, `formatSse`, `formatSseText`, `getLastEventId`, `SseStreamClosedError`, SSE message and stream types |
906
+ | `void/sse/client` | `connectEventStream`, browser `EventSource` wrapper types |
907
+ | `void/live` | `defineLiveStream`, SSE topic fanout runtime types. Server-only. |
908
+ | `void/live/client` | `connectLiveStream`, browser helper for one SSE connection plus POST subscribe/unsubscribe control. |
905
909
  | `void/response` | `convertReturnValue` |
906
910
  | `void/validator` | `runValidation`, `ValidatorSlots`, `HandlerInput` |
907
911
  | `void/drizzle-zod` | Re-exports [`drizzle-zod`](https://orm.drizzle.team/docs/zod) for schema-derived Zod validators for Drizzle tables |