@workkit/chat
v0.2.0
Published
Real-time chat transport with typed WebSocket messaging and Durable Object sessions for Cloudflare Workers
Maintainers
Readme
@workkit/chat
Real-time chat over WebSockets for Cloudflare Workers — typed envelopes, heartbeats, optional Durable Object sessions with hibernation.
Two modes: stateless transport (createChatTransport) for proxies/echoes, and ChatSessionDO for persistent sessions with reconnect replay. Both share the same ChatMessage envelope and onMessage handler signature.
Install
bun add @workkit/chatUsage — stateless transport
import { createChatTransport } from "@workkit/chat";
const transport = createChatTransport({
onMessage: async (sessionId, msg) => ({
id: crypto.randomUUID(),
type: "message",
role: "assistant",
content: `Echo: ${msg.content}`,
timestamp: Date.now(),
}),
});
export default {
async fetch(req: Request) {
const sessionId = new URL(req.url).searchParams.get("sessionId") ?? crypto.randomUUID();
return transport.handleUpgrade(req, sessionId);
},
};Usage — Durable Object sessions
Cloudflare instantiates DOs with (state, env) only, so subclass and pass your onMessage to super(...):
import { ChatSessionDO } from "@workkit/chat";
export class ChatDO extends ChatSessionDO {
constructor(state: DurableObjectState, env: Env) {
super(state, env, {
onMessage: async (sessionId, msg) =>
msg.type === "message"
? { id: crypto.randomUUID(), type: "message", role: "assistant", content: `Echo: ${msg.content}`, timestamp: Date.now() }
: undefined,
});
}
}
export default {
async fetch(req: Request, env: Env) {
const sessionId = new URL(req.url).searchParams.get("sessionId") ?? crypto.randomUUID();
const id = env.CHAT_DO.idFromName(sessionId);
return env.CHAT_DO.get(id).fetch(req);
},
};The DO uses WebSocket hibernation, persists up to maxStoredMessages (default 100) for reconnect replay via ?lastMessageId=<id>.
Highlights
- Typed
ChatMessageenvelope withmessage | typing | error | tool_call | tool_result | systemdiscriminant - Configurable heartbeat (default 30s) and max message size (default 64 KB)
- WebSocket hibernation in DO mode — connections survive Worker restarts
- Reconnect message replay
- Wire codec exposed via
encodeMessage/decodeMessage
Documentation
Full guide: workkit docs — Real-time Chat
