verani 0.4.2 → 0.4.3
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.
- package/README.md +74 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,28 +40,30 @@ export const chatRoom = defineRoom({
|
|
|
40
40
|
|
|
41
41
|
onConnect(ctx) {
|
|
42
42
|
console.log(`User ${ctx.meta.userId} connected`);
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
// Use emit API (socket.io-like)
|
|
44
|
+
ctx.actor.emit.to("default").emit("user.joined", {
|
|
45
45
|
userId: ctx.meta.userId
|
|
46
46
|
});
|
|
47
47
|
},
|
|
48
48
|
|
|
49
|
-
onMessage(ctx, frame) {
|
|
50
|
-
if (frame.type === "chat.message") {
|
|
51
|
-
// Broadcast to everyone except sender
|
|
52
|
-
ctx.actor.broadcast("default", {
|
|
53
|
-
type: "chat.message",
|
|
54
|
-
from: ctx.meta.userId,
|
|
55
|
-
text: frame.data.text
|
|
56
|
-
}, { except: ctx.ws });
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
|
|
60
49
|
onDisconnect(ctx) {
|
|
61
50
|
console.log(`User ${ctx.meta.userId} disconnected`);
|
|
51
|
+
ctx.actor.emit.to("default").emit("user.left", {
|
|
52
|
+
userId: ctx.meta.userId
|
|
53
|
+
});
|
|
62
54
|
}
|
|
63
55
|
});
|
|
64
56
|
|
|
57
|
+
// Register event handlers (socket.io-like, recommended)
|
|
58
|
+
chatRoom.on("chat.message", (ctx, data) => {
|
|
59
|
+
// Broadcast to all in default channel
|
|
60
|
+
ctx.actor.emit.to("default").emit("chat.message", {
|
|
61
|
+
from: ctx.meta.userId,
|
|
62
|
+
text: data.text,
|
|
63
|
+
timestamp: Date.now()
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
65
67
|
// Create the Durable Object class
|
|
66
68
|
export const ChatRoom = createActorHandler(chatRoom);
|
|
67
69
|
```
|
|
@@ -99,14 +101,27 @@ The three-way relationship:
|
|
|
99
101
|
```typescript
|
|
100
102
|
import { VeraniClient } from "verani";
|
|
101
103
|
|
|
102
|
-
// Connect to your Cloudflare Worker
|
|
103
|
-
const client = new VeraniClient("wss://your-worker.dev/ws?userId=alice"
|
|
104
|
+
// Connect to your Cloudflare Worker with ping/pong keepalive
|
|
105
|
+
const client = new VeraniClient("wss://your-worker.dev/ws?userId=alice", {
|
|
106
|
+
pingInterval: 5000, // Send ping every 5 seconds
|
|
107
|
+
pongTimeout: 5000, // Expect pong within 5 seconds
|
|
108
|
+
reconnection: {
|
|
109
|
+
enabled: true,
|
|
110
|
+
maxAttempts: 10,
|
|
111
|
+
initialDelay: 1000,
|
|
112
|
+
maxDelay: 30000
|
|
113
|
+
}
|
|
114
|
+
});
|
|
104
115
|
|
|
105
116
|
// Listen for messages
|
|
106
117
|
client.on("chat.message", (data) => {
|
|
107
118
|
console.log(`${data.from}: ${data.text}`);
|
|
108
119
|
});
|
|
109
120
|
|
|
121
|
+
client.on("user.joined", (data) => {
|
|
122
|
+
console.log(`User ${data.userId} joined`);
|
|
123
|
+
});
|
|
124
|
+
|
|
110
125
|
// Send messages
|
|
111
126
|
client.emit("chat.message", { text: "Hello, world!" });
|
|
112
127
|
|
|
@@ -118,6 +133,10 @@ client.onOpen(() => {
|
|
|
118
133
|
client.onStateChange((state) => {
|
|
119
134
|
console.log("Connection state:", state);
|
|
120
135
|
});
|
|
136
|
+
|
|
137
|
+
// Wait for connection before sending
|
|
138
|
+
await client.waitForConnection();
|
|
139
|
+
client.emit("ready", {});
|
|
121
140
|
```
|
|
122
141
|
|
|
123
142
|
## Key Concepts
|
|
@@ -135,8 +154,11 @@ Each Cloudflare Actor instance represents a **logical container** for realtime c
|
|
|
135
154
|
Inside an Actor, connections can join **channels** for selective message routing:
|
|
136
155
|
|
|
137
156
|
```typescript
|
|
138
|
-
// Server: broadcast to specific channel
|
|
139
|
-
ctx.actor.
|
|
157
|
+
// Server: broadcast to specific channel using emit API
|
|
158
|
+
ctx.actor.emit.to("game-updates").emit("update", data);
|
|
159
|
+
|
|
160
|
+
// Or send to a specific user (all their sessions)
|
|
161
|
+
ctx.emit.to("alice").emit("notification", { message: "Hello!" });
|
|
140
162
|
|
|
141
163
|
// Client: joins "default" channel automatically
|
|
142
164
|
// You can implement join/leave for custom channels
|
|
@@ -152,37 +174,61 @@ Verani handles Cloudflare's hibernation automatically:
|
|
|
152
174
|
|
|
153
175
|
## Documentation
|
|
154
176
|
|
|
155
|
-
- **[Getting Started](./docs/
|
|
156
|
-
- **[
|
|
157
|
-
- **[
|
|
158
|
-
- **[Examples](./docs/
|
|
159
|
-
- **[
|
|
160
|
-
- **[
|
|
177
|
+
- **[Getting Started](./docs/getting-started/)** - Installation and quick start guide
|
|
178
|
+
- **[API Reference](./docs/api/)** - Complete server and client API documentation
|
|
179
|
+
- **[Guides](./docs/guides/)** - Configuration, deployment, scaling, and RPC
|
|
180
|
+
- **[Examples](./docs/examples/)** - Common usage patterns and code samples
|
|
181
|
+
- **[Concepts](./docs/concepts/)** - Architecture, hibernation, and core concepts
|
|
182
|
+
- **[Security](./docs/security/)** - Authentication, authorization, and best practices
|
|
161
183
|
|
|
162
184
|
## Features
|
|
163
185
|
|
|
164
186
|
### Server (Actor) Side
|
|
165
187
|
|
|
166
|
-
-
|
|
188
|
+
- **Socket.io-like event handlers** - `room.on()` and `room.off()` for clean event handling
|
|
189
|
+
- **Emit API** - `ctx.emit` and `ctx.actor.emit.to()` for intuitive message sending
|
|
190
|
+
- Room-based architecture with lifecycle hooks (`onConnect`, `onDisconnect`, `onHibernationRestore`)
|
|
167
191
|
- WebSocket attachment management for hibernation
|
|
168
|
-
- Selective broadcasting with filters
|
|
192
|
+
- Selective broadcasting with filters (userIds, clientIds, except)
|
|
169
193
|
- User and client ID tracking
|
|
170
194
|
- **RPC methods** - Call Actor methods remotely from Workers or other Actors
|
|
195
|
+
- Durable Object storage access for persistent state
|
|
171
196
|
- Error boundaries and logging
|
|
172
197
|
- Flexible metadata extraction from requests
|
|
173
198
|
|
|
174
199
|
### Client Side
|
|
175
200
|
|
|
176
201
|
- Automatic reconnection with exponential backoff
|
|
177
|
-
- Connection state management
|
|
202
|
+
- Connection state management (`getState()`, `getConnectionState()`, `isConnecting`)
|
|
178
203
|
- Message queueing when disconnected
|
|
179
204
|
- Event-based API (on/off/once/emit)
|
|
180
|
-
- Promise-based connection waiting
|
|
181
|
-
- Lifecycle callbacks
|
|
205
|
+
- Promise-based connection waiting (`waitForConnection()`)
|
|
206
|
+
- Lifecycle callbacks (`onOpen`, `onClose`, `onError`, `onStateChange`)
|
|
182
207
|
- **Ping/pong keepalive** with automatic Page Visibility API resync
|
|
208
|
+
- Configurable connection timeout and queue size
|
|
183
209
|
|
|
184
210
|
### RPC Support
|
|
185
211
|
|
|
212
|
+
Call Actor methods remotely from Workers or other Actors:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// In your Worker fetch handler
|
|
216
|
+
const stub = ChatRoom.get("room-id");
|
|
217
|
+
|
|
218
|
+
// Send to user
|
|
219
|
+
await stub.sendToUser("alice", "notifications", {
|
|
220
|
+
type: "alert",
|
|
221
|
+
message: "You have a new message"
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Broadcast to channel
|
|
225
|
+
await stub.broadcast("default", { type: "announcement", text: "Hello!" });
|
|
226
|
+
|
|
227
|
+
// Query state
|
|
228
|
+
const count = await stub.getSessionCount();
|
|
229
|
+
const userIds = await stub.getConnectedUserIds();
|
|
230
|
+
```
|
|
231
|
+
|
|
186
232
|
- Send messages to users from HTTP endpoints
|
|
187
233
|
- Query actor state remotely
|
|
188
234
|
- Broadcast from external events or scheduled tasks
|