{"id":15723444,"url":"https://github.com/napolab/y-durableobjects","last_synced_at":"2025-04-09T13:09:15.522Z","repository":{"id":221221001,"uuid":"737174693","full_name":"napolab/y-durableobjects","owner":"napolab","description":"Real-time collaboration with Yjs on Cloudflare Workers using Durable Objects, eliminating Node.js dependencies. Inspired by y-websocket","archived":false,"fork":false,"pushed_at":"2024-09-05T09:02:56.000Z","size":451,"stargazers_count":150,"open_issues_count":4,"forks_count":8,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-02T11:04:31.797Z","etag":null,"topics":["cloudflareworkers","durable-objects","hono","yjs"],"latest_commit_sha":null,"homepage":"https://yjs.napochaan.dev/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/napolab.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-30T04:22:53.000Z","updated_at":"2025-03-31T17:20:40.000Z","dependencies_parsed_at":"2024-10-24T17:47:29.118Z","dependency_job_id":"e211b0b7-8215-4639-a33e-2133efee0538","html_url":"https://github.com/napolab/y-durableobjects","commit_stats":null,"previous_names":["napolab/yjs-worker","napolab/y-durableobjects","napolab/yjs-worker-demo"],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/napolab%2Fy-durableobjects","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/napolab%2Fy-durableobjects/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/napolab%2Fy-durableobjects/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/napolab%2Fy-durableobjects/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/napolab","download_url":"https://codeload.github.com/napolab/y-durableobjects/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248045233,"owners_count":21038553,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflareworkers","durable-objects","hono","yjs"],"created_at":"2024-10-03T22:11:39.451Z","updated_at":"2025-04-09T13:09:15.503Z","avatar_url":"https://github.com/napolab.png","language":"TypeScript","readme":"# y-durableobjects\n\n[![Yjs on Cloudflare Workers with Durable Objects Demo Movie](https://i.gyazo.com/e94637740dbb11fc5107b0cd0850326d.gif)](https://gyazo.com/e94637740dbb11fc5107b0cd0850326d)\n\nThe `y-durableobjects` library is designed to facilitate real-time collaboration in the Cloudflare Workers environment using Yjs and Durable Objects. It provides a straightforward way to integrate Yjs for decentralized, scalable real-time editing features.\n\n## Requirements\n\n- Hono version 4.3 or higher is required.\n\n## Installation\n\nTo use `y-durableobjects`, you need to install the package along with `hono`, as it is a peer dependency.\n\n```bash\nnpm install y-durableobjects hono\n```\n\nor using yarn:\n\n```bash\nyarn add y-durableobjects hono\n```\n\nor pnpm:\n\n```bash\npnpm add y-durableobjects hono\n```\n\n### Configuration for Durable Objects\n\nTo properly utilize Durable Objects, you need to configure bindings in your `wrangler.toml` file. This involves specifying the name of the Durable Object binding and the class name that represents your Durable Object. For detailed instructions on how to set up your `wrangler.toml` for Durable Objects, including setting up environment variables and additional resources, refer to [Cloudflare's Durable Objects documentation](https://developers.cloudflare.com/durable-objects/get-started/#5-configure-durable-object-bindings).\n\nThis configuration ensures that your Cloudflare Worker can correctly instantiate and interact with Durable Objects, allowing `y-durableobjects` to manage real-time collaboration sessions.\n\n```toml\nname = \"your-worker-name\"\nmain = \"src/index.ts\"\ncompatibility_date = \"2024-04-05\"\n\naccount_id = \"your-account-id\"\nworkers_dev = true\n\n# Durable Objects binding\n[durable_objects]\nbindings = [\n  { name = \"Y_DURABLE_OBJECTS\", class_name = \"YDurableObjects\" }\n]\n\n# Durable Objects migrations\n[[migrations]]\ntag = \"v1\"\nnew_classes = [\"YDurableObjects\"]\n```\n\n## Usage\n\n### With Hono shorthand\n\n```typescript\nimport { Hono } from \"hono\";\nimport { YDurableObjects, yRoute } from \"y-durableobjects\";\n\ntype Bindings = {\n  Y_DURABLE_OBJECTS: DurableObjectNamespace\u003cYDurableObjects\u003cEnv\u003e\u003e;\n};\n\ntype Env = {\n  Bindings: Bindings;\n};\n\nconst app = new Hono\u003cEnv\u003e();\n\nconst route = app.route(\n  \"/editor\",\n  yRoute\u003cEnv\u003e((env) =\u003e env.Y_DURABLE_OBJECTS),\n);\n\nexport default route;\nexport type AppType = typeof route;\nexport { YDurableObjects };\n```\n\n### Without the shorthand\n\nThe following example demonstrates how to integrate Hono RPC with `y-durableobjects`. Note that WebSocket connections must be handled via fetch due to the current limitations of JS RPC (see [Cloudflare issue](https://github.com/cloudflare/workerd/issues/2319)):\n\n```typescript\nimport { Hono } from \"hono\";\nimport { YDurableObjects, type YDurableObjectsAppType } from \"y-durableobjects\";\nimport { upgrade } from \"y-durableobjects/helpers/upgrade\";\n\ntype Bindings = {\n  Y_DURABLE_OBJECTS: DurableObjectNamespace\u003cYDurableObjects\u003cEnv\u003e\u003e;\n};\n\ntype Env = {\n  Bindings: Bindings;\n};\n\nconst app = new Hono\u003cEnv\u003e();\napp.get(\"/editor/:id\", upgrade(), async (c) =\u003e {\n  const id = c.env.Y_DURABLE_OBJECTS.idFromName(c.req.param(\"id\"));\n  const stub = c.env.Y_DURABLE_OBJECTS.get(id);\n\n  const url = new URL(\"/\", c.req.url);\n  const client = hc\u003cYDurableObjectsAppType\u003e(url.toString(), {\n    fetch: stub.fetch.bind(stub),\n  });\n\n  const res = await client.rooms[\":id\"].$get(\n    { param: { id: c.req.param(\"id\") } },\n    { init: { headers: c.req.raw.headers } },\n  );\n\n  return new Response(null, {\n    webSocket: res.webSocket,\n    status: res.status,\n    statusText: res.statusText,\n  });\n});\n\nexport default app;\nexport type AppType = typeof app;\nexport { YDurableObjects };\n```\n\n### JS RPC support\n\n`y-durableobjects` supports JS RPC for fetching and updating YDocs. Below are examples of how to use the `getYDoc` and `updateYDoc` interfaces.\n\n#### getYDoc\n\nThis API fetches the state of the YDoc within a Durable Object.\n\nExample usage in Hono:\n\n```typescript\nimport { Hono } from \"hono\";\nimport { YDurableObjects } from \"y-durableobjects\";\nimport { fromUint8Array } from \"js-base64\";\n\ntype Bindings = {\n  Y_DURABLE_OBJECTS: DurableObjectNamespace\u003cYDurableObjects\u003cEnv\u003e\u003e;\n};\n\ntype Env = {\n  Bindings: Bindings;\n};\n\nconst app = new Hono\u003cEnv\u003e();\n\napp.get(\"/rooms/:id/state\", async (c) =\u003e {\n  const roomId = c.req.param(\"id\");\n  const id = c.env.Y_DURABLE_OBJECTS.idFromName(roomId);\n  const stub = c.env.Y_DURABLE_OBJECTS.get(id);\n\n  const doc = await stub.getYDoc();\n  const base64 = fromUint8Array(doc);\n\n  return c.json({ doc: base64 }, 200);\n});\n\nexport default app;\nexport { YDurableObjects };\n```\n\n#### updateYDoc\n\nThis API updates the state of the YDoc within a Durable Object.\n\nExample usage in Hono:\n\n```typescript\nimport { Hono } from \"hono\";\nimport { YDurableObjects } from \"y-durableobjects\";\n\ntype Bindings = {\n  Y_DURABLE_OBJECTS: DurableObjectNamespace\u003cYDurableObjects\u003cEnv\u003e\u003e;\n};\n\ntype Env = {\n  Bindings: Bindings;\n};\n\nconst app = new Hono\u003cEnv\u003e();\n\napp.post(\"/rooms/:id/update\", async (c) =\u003e {\n  const roomId = c.req.param(\"id\");\n  const id = c.env.Y_DURABLE_OBJECTS.idFromName(roomId);\n  const stub = c.env.Y_DURABLE_OBJECTS.get(id);\n\n  const buffer = await c.req.arrayBuffer();\n  const update = new Uint8Array(buffer);\n\n  await stub.updateYDoc(update);\n\n  return c.json(null, 200);\n});\n\nexport default app;\nexport { YDurableObjects };\n```\n\n### Extending with JS RPC\n\nBy supporting JS RPC, `y-durableobjects` allows for advanced operations through extensions. You can manipulate the protected fields for custom functionality:\n\nExample:\n\n```typescript\nimport { applyUpdate, encodeStateAsUpdate } from \"yjs\";\nimport { YDurableObjects } from \"y-durableobjects\";\n\nexport class CustomDurableObject extends YDurableObjects {\n  async customMethod() {\n    // Access and manipulate the YDoc state\n    const update = new Uint8Array([\n      /* some update data */\n    ]);\n    this.doc.update(update);\n    await this.cleanup();\n  }\n}\n```\n\n### Hono RPC support for ClientSide\n\n- Utilizes Hono's WebSocket Helper, making the `$ws` method available in `hono/client` for WebSocket communications.\n  - For more information on server and client setup, see the [Hono WebSocket Helper documentation](https://hono.dev/helpers/websocket#server-and-client).\n\n#### Server Implementation\n\n##### Using shorthand\n\n```typescript\nimport { Hono } from \"hono\";\nimport { YDurableObjects, yRoute } from \"y-durableobjects\";\n\ntype Bindings = {\n  Y_DURABLE_OBJECTS: DurableObjectNamespace\u003cYDurableObjects\u003cEnv\u003e\u003e;\n};\n\ntype Env = {\n  Bindings: Bindings;\n};\n\nconst app = new Hono\u003cEnv\u003e();\nconst route = app.route(\n  \"/editor\",\n  yRoute\u003cEnv\u003e((env) =\u003e env.Y_DURABLE_OBJECTS),\n);\n\nexport default route;\nexport type AppType = typeof route;\nexport { YDurableObjects };\n```\n\n##### Without shorthand\n\n```typescript\nimport { Hono } from \"hono\";\nimport { YDurableObjects, YDurableObjectsAppType } from \"y-durableobjects\";\nimport { upgrade } from \"y-durableobjects/helpers/upgrade\";\n\ntype Bindings = {\n  Y_DURABLE_OBJECTS: DurableObjectNamespace\u003cYDurableObjects\u003cEnv\u003e\u003e;\n};\n\ntype Env = {\n  Bindings: Bindings;\n};\n\nconst app = new Hono\u003cEnv\u003e();\napp.get(\"/editor/:id\", upgrade(), async (c) =\u003e {\n  const id = c.env.Y_DURABLE_OBJECTS.idFromName(c.req.param(\"id\"));\n  const stub = c.env.Y_DURABLE_OBJECTS.get(id);\n\n  const url = new URL(\"/\", c.req.url);\n  const client = hc\u003cYDurableObjectsAppType\u003e(url, {\n    fetch: stub.fetch.bind(stub),\n  });\n\n  const res = await client.rooms[\":id\"].$get(\n    { param: { id: c.req.param(\"id\") } },\n    { init: { headers: c.req.raw.headers } },\n  );\n\n  return new Response(null, {\n    webSocket: res.webSocket,\n    status: res.status,\n    statusText: res.statusText,\n  });\n});\n\nexport default app;\nexport type AppType = typeof app;\nexport { YDurableObjects };\n```\n\n#### Client Implementation\n\nTo utilize Hono RPC on the client side, you can create a typed client using `hc` from `hono/client`:\n\n```typescript\nimport { hc } from \"hono/client\";\nimport type { AppType } from \"./server\"; // Adjust the import path as needed\n\nconst API_URL = \"http://localhost:8787\";\n\nexport const client = hc\u003cAppType\u003e(API_URL);\nconst ws = client.editor[\":id\"].$ws({ param: { id: \"example\" } });\n//    ^?const ws: WebSocket\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnapolab%2Fy-durableobjects","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnapolab%2Fy-durableobjects","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnapolab%2Fy-durableobjects/lists"}