diff --git a/README.md b/README.md index ba52fb5..c334064 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,11 @@ export const Counter = generateHonoObject("/counter", async (app, state) => { You want to find more? Check out the [examples](./examples)! +## Support + +- [x] [`alarm` API](https://developers.cloudflare.com/durable-objects/api/alarms/) +- [x] [`Hibernation Websocket API`](https://developers.cloudflare.com/durable-objects/learning/websockets/#websocket-hibernation) + ## License [MIT](./LICENSE) diff --git a/examples/hibernatable-chat/README.md b/examples/hibernatable-chat/README.md new file mode 100644 index 0000000..cc58e96 --- /dev/null +++ b/examples/hibernatable-chat/README.md @@ -0,0 +1,8 @@ +``` +npm install +npm run dev +``` + +``` +npm run deploy +``` diff --git a/examples/hibernatable-chat/package.json b/examples/hibernatable-chat/package.json new file mode 100644 index 0000000..20c1717 --- /dev/null +++ b/examples/hibernatable-chat/package.json @@ -0,0 +1,21 @@ +{ + "name": "hono-do-example-hibernatable-chat", + "private": true, + "version": "0.0.0", + "scripts": { + "lint": "eslint --fix --ext .ts,.tsx src", + "lint:check": "eslint --ext .ts,.tsx src", + "format": "prettier --write \"src/**/*.{ts,tsx}\"", + "format:check": "prettier --check \"src/**/*.{ts,tsx}\"", + "dev": "wrangler dev src/index.ts", + "deploy": "wrangler deploy --minify src/index.ts" + }, + "dependencies": { + "hono": "^3.6.0", + "hono-do": "workspace:*" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20230821.0", + "wrangler": "^3.7.0" + } +} diff --git a/examples/hibernatable-chat/src/chat.ts b/examples/hibernatable-chat/src/chat.ts new file mode 100644 index 0000000..7505d44 --- /dev/null +++ b/examples/hibernatable-chat/src/chat.ts @@ -0,0 +1,58 @@ +import { generateHonoObject } from "hono-do"; + +function uuidv4() { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { + const r = (Math.random() * 16) | 0, + v = c == "x" ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); +} + +declare module "hono-do" { + interface HonoObjectVars { + messages: { + timestamp: string; + text: string; + }[]; + } +} + +export const Chat = generateHonoObject("/chat", (app, state, vars) => { + vars.messages = []; + + app.get("/messages", async (c) => c.json(vars.messages)); + + app.get("/websocket", async (c) => { + if (c.req.header("Upgrade") === "websocket") { + return await handleWebSocketUpgrade(); + } + return c.text("Not found", 404); + }); + + async function handleWebSocketUpgrade() { + const [client, server] = Object.values(new WebSocketPair()); + const clientId = uuidv4(); + state.acceptWebSocket(server); + + server.serializeAttachment({ clientId }); + + return new Response(null, { status: 101, webSocket: client }); + } +}); + +Chat.webSocketMessage(async (webSocket, msg, state, vars) => { + const { clientId: senderClientId } = await webSocket.deserializeAttachment(); + state.getWebSockets().forEach((ws) => { + const { clientId } = ws.deserializeAttachment(); + if (clientId === senderClientId) { + return; + } + + try { + vars.messages.push(JSON.parse(msg.toString())); + ws.send(msg.toString()); + } catch (error) { + ws.close(); + } + }); +}); diff --git a/examples/hibernatable-chat/src/index.ts b/examples/hibernatable-chat/src/index.ts new file mode 100644 index 0000000..77e72d6 --- /dev/null +++ b/examples/hibernatable-chat/src/index.ts @@ -0,0 +1,22 @@ +import { Hono } from "hono"; + +import { Template } from "./template"; + +const app = new Hono<{ + Bindings: { + CHAT: DurableObjectNamespace; + }; +}>(); + +app.get("/", (c) => { + return c.html(Template); +}); + +app.all("/chat/*", (c) => { + const id = c.env.CHAT.idFromName("chat"); + const obj = c.env.CHAT.get(id); + return obj.fetch(c.req.raw); +}); + +export default app; +export * from "./chat"; diff --git a/examples/hibernatable-chat/src/template.ts b/examples/hibernatable-chat/src/template.ts new file mode 100644 index 0000000..01beb3c --- /dev/null +++ b/examples/hibernatable-chat/src/template.ts @@ -0,0 +1,88 @@ +export const Template = /*html*/ ` + +
+