@inkly/cloudflare
v0.0.0
Published
Cloudflare Workers + Durable Objects adapter for inkly, with the WebSocket Hibernation API.
Readme
@inkly/cloudflare
Cloudflare Workers + Durable Objects adapter for inkly.
import { durableObject, inklyWorker } from "@inkly/cloudflare";
import { app } from "./app";
// Each Durable Object owns one room's sockets.
export class ChatRoom extends durableObject(app, { path: "/ws" }) {}
// The stateless Worker is the front door: validate at the edge, route to a per-room DO.
export default {
fetch: inklyWorker<Env>({
namespace: (env) => env.CHAT_ROOM,
path: "/ws",
room: (req) => new URL(req.url).searchParams.get("room") ?? "lobby",
allowedOrigins: ["https://app.example.com"],
}),
};For hibernation-friendly deployments, create the core app with heartbeat: false; Cloudflare handles protocol ping/pong without waking the Durable Object.
The adapter uses Cloudflare's hibernatable Durable Object WebSocket API:
new WebSocketPair()state.acceptWebSocket(server)- Durable Object
webSocketMessage,webSocketClose, andwebSocketErrormethods serializeAttachment()/deserializeAttachment()to rebuild adapter-level connection metadata after wakesetWebSocketAutoResponse()so client heartbeats are answered without waking (and billing) the DO
Two-tier routing (inklyWorker)
inklyWorker(options) returns a Worker fetch handler that validates the upgrade at the edge and
forwards it to a per-room Durable Object via DurableObjectNamespace.getByName(name) (Cloudflare's
current one-call routing API). Validating in the stateless Worker means malformed or cross-site
requests never reach — or bill — a Durable Object.
| Option | Purpose |
| --- | --- |
| namespace(env) | Select the DO namespace binding (e.g. (env) => env.CHAT_ROOM). Required. |
| room(request) | Derive the DO instance name (typically the room). Same name -> same DO -> shared rooms/presence. Defaults to the request pathname (one DO per path). |
| path | Only route upgrades on this pathname; others get 404. |
| allowedOrigins | CSWSH allow-list (or predicate) enforced at the edge with 403, before a DO is billed. Mirror the app's allowedOrigins. |
Durable Object options (durableObject(app, options))
| Option | Purpose |
| --- | --- |
| path | Only accept WebSocket upgrades for this pathname. Default "/". |
| authorize(request) | Pre-authorize at the HTTP upgrade; throw an InklyError to control the status, otherwise failed auth is 401. |
| autoResponse | Register the canonical heartbeat as a hibernation auto-response so client pings are answered without waking the DO. Default true; only applies to text codecs (the default JSON codec). |
Origin validation also runs inside the DO fetch (defense in depth) using the app's allowedOrigins,
so a direct-to-DO request is still checked even if the edge gate is bypassed.
See docs/overview.md and examples/.
Current test coverage
This package is typechecked against @cloudflare/workers-types and unit-tested under Node/Vitest with fake Workers primitives. The tests drive the real inkly core protocol through the adapter, but this repository does not execute the adapter in a real Workers runtime.
