From df3aa5cf5f3dcc0095caf2a3b5fd7dade835da68 Mon Sep 17 00:00:00 2001 From: NathanFlurry Date: Sat, 22 Nov 2025 01:14:52 +0000 Subject: [PATCH 1/2] chore: remove old examples (#3525) --- examples/background-jobs/.gitignore | 2 - examples/background-jobs/README.md | 37 -- examples/background-jobs/package.json | 22 -- examples/background-jobs/src/registry.ts | 52 --- examples/background-jobs/src/server.ts | 30 -- examples/background-jobs/src/types.ts | 9 - examples/background-jobs/turbo.json | 4 - examples/better-auth-external-db/.gitignore | 31 -- examples/better-auth-external-db/README.md | 62 ---- .../2025-06-27T06-53-24.043Z.sql | 7 - examples/better-auth-external-db/package.json | 39 -- .../src/backend/auth.ts | 10 - .../src/backend/registry.ts | 44 --- .../src/backend/server.ts | 31 -- .../src/frontend/App.tsx | 75 ---- .../src/frontend/auth-client.ts | 5 - .../src/frontend/components/AuthForm.tsx | 125 ------- .../src/frontend/components/ChatRoom.tsx | 186 ---------- .../src/frontend/index.html | 13 - .../src/frontend/main.tsx | 9 - examples/better-auth-external-db/turbo.json | 4 - examples/bots/.gitignore | 2 - examples/bots/README.md | 34 -- examples/bots/package.json | 23 -- examples/bots/src/registry.ts | 62 ---- examples/bots/src/server.ts | 36 -- examples/bots/src/types.ts | 9 - examples/bots/src/utils.ts | 21 -- examples/bots/turbo.json | 4 - .../cloudflare-workers-hono/wrangler.json | 2 +- .../README.md | 33 -- .../package.json | 25 -- .../scripts/client-http.ts | 24 -- .../scripts/client-rivetkit.ts | 31 -- .../src/index.ts | 43 --- .../src/registry.ts | 16 - .../turbo.json | 4 - .../wrangler.json | 30 -- examples/cloudflare-workers/src/registry.ts | 2 +- examples/cloudflare-workers/wrangler.json | 2 +- examples/counter-next-js/.gitignore | 41 --- examples/counter-next-js/README.md | 54 --- examples/counter-next-js/next.config.ts | 7 - examples/counter-next-js/package.json | 27 -- examples/counter-next-js/scripts/connect.ts | 19 - .../src/app/api/rivet/[...all]/route.ts | 6 - examples/counter-next-js/src/app/globals.css | 220 ----------- examples/counter-next-js/src/app/layout.tsx | 32 -- examples/counter-next-js/src/app/page.tsx | 5 - .../src/components/Counter.tsx | 103 ------ .../counter-next-js/src/rivet/registry.ts | 21 -- examples/counter-next-js/tsconfig.json | 27 -- examples/counter/scripts/connect.ts | 7 +- examples/crdt/README.md | 64 ---- examples/crdt/src/frontend/App.tsx | 173 --------- examples/crdt/tests/crdt.test.ts | 90 ----- examples/crdt/tsconfig.json | 21 -- examples/crdt/turbo.json | 4 - .../src/backend/registry.ts | 3 +- examples/database/README.md | 81 ---- examples/database/src/backend/my-utils.ts | 7 - examples/database/src/backend/registry.ts | 57 --- examples/database/src/frontend/App.tsx | 201 ---------- examples/database/src/frontend/index.html | 189 ---------- examples/database/tests/database.test.ts | 121 ------ examples/database/tsconfig.json | 21 -- examples/database/turbo.json | 4 - examples/game/README.md | 102 ------ examples/game/package.json | 29 -- examples/game/src/backend/types.ts | 7 - examples/game/src/frontend/App.tsx | 202 ---------- examples/game/tests/game.test.ts | 106 ------ examples/game/turbo.json | 4 - .../kitchen-sink/src/backend/actors/demo.ts | 2 +- .../src/backend/actors/websocket.ts | 8 +- examples/kitchen-sink/src/frontend/App.tsx | 6 +- .../frontend/components/ConnectionScreen.tsx | 22 ++ .../frontend/components/InteractionScreen.tsx | 2 +- .../components/tabs/ConnectionsTab.tsx | 4 + .../{rate => quickstart-actions}/package.json | 12 +- .../src/backend/registry.ts | 137 +++++++ .../src/backend/server.ts | 0 .../quickstart-actions/src/frontend/App.tsx | 267 ++++++++++++++ .../src/frontend/index.html | 138 +++++++ .../src/frontend/main.tsx | 2 +- .../quickstart-actions/tests/actions.test.ts | 179 +++++++++ .../tsconfig.json | 6 +- .../vite.config.ts | 2 +- .../vitest.config.ts | 3 + .../package.json | 12 +- .../src/backend/registry.ts | 169 +++++++++ .../src/backend/server.ts | 0 .../src/frontend/App.tsx | 271 ++++++++++++++ .../src/frontend/index.html | 189 ++++++++++ .../src/frontend/main.tsx | 2 +- .../tests/cross-actor.test.ts | 292 +++++++++++++++ .../tsconfig.json | 6 +- .../vite.config.ts | 2 +- .../vitest.config.ts | 5 +- .../package.json | 15 +- .../src/backend/registry.ts | 111 ++++++ .../src/backend/server.ts | 0 .../src/backend/types.ts | 18 + .../src/frontend/App.tsx | 260 +++++++++++++ .../src/frontend/index.html | 100 +++-- .../src/frontend/main.tsx | 2 +- .../tests/regions.test.ts | 181 +++++++++ .../tsconfig.json | 0 .../vite.config.ts | 0 .../vitest.config.ts | 0 .../quickstart-native-websockets/package.json | 32 ++ .../src/backend/registry.ts | 121 ++++++ .../src/backend/server.ts | 0 .../src/frontend/App.tsx | 210 +++++++++++ .../src/frontend/index.html | 94 +++++ .../src/frontend/main.tsx | 2 +- .../tests/websocket.test.ts | 12 + .../tsconfig.json | 43 +++ .../vite.config.ts | 2 +- .../vitest.config.ts | 5 +- examples/quickstart-realtime/package.json | 30 ++ .../src/backend/registry.ts | 50 +++ .../src/backend/server.ts | 0 .../quickstart-realtime/src/frontend/App.tsx | 78 ++++ .../src/frontend/index.html | 90 +++++ .../quickstart-realtime/src/frontend/main.tsx | 12 + .../quickstart-realtime/tests/cursors.test.ts | 87 +++++ examples/quickstart-realtime/tsconfig.json | 43 +++ .../vite.config.ts | 5 +- .../vitest.config.ts | 0 examples/quickstart-scheduling/package.json | 30 ++ .../src/backend/registry.ts | 130 +++++++ .../src/backend/server.ts | 0 .../src/frontend/App.tsx | 272 ++++++++++++++ .../src/frontend/index.html | 344 +++++++++++++++++ .../src/frontend/main.tsx | 12 + .../tests/scheduling.test.ts | 232 ++++++++++++ .../tsconfig.json | 2 +- examples/quickstart-scheduling/vite.config.ts | 10 + .../vitest.config.ts | 0 examples/quickstart-state/package.json | 30 ++ .../quickstart-state/src/backend/registry.ts | 47 +++ .../src/backend}/server.ts | 0 .../quickstart-state/src/frontend/App.tsx | 122 +++++++ .../src/frontend/index.html | 152 ++++---- .../quickstart-state/src/frontend/main.tsx | 12 + examples/quickstart-state/tests/chat.test.ts | 131 +++++++ .../{bots => quickstart-state}/tsconfig.json | 6 +- examples/quickstart-state/vite.config.ts | 10 + examples/quickstart-state/vitest.config.ts | 10 + examples/rate/README.md | 89 ----- examples/rate/src/backend/registry.ts | 66 ---- examples/rate/src/frontend/App.tsx | 149 -------- examples/rate/src/frontend/index.html | 156 -------- examples/rate/tests/rate.test.ts | 98 ----- examples/rate/tsconfig.json | 21 -- examples/rate/turbo.json | 4 - .../raw-fetch-handler/src/backend/registry.ts | 2 +- examples/sync/README.md | 183 ---------- examples/sync/package.json | 29 -- examples/sync/src/backend/registry.ts | 91 ----- examples/sync/src/backend/types.ts | 7 - examples/sync/src/frontend/App.tsx | 345 ------------------ examples/sync/src/frontend/index.html | 231 ------------ examples/sync/src/frontend/main.tsx | 12 - examples/sync/tests/sync.test.ts | 217 ----------- examples/sync/tsconfig.json | 20 - examples/sync/turbo.json | 4 - examples/sync/vite.config.ts | 14 - examples/tenant/README.md | 196 ---------- examples/tenant/package.json | 29 -- examples/tenant/src/backend/registry.ts | 105 ------ examples/tenant/src/frontend/App.tsx | 226 ------------ examples/tenant/src/frontend/index.html | 253 ------------- examples/tenant/src/frontend/main.tsx | 12 - examples/tenant/tests/tenant.test.ts | 120 ------ examples/tenant/tsconfig.json | 20 - examples/tenant/turbo.json | 4 - examples/tenant/vite.config.ts | 14 - examples/workflows/.gitignore | 2 - examples/workflows/README.md | 33 -- examples/workflows/package.json | 19 - examples/workflows/src/registry.ts | 52 --- examples/workflows/src/types.ts | 20 - examples/workflows/tsconfig.json | 43 --- examples/workflows/turbo.json | 4 - 186 files changed, 4734 insertions(+), 6304 deletions(-) delete mode 100644 examples/background-jobs/.gitignore delete mode 100644 examples/background-jobs/README.md delete mode 100644 examples/background-jobs/package.json delete mode 100644 examples/background-jobs/src/registry.ts delete mode 100644 examples/background-jobs/src/server.ts delete mode 100644 examples/background-jobs/src/types.ts delete mode 100644 examples/background-jobs/turbo.json delete mode 100644 examples/better-auth-external-db/.gitignore delete mode 100644 examples/better-auth-external-db/README.md delete mode 100644 examples/better-auth-external-db/better-auth_migrations/2025-06-27T06-53-24.043Z.sql delete mode 100644 examples/better-auth-external-db/package.json delete mode 100644 examples/better-auth-external-db/src/backend/auth.ts delete mode 100644 examples/better-auth-external-db/src/backend/registry.ts delete mode 100644 examples/better-auth-external-db/src/backend/server.ts delete mode 100644 examples/better-auth-external-db/src/frontend/App.tsx delete mode 100644 examples/better-auth-external-db/src/frontend/auth-client.ts delete mode 100644 examples/better-auth-external-db/src/frontend/components/AuthForm.tsx delete mode 100644 examples/better-auth-external-db/src/frontend/components/ChatRoom.tsx delete mode 100644 examples/better-auth-external-db/src/frontend/index.html delete mode 100644 examples/better-auth-external-db/src/frontend/main.tsx delete mode 100644 examples/better-auth-external-db/turbo.json delete mode 100644 examples/bots/.gitignore delete mode 100644 examples/bots/README.md delete mode 100644 examples/bots/package.json delete mode 100644 examples/bots/src/registry.ts delete mode 100644 examples/bots/src/server.ts delete mode 100644 examples/bots/src/types.ts delete mode 100644 examples/bots/src/utils.ts delete mode 100644 examples/bots/turbo.json delete mode 100644 examples/cloudflare-workers-inline-client/README.md delete mode 100644 examples/cloudflare-workers-inline-client/package.json delete mode 100644 examples/cloudflare-workers-inline-client/scripts/client-http.ts delete mode 100644 examples/cloudflare-workers-inline-client/scripts/client-rivetkit.ts delete mode 100644 examples/cloudflare-workers-inline-client/src/index.ts delete mode 100644 examples/cloudflare-workers-inline-client/src/registry.ts delete mode 100644 examples/cloudflare-workers-inline-client/turbo.json delete mode 100644 examples/cloudflare-workers-inline-client/wrangler.json delete mode 100644 examples/counter-next-js/.gitignore delete mode 100644 examples/counter-next-js/README.md delete mode 100644 examples/counter-next-js/next.config.ts delete mode 100644 examples/counter-next-js/package.json delete mode 100644 examples/counter-next-js/scripts/connect.ts delete mode 100644 examples/counter-next-js/src/app/api/rivet/[...all]/route.ts delete mode 100644 examples/counter-next-js/src/app/globals.css delete mode 100644 examples/counter-next-js/src/app/layout.tsx delete mode 100644 examples/counter-next-js/src/app/page.tsx delete mode 100644 examples/counter-next-js/src/components/Counter.tsx delete mode 100644 examples/counter-next-js/src/rivet/registry.ts delete mode 100644 examples/counter-next-js/tsconfig.json delete mode 100644 examples/crdt/README.md delete mode 100644 examples/crdt/src/frontend/App.tsx delete mode 100644 examples/crdt/tests/crdt.test.ts delete mode 100644 examples/crdt/tsconfig.json delete mode 100644 examples/crdt/turbo.json delete mode 100644 examples/database/README.md delete mode 100644 examples/database/src/backend/my-utils.ts delete mode 100644 examples/database/src/backend/registry.ts delete mode 100644 examples/database/src/frontend/App.tsx delete mode 100644 examples/database/src/frontend/index.html delete mode 100644 examples/database/tests/database.test.ts delete mode 100644 examples/database/tsconfig.json delete mode 100644 examples/database/turbo.json delete mode 100644 examples/game/README.md delete mode 100644 examples/game/package.json delete mode 100644 examples/game/src/backend/types.ts delete mode 100644 examples/game/src/frontend/App.tsx delete mode 100644 examples/game/tests/game.test.ts delete mode 100644 examples/game/turbo.json rename examples/{rate => quickstart-actions}/package.json (83%) create mode 100644 examples/quickstart-actions/src/backend/registry.ts rename examples/{crdt => quickstart-actions}/src/backend/server.ts (100%) create mode 100644 examples/quickstart-actions/src/frontend/App.tsx create mode 100644 examples/quickstart-actions/src/frontend/index.html rename examples/{game => quickstart-actions}/src/frontend/main.tsx (98%) create mode 100644 examples/quickstart-actions/tests/actions.test.ts rename examples/{cloudflare-workers-inline-client => quickstart-actions}/tsconfig.json (93%) rename examples/{database => quickstart-actions}/vite.config.ts (92%) rename examples/{rate => quickstart-actions}/vitest.config.ts (81%) rename examples/{database => quickstart-cross-actor-actions}/package.json (82%) create mode 100644 examples/quickstart-cross-actor-actions/src/backend/registry.ts rename examples/{database => quickstart-cross-actor-actions}/src/backend/server.ts (100%) create mode 100644 examples/quickstart-cross-actor-actions/src/frontend/App.tsx create mode 100644 examples/quickstart-cross-actor-actions/src/frontend/index.html rename examples/{database => quickstart-cross-actor-actions}/src/frontend/main.tsx (98%) create mode 100644 examples/quickstart-cross-actor-actions/tests/cross-actor.test.ts rename examples/{background-jobs => quickstart-cross-actor-actions}/tsconfig.json (93%) rename examples/{rate => quickstart-cross-actor-actions}/vite.config.ts (92%) rename examples/{sync => quickstart-cross-actor-actions}/vitest.config.ts (59%) rename examples/{crdt => quickstart-multi-region}/package.json (77%) create mode 100644 examples/quickstart-multi-region/src/backend/registry.ts rename examples/{game => quickstart-multi-region}/src/backend/server.ts (100%) create mode 100644 examples/quickstart-multi-region/src/backend/types.ts create mode 100644 examples/quickstart-multi-region/src/frontend/App.tsx rename examples/{game => quickstart-multi-region}/src/frontend/index.html (63%) rename examples/{crdt => quickstart-multi-region}/src/frontend/main.tsx (98%) create mode 100644 examples/quickstart-multi-region/tests/regions.test.ts rename examples/{game => quickstart-multi-region}/tsconfig.json (100%) rename examples/{game => quickstart-multi-region}/vite.config.ts (100%) rename examples/{game => quickstart-multi-region}/vitest.config.ts (100%) create mode 100644 examples/quickstart-native-websockets/package.json create mode 100644 examples/quickstart-native-websockets/src/backend/registry.ts rename examples/{rate => quickstart-native-websockets}/src/backend/server.ts (100%) create mode 100644 examples/quickstart-native-websockets/src/frontend/App.tsx create mode 100644 examples/quickstart-native-websockets/src/frontend/index.html rename examples/{rate => quickstart-native-websockets}/src/frontend/main.tsx (98%) create mode 100644 examples/quickstart-native-websockets/tests/websocket.test.ts create mode 100644 examples/quickstart-native-websockets/tsconfig.json rename examples/{crdt => quickstart-native-websockets}/vite.config.ts (92%) rename examples/{tenant => quickstart-native-websockets}/vitest.config.ts (59%) create mode 100644 examples/quickstart-realtime/package.json create mode 100644 examples/quickstart-realtime/src/backend/registry.ts rename examples/{sync => quickstart-realtime}/src/backend/server.ts (100%) create mode 100644 examples/quickstart-realtime/src/frontend/App.tsx create mode 100644 examples/quickstart-realtime/src/frontend/index.html create mode 100644 examples/quickstart-realtime/src/frontend/main.tsx create mode 100644 examples/quickstart-realtime/tests/cursors.test.ts create mode 100644 examples/quickstart-realtime/tsconfig.json rename examples/{better-auth-external-db => quickstart-realtime}/vite.config.ts (75%) rename examples/{crdt => quickstart-realtime}/vitest.config.ts (100%) create mode 100644 examples/quickstart-scheduling/package.json create mode 100644 examples/quickstart-scheduling/src/backend/registry.ts rename examples/{tenant => quickstart-scheduling}/src/backend/server.ts (100%) create mode 100644 examples/quickstart-scheduling/src/frontend/App.tsx create mode 100644 examples/quickstart-scheduling/src/frontend/index.html create mode 100644 examples/quickstart-scheduling/src/frontend/main.tsx create mode 100644 examples/quickstart-scheduling/tests/scheduling.test.ts rename examples/{better-auth-external-db => quickstart-scheduling}/tsconfig.json (96%) create mode 100644 examples/quickstart-scheduling/vite.config.ts rename examples/{database => quickstart-scheduling}/vitest.config.ts (100%) create mode 100644 examples/quickstart-state/package.json create mode 100644 examples/quickstart-state/src/backend/registry.ts rename examples/{workflows/src => quickstart-state/src/backend}/server.ts (100%) create mode 100644 examples/quickstart-state/src/frontend/App.tsx rename examples/{crdt => quickstart-state}/src/frontend/index.html (52%) create mode 100644 examples/quickstart-state/src/frontend/main.tsx create mode 100644 examples/quickstart-state/tests/chat.test.ts rename examples/{bots => quickstart-state}/tsconfig.json (93%) create mode 100644 examples/quickstart-state/vite.config.ts create mode 100644 examples/quickstart-state/vitest.config.ts delete mode 100644 examples/rate/README.md delete mode 100644 examples/rate/src/backend/registry.ts delete mode 100644 examples/rate/src/frontend/App.tsx delete mode 100644 examples/rate/src/frontend/index.html delete mode 100644 examples/rate/tests/rate.test.ts delete mode 100644 examples/rate/tsconfig.json delete mode 100644 examples/rate/turbo.json delete mode 100644 examples/sync/README.md delete mode 100644 examples/sync/package.json delete mode 100644 examples/sync/src/backend/registry.ts delete mode 100644 examples/sync/src/backend/types.ts delete mode 100644 examples/sync/src/frontend/App.tsx delete mode 100644 examples/sync/src/frontend/index.html delete mode 100644 examples/sync/src/frontend/main.tsx delete mode 100644 examples/sync/tests/sync.test.ts delete mode 100644 examples/sync/tsconfig.json delete mode 100644 examples/sync/turbo.json delete mode 100644 examples/sync/vite.config.ts delete mode 100644 examples/tenant/README.md delete mode 100644 examples/tenant/package.json delete mode 100644 examples/tenant/src/backend/registry.ts delete mode 100644 examples/tenant/src/frontend/App.tsx delete mode 100644 examples/tenant/src/frontend/index.html delete mode 100644 examples/tenant/src/frontend/main.tsx delete mode 100644 examples/tenant/tests/tenant.test.ts delete mode 100644 examples/tenant/tsconfig.json delete mode 100644 examples/tenant/turbo.json delete mode 100644 examples/tenant/vite.config.ts delete mode 100644 examples/workflows/.gitignore delete mode 100644 examples/workflows/README.md delete mode 100644 examples/workflows/package.json delete mode 100644 examples/workflows/src/registry.ts delete mode 100644 examples/workflows/src/types.ts delete mode 100644 examples/workflows/tsconfig.json delete mode 100644 examples/workflows/turbo.json diff --git a/examples/background-jobs/.gitignore b/examples/background-jobs/.gitignore deleted file mode 100644 index dc6f607390..0000000000 --- a/examples/background-jobs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.actorcore -node_modules diff --git a/examples/background-jobs/README.md b/examples/background-jobs/README.md deleted file mode 100644 index d5298c6be1..0000000000 --- a/examples/background-jobs/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Daily Email Campaign for RivetKit - -Example project demonstrating scheduled background emails with [RivetKit](https://rivetkit.org). - -[Learn More →](https://github.com/rivet-dev/rivetkit) - -[Discord](https://rivet.dev/discord) — [Documentation](https://rivetkit.org) — [Issues](https://github.com/rivet-dev/rivetkit/issues) - -## Getting Started - -### Prerequisites - -- Node.js 20+ -- [Resend](https://resend.com) API key and verified sender domain - -### Installation - -```sh -git clone https://github.com/rivet-dev/rivetkit -cd rivetkit/examples/background-jobs -npm install -``` - -### Development - -```sh -RESEND_API_KEY=your-api-key \ -RESEND_FROM_EMAIL="Example " \ -CAMPAIGN_USER_EMAIL=user@example.com \ -npm run dev -``` - -The example creates a single `emailCampaignUser` actor, stores the recipient email, and schedules a daily task that sends mail through the live Resend API. The server logs the next scheduled send time, and the actor reschedules itself after each successful delivery. Set `CAMPAIGN_USER_ID` to control the actor key when you need to track multiple users. - -## License - -Apache 2.0 diff --git a/examples/background-jobs/package.json b/examples/background-jobs/package.json deleted file mode 100644 index 60e2300a9d..0000000000 --- a/examples/background-jobs/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "example-background-jobs", - "version": "2.0.21", - "private": true, - "type": "module", - "scripts": { - "dev": "tsx src/server.ts", - "check-types": "tsc --noEmit", - "test": "vitest run" - }, - "devDependencies": { - "rivetkit": "workspace:*", - "@types/node": "^22.13.9", - "tsx": "^3.12.7", - "typescript": "^5.7.3", - "vitest": "^3.1.1" - }, - "dependencies": { - "resend": "^4.0.1" - }, - "stableVersion": "0.8.0" -} diff --git a/examples/background-jobs/src/registry.ts b/examples/background-jobs/src/registry.ts deleted file mode 100644 index b73f3c980f..0000000000 --- a/examples/background-jobs/src/registry.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Resend } from "resend"; -import { actor, setup } from "rivetkit"; -import type { CampaignInput, CampaignState } from "./types"; - -const DAY_IN_MS = 86_400_000; - -const EMAIL_SUBJECT = "Daily campaign update"; -const EMAIL_BODY = [ - "

Hi there,

", - "

This is your automated daily campaign email from RivetKit.

", - "

Have a great day!

", -].join(""); - -const emailCampaignUser = actor({ - createState: (_c, input: CampaignInput): CampaignState => ({ - email: input.email, - }), - - onCreate: async (c) => { - const nextSendAt = Date.now() + DAY_IN_MS; - c.state.nextSendAt = nextSendAt; - await c.schedule.at(nextSendAt, "sendDailyEmail"); - }, - - actions: { - sendDailyEmail: async (c) => { - const resend = new Resend(process.env.RESEND_API_KEY ?? ""); - - const { data, error } = await resend.emails.send({ - from: process.env.RESEND_FROM_EMAIL ?? "", - to: c.state.email, - subject: EMAIL_SUBJECT, - html: EMAIL_BODY, - }); - - c.state.lastSentAt = Date.now(); - c.state.lastMessageId = data?.id ?? String(error ?? ""); - - const nextSendAt = Date.now() + DAY_IN_MS; - c.state.nextSendAt = nextSendAt; - await c.schedule.at(nextSendAt, "sendDailyEmail"); - }, - - getStatus: (c) => c.state, - }, -}); - -export const registry = setup({ - use: { emailCampaignUser }, -}); - -export type Registry = typeof registry; diff --git a/examples/background-jobs/src/server.ts b/examples/background-jobs/src/server.ts deleted file mode 100644 index cf55d75aba..0000000000 --- a/examples/background-jobs/src/server.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { registry } from "./registry"; - -const { client } = registry.start(); - -async function main() { - const userEmail = process.env.CAMPAIGN_USER_EMAIL; - const userId = process.env.CAMPAIGN_USER_ID ?? "demo-user"; - - if (!userEmail) { - console.warn( - "Set CAMPAIGN_USER_EMAIL to schedule the daily email campaign (e.g. CAMPAIGN_USER_EMAIL=user@example.com).", - ); - return; - } - - const campaign = client.emailCampaignUser.getOrCreate(userId, { - createWithInput: { email: userEmail }, - }); - const status = await campaign.getStatus(); - - const nextSend = status.nextSendAt - ? new Date(status.nextSendAt).toISOString() - : "not scheduled"; - - console.log( - `Next daily email for ${status.email} scheduled at ${nextSend}`, - ); -} - -void main(); diff --git a/examples/background-jobs/src/types.ts b/examples/background-jobs/src/types.ts deleted file mode 100644 index f865c34eef..0000000000 --- a/examples/background-jobs/src/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type CampaignInput = { - email: string; -}; - -export type CampaignState = CampaignInput & { - lastSentAt?: number; - lastMessageId?: string; - nextSendAt?: number; -}; diff --git a/examples/background-jobs/turbo.json b/examples/background-jobs/turbo.json deleted file mode 100644 index 29d4cb2625..0000000000 --- a/examples/background-jobs/turbo.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://turbo.build/schema.json", - "extends": ["//"] -} diff --git a/examples/better-auth-external-db/.gitignore b/examples/better-auth-external-db/.gitignore deleted file mode 100644 index 959e16e69e..0000000000 --- a/examples/better-auth-external-db/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -# Dependencies -node_modules -.pnpm-debug.log* - -# Build outputs -dist -.turbo - -# Environment variables -.env -.env.local -.env.production.local -.env.development.local - -# Database -*.sqlite -*.db - -# IDE -.vscode -.idea - -# OS -.DS_Store -Thumbs.db - -# Logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* \ No newline at end of file diff --git a/examples/better-auth-external-db/README.md b/examples/better-auth-external-db/README.md deleted file mode 100644 index 9e8aff5b92..0000000000 --- a/examples/better-auth-external-db/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Better Auth with External Database for RivetKit - -Example project demonstrating authentication integration with [RivetKit](https://rivetkit.org) using Better Auth and SQLite database. - -[Learn More →](https://github.com/rivet-dev/rivetkit) - -[Discord](https://rivet.dev/discord) — [Documentation](https://rivetkit.org) — [Issues](https://github.com/rivet-dev/rivetkit/issues) - -## Getting Started - -### Prerequisites - -- Node.js 18+ -- npm or pnpm - -### Installation - -```sh -git clone https://github.com/rivet-dev/rivetkit -cd rivetkit/examples/better-auth-external-db -npm install -``` - -### Development - -```sh -npm run dev -``` - -The database migrations will run automatically on startup. Open your browser to `http://localhost:5173` to see the frontend and the backend will be running on `http://localhost:8080`. - -## Features - -- **Authentication**: Email/password authentication using Better Auth -- **Protected Actors**: Rivet Actors with authentication via `onAuth` hook -- **Real-time Chat**: Authenticated chat room with real-time messaging -- **External Database**: Shows how to configure Better Auth with external database (SQLite example) - -## How It Works - -1. **Better Auth Setup**: Configured with SQLite database for persistent user storage (auto-migrated in development) -2. **Protected Actor**: The `chatRoom` actor uses the `onAuth` hook to verify user sessions -3. **Frontend Integration**: React components handle authentication flow and chat interface -4. **Session Management**: Better Auth handles session creation, validation, and cleanup -5. **Auto-Migration**: Database schema is automatically migrated when starting the development server - -## Database Commands - -- `npm run db:generate` - Generate migration files for database schema changes -- `npm run db:migrate` - Apply migrations to the database (used in production) - -## Key Files - -- `src/backend/auth.ts` - Better Auth configuration with SQLite database -- `src/backend/registry.ts` - Rivet Actor with authentication -- `src/frontend/components/AuthForm.tsx` - Login/signup form -- `src/frontend/components/ChatRoom.tsx` - Authenticated chat interface -- `auth.sqlite` - SQLite database file (auto-created) - -## License - -Apache 2.0 diff --git a/examples/better-auth-external-db/better-auth_migrations/2025-06-27T06-53-24.043Z.sql b/examples/better-auth-external-db/better-auth_migrations/2025-06-27T06-53-24.043Z.sql deleted file mode 100644 index 13b82ff1d2..0000000000 --- a/examples/better-auth-external-db/better-auth_migrations/2025-06-27T06-53-24.043Z.sql +++ /dev/null @@ -1,7 +0,0 @@ -create table "user" ("id" text not null primary key, "name" text not null, "email" text not null unique, "emailVerified" integer not null, "image" text, "createdAt" date not null, "updatedAt" date not null); - -create table "session" ("id" text not null primary key, "expiresAt" date not null, "token" text not null unique, "createdAt" date not null, "updatedAt" date not null, "ipAddress" text, "userAgent" text, "userId" text not null references "user" ("id")); - -create table "account" ("id" text not null primary key, "accountId" text not null, "providerId" text not null, "userId" text not null references "user" ("id"), "accessToken" text, "refreshToken" text, "idToken" text, "accessTokenExpiresAt" date, "refreshTokenExpiresAt" date, "scope" text, "password" text, "createdAt" date not null, "updatedAt" date not null); - -create table "verification" ("id" text not null primary key, "identifier" text not null, "value" text not null, "expiresAt" date not null, "createdAt" date, "updatedAt" date); \ No newline at end of file diff --git a/examples/better-auth-external-db/package.json b/examples/better-auth-external-db/package.json deleted file mode 100644 index a4a3760e11..0000000000 --- a/examples/better-auth-external-db/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "example-better-auth-external-db", - "version": "2.0.21", - "private": true, - "type": "module", - "scripts": { - "dev": "pnpm db:migrate && concurrently \"npm run dev:backend\" \"npm run dev:frontend\"", - "dev:backend": "tsx --watch src/backend/server.ts", - "dev:frontend": "vite", - "build": "vite build", - "check-types": "tsc --noEmit", - "test": "vitest run", - "db:generate": "pnpm dlx @better-auth/cli@latest generate --config src/backend/auth.ts", - "db:migrate": "pnpm dlx @better-auth/cli@latest migrate --config src/backend/auth.ts -y" - }, - "devDependencies": { - "rivetkit": "workspace:*", - "@types/node": "^22.13.9", - "@types/react": "^18.2.0", - "@types/react-dom": "^18.2.0", - "@vitejs/plugin-react": "^4.2.0", - "concurrently": "^8.2.2", - "tsx": "^3.12.7", - "typescript": "^5.5.2", - "vite": "^5.0.0", - "vitest": "^3.1.1" - }, - "dependencies": { - "@hono/node-server": "^1.14.0", - "@rivetkit/react": "workspace:*", - "@types/better-sqlite3": "^7.6.13", - "better-auth": "^1.0.1", - "better-sqlite3": "^11.10.0", - "hono": "^4.7.0", - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "stableVersion": "0.8.0" -} diff --git a/examples/better-auth-external-db/src/backend/auth.ts b/examples/better-auth-external-db/src/backend/auth.ts deleted file mode 100644 index 8711420d95..0000000000 --- a/examples/better-auth-external-db/src/backend/auth.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { betterAuth } from "better-auth"; -import Database from "better-sqlite3"; - -export const auth = betterAuth({ - database: new Database("/tmp/auth.sqlite"), - trustedOrigins: ["http://localhost:5173"], - emailAndPassword: { - enabled: true, - }, -}); diff --git a/examples/better-auth-external-db/src/backend/registry.ts b/examples/better-auth-external-db/src/backend/registry.ts deleted file mode 100644 index 3773d83952..0000000000 --- a/examples/better-auth-external-db/src/backend/registry.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { actor, setup } from "rivetkit"; - -interface State { - messages: Message[]; -} - -interface Message { - id: string; - userId: string; - username: string; - message: string; - timestamp: number; -} - -export const chatRoom = actor({ - state: { - messages: [], - } as State, - actions: { - sendMessage: (c, message: string) => { - // TODO: Add back using onBeforeConnect - // // Access Better Auth with c.conn.auth - // const newMessage = { - // id: crypto.randomUUID(), - // userId: c.conn.auth.user.id, - // username: c.conn.auth.user.name, - // message, - // timestamp: Date.now(), - // }; - // - // c.state.messages.push(newMessage); - // c.broadcast("newMessage", newMessage); - // - // return newMessage; - }, - getMessages: (c) => { - return c.state.messages; - }, - }, -}); - -export const registry = setup({ - use: { chatRoom }, -}); diff --git a/examples/better-auth-external-db/src/backend/server.ts b/examples/better-auth-external-db/src/backend/server.ts deleted file mode 100644 index 637a609a70..0000000000 --- a/examples/better-auth-external-db/src/backend/server.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { serve } from "@hono/node-server"; -import { Hono } from "hono"; -import { cors } from "hono/cors"; -import { ALLOWED_PUBLIC_HEADERS } from "rivetkit"; -import { auth } from "./auth"; -import { registry } from "./registry"; - -// Start RivetKit -registry.start(); - -// Setup router -const app = new Hono(); - -app.use( - "*", - cors({ - origin: "http://localhost:5173", - // Need to allow custom headers used in RivetKit - allowHeaders: ["Authorization", ...ALLOWED_PUBLIC_HEADERS], - allowMethods: ["POST", "GET", "OPTIONS"], - exposeHeaders: ["Content-Length"], - maxAge: 600, - credentials: true, - }), -); - -// Mount Better Auth routes -app.on(["GET", "POST"], "/api/auth/**", (c) => auth.handler(c.req.raw)); - -serve({ fetch: app.fetch, port: 8080 }); -console.log("Listening on port 8080"); diff --git a/examples/better-auth-external-db/src/frontend/App.tsx b/examples/better-auth-external-db/src/frontend/App.tsx deleted file mode 100644 index 49bc8abd9f..0000000000 --- a/examples/better-auth-external-db/src/frontend/App.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { useEffect, useState } from "react"; -import { authClient } from "./auth-client"; -import { AuthForm } from "./components/AuthForm"; -import { ChatRoom } from "./components/ChatRoom"; - -function App() { - const [user, setUser] = useState<{ id: string; email: string } | null>(null); - const [loading, setLoading] = useState(true); - - useEffect(() => { - // Check if user is already authenticated - const checkAuth = async () => { - try { - const session = await authClient.getSession(); - if (session.data?.user) { - setUser(session.data.user); - } - } catch (error) { - console.error("Auth check failed:", error); - } finally { - setLoading(false); - } - }; - - checkAuth(); - }, []); - - const handleAuthSuccess = async () => { - try { - const session = await authClient.getSession(); - if (session.data?.user) { - setUser(session.data.user); - } - } catch (error) { - console.error("Failed to get user after auth:", error); - } - }; - - const handleSignOut = () => { - setUser(null); - }; - - if (loading) { - return ( -
- Loading... -
- ); - } - - return ( -
-
-

- RivetKit with Better Auth -

- - {user ? ( - - ) : ( - - )} -
-
- ); -} - -export default App; diff --git a/examples/better-auth-external-db/src/frontend/auth-client.ts b/examples/better-auth-external-db/src/frontend/auth-client.ts deleted file mode 100644 index 64c8a7b5a5..0000000000 --- a/examples/better-auth-external-db/src/frontend/auth-client.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createAuthClient } from "better-auth/react"; - -export const authClient = createAuthClient({ - baseURL: "http://localhost:8080", -}); diff --git a/examples/better-auth-external-db/src/frontend/components/AuthForm.tsx b/examples/better-auth-external-db/src/frontend/components/AuthForm.tsx deleted file mode 100644 index 643ce707c7..0000000000 --- a/examples/better-auth-external-db/src/frontend/components/AuthForm.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { useState } from "react"; -import { authClient } from "../auth-client"; - -interface AuthFormProps { - onAuthSuccess: () => void; -} - -export function AuthForm({ onAuthSuccess }: AuthFormProps) { - const [isLogin, setIsLogin] = useState(true); - const [email, setEmail] = useState(""); - const [name, setName] = useState(""); - const [password, setPassword] = useState(""); - const [error, setError] = useState(""); - const [loading, setLoading] = useState(false); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(""); - setLoading(true); - - try { - if (isLogin) { - await authClient.signIn.email({ - email, - password, - }); - } else { - await authClient.signUp.email({ - email, - name, - password, - }); - } - onAuthSuccess(); - } catch (err) { - setError(err instanceof Error ? err.message : "Authentication failed"); - } finally { - setLoading(false); - } - }; - - return ( -
-

{isLogin ? "Sign In" : "Sign Up"}

- -
-
- - setEmail(e.target.value)} - required - style={{ width: "100%", padding: "8px", marginTop: "5px" }} - /> -
- - {!isLogin && ( -
- - setName(e.target.value)} - required - style={{ width: "100%", padding: "8px", marginTop: "5px" }} - /> -
- )} - -
- - setPassword(e.target.value)} - required - style={{ width: "100%", padding: "8px", marginTop: "5px" }} - /> -
- - {error &&
{error}
} - - -
- -
- -
-
- ); -} diff --git a/examples/better-auth-external-db/src/frontend/components/ChatRoom.tsx b/examples/better-auth-external-db/src/frontend/components/ChatRoom.tsx deleted file mode 100644 index bba4c8c398..0000000000 --- a/examples/better-auth-external-db/src/frontend/components/ChatRoom.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import { createRivetKit } from "@rivetkit/react"; -import { useEffect, useState } from "react"; -import type { registry } from "../../backend/registry"; -import { authClient } from "../auth-client"; - -const { useActor } = createRivetKit("http://localhost:8080"); - -interface ChatRoomProps { - user: { id: string; email: string }; - onSignOut: () => void; -} - -export function ChatRoom({ user, onSignOut }: ChatRoomProps) { - const [message, setMessage] = useState(""); - const [messages, setMessages] = useState< - Array<{ - id: string; - userId: string; - username: string; - message: string; - timestamp: number; - }> - >([]); - const [roomId] = useState("general"); - - const chatRoom = useActor({ - name: "chatRoom", - key: [roomId], - }); - - // Listen for new messages - chatRoom.useEvent("newMessage", (newMessage) => { - setMessages((prev) => [ - ...prev, - newMessage as { - id: string; - userId: string; - username: string; - message: string; - timestamp: number; - }, - ]); - }); - - // Load initial messages when connected - useEffect(() => { - if (chatRoom.connection) { - chatRoom.connection.getMessages().then((initialMessages) => { - setMessages(initialMessages); - }); - } - }, [chatRoom.connection]); - - const handleSendMessage = async (e: React.FormEvent) => { - e.preventDefault(); - if (!message.trim() || !chatRoom.connection) return; - - try { - await chatRoom.connection.sendMessage(message.trim()); - setMessage(""); - } catch (error) { - console.error("Failed to send message:", error); - } - }; - - const handleSignOut = async () => { - await authClient.signOut(); - onSignOut(); - }; - - return ( -
-
-
-

Chat Room: {roomId}

-

Logged in as: {user.email}

-
- -
- -
- {messages.length === 0 ? ( -

- No messages yet. Start the conversation! -

- ) : ( - messages.map((msg) => ( -
-
- {msg.username} • {new Date(msg.timestamp).toLocaleTimeString()} -
-
{msg.message}
-
- )) - )} -
- -
- setMessage(e.target.value)} - placeholder="Type your message..." - style={{ - flex: 1, - padding: "10px", - border: "1px solid #ccc", - borderRadius: "4px", - }} - /> - -
- -
- Connection Status: {chatRoom.connection ? "Connected" : "Connecting..."} -
-
- ); -} diff --git a/examples/better-auth-external-db/src/frontend/index.html b/examples/better-auth-external-db/src/frontend/index.html deleted file mode 100644 index c8973df5da..0000000000 --- a/examples/better-auth-external-db/src/frontend/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - RivetKit + Better Auth - - -
- - - \ No newline at end of file diff --git a/examples/better-auth-external-db/src/frontend/main.tsx b/examples/better-auth-external-db/src/frontend/main.tsx deleted file mode 100644 index 6d0ba79496..0000000000 --- a/examples/better-auth-external-db/src/frontend/main.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import App from "./App"; - -ReactDOM.createRoot(document.getElementById("root")!).render( - - - , -); diff --git a/examples/better-auth-external-db/turbo.json b/examples/better-auth-external-db/turbo.json deleted file mode 100644 index 29d4cb2625..0000000000 --- a/examples/better-auth-external-db/turbo.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://turbo.build/schema.json", - "extends": ["//"] -} diff --git a/examples/bots/.gitignore b/examples/bots/.gitignore deleted file mode 100644 index dc6f607390..0000000000 --- a/examples/bots/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.actorcore -node_modules diff --git a/examples/bots/README.md b/examples/bots/README.md deleted file mode 100644 index 8aab62f7fe..0000000000 --- a/examples/bots/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Discord Bot Gateway for RivetKit - -Example project demonstrating Discord Gateway lifecycle management with [RivetKit](https://rivetkit.org). - -[Learn More →](https://github.com/rivet-dev/rivetkit) - -[Discord](https://rivet.dev/discord) — [Documentation](https://rivetkit.org) — [Issues](https://github.com/rivet-dev/rivetkit/issues) - -## Getting Started - -### Prerequisites - -- Node.js 22+ -- Discord application with a bot token and the **Message Content Intent** enabled - -### Installation - -```sh -git clone https://github.com/rivet-dev/rivetkit -cd rivetkit/examples/bots -npm install -``` - -### Development - -```sh -DISCORD_BOT_TOKEN=your-token npm run dev -``` - -Invite the bot to a server, then send messages such as "hello" or "ping" in a text channel to see automatic replies and state updates. - -## License - -Apache 2.0 diff --git a/examples/bots/package.json b/examples/bots/package.json deleted file mode 100644 index 717a1b14ce..0000000000 --- a/examples/bots/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "example-bots", - "version": "2.0.21", - "private": true, - "type": "module", - "scripts": { - "dev": "tsx src/server.ts", - "check-types": "tsc --noEmit", - "test": "vitest run" - }, - "devDependencies": { - "rivetkit": "workspace:*", - "@types/node": "^22.13.9", - "tsx": "^3.12.7", - "typescript": "^5.7.3", - "vitest": "^3.1.1" - }, - "dependencies": { - "@hono/node-server": "^1.14.3", - "hono": "^4.7.0" - }, - "stableVersion": "0.8.0" -} diff --git a/examples/bots/src/registry.ts b/examples/bots/src/registry.ts deleted file mode 100644 index d15b2c04a4..0000000000 --- a/examples/bots/src/registry.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { actor, setup } from "rivetkit"; -import type { WorkspaceInput, WorkspaceState } from "./types"; -import { sendSlackMessage } from "./utils"; - -const DAY_IN_MS = 86_400_000; - -export const slackWorkspaceBot = actor({ - createState: async (c, input): Promise => { - // Schedule first daily report - const nextReportAt = Date.now() + DAY_IN_MS; - await c.schedule.at(nextReportAt, "sendDailyReport"); - - return { - workspaceId: (input as WorkspaceInput).workspaceId, - channelId: (input as WorkspaceInput).channelId, - messageCount: 0, - nextReportAt, - }; - }, - - actions: { - // Called by the Slack webhook in the Hono server - handleMessage: async (c, text: string) => { - c.state.messageCount++; - - const msg = text.toLowerCase().trim(); - let response: string | undefined; - - if (msg === "ping") { - response = "pong"; - } else if (msg === "count") { - response = `I've received ${c.state.messageCount} messages in this workspace`; - } else if (msg === "help") { - response = - "Available commands:\n• ping - responds with pong\n• count - shows message count\n• help - shows this message"; - } - - if (response) { - await sendSlackMessage(c.state.channelId, response); - } - }, - - sendDailyReport: async (c) => { - // Schedule next report - const nextReportAt = Date.now() + DAY_IN_MS; - c.state.nextReportAt = nextReportAt; - await c.schedule.at(nextReportAt, "sendDailyReport"); - - // Send report to Slack if we have a channel - if (c.state.channelId) { - const report = `Daily report: ${c.state.messageCount} messages received so far`; - await sendSlackMessage(c.state.channelId, report); - } - }, - }, -}); - -export const registry = setup({ - use: { slackWorkspaceBot }, -}); - -export type Registry = typeof registry; diff --git a/examples/bots/src/server.ts b/examples/bots/src/server.ts deleted file mode 100644 index e373de7fc0..0000000000 --- a/examples/bots/src/server.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { serve } from "@hono/node-server"; -import { Hono } from "hono"; -import { registry } from "./registry"; - -const { client } = registry.start(); - -const app = new Hono(); - -app.post("/slack/events", async (c) => { - const body = await c.req.json(); - - if (body.type === "url_verification") { - return c.json({ challenge: body.challenge }); - } - - if (body.type === "event_callback" && body.event.type === "message") { - const workspaceId = body.team_id; - const channelId = body.event.channel; - - const bot = client.slackWorkspaceBot.getOrCreate(workspaceId, { - createWithInput: { - workspaceId, - channelId, - }, - }); - - await bot.handleMessage(body.event.text); - - return c.json({ ok: true }); - } - - return c.json({ ok: false }, 400); -}); - -serve({ fetch: app.fetch, port: 8080 }); -console.log("Slack bot listening on port 8080"); diff --git a/examples/bots/src/types.ts b/examples/bots/src/types.ts deleted file mode 100644 index 8b3b2ffddc..0000000000 --- a/examples/bots/src/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type WorkspaceInput = { - workspaceId: string; - channelId: string; -}; - -export type WorkspaceState = WorkspaceInput & { - messageCount: number; - nextReportAt?: number; -}; diff --git a/examples/bots/src/utils.ts b/examples/bots/src/utils.ts deleted file mode 100644 index 144f3e4193..0000000000 --- a/examples/bots/src/utils.ts +++ /dev/null @@ -1,21 +0,0 @@ -export async function sendSlackMessage( - channelId: string, - text: string, -): Promise { - const token = process.env.SLACK_BOT_TOKEN; - if (!token) { - return; - } - - await fetch("https://slack.com/api/chat.postMessage", { - method: "POST", - headers: { - Authorization: `Bearer ${token}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - channel: channelId, - text, - }), - }); -} diff --git a/examples/bots/turbo.json b/examples/bots/turbo.json deleted file mode 100644 index 29d4cb2625..0000000000 --- a/examples/bots/turbo.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://turbo.build/schema.json", - "extends": ["//"] -} diff --git a/examples/cloudflare-workers-hono/wrangler.json b/examples/cloudflare-workers-hono/wrangler.json index f5b84c4ef6..29b055cf3f 100644 --- a/examples/cloudflare-workers-hono/wrangler.json +++ b/examples/cloudflare-workers-hono/wrangler.json @@ -6,7 +6,7 @@ "migrations": [ { "tag": "v1", - "new_sqlite_classes": ["ActorHandler"] + "new_classes": ["ActorHandler"] } ], "durable_objects": { diff --git a/examples/cloudflare-workers-inline-client/README.md b/examples/cloudflare-workers-inline-client/README.md deleted file mode 100644 index a522c3f103..0000000000 --- a/examples/cloudflare-workers-inline-client/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Cloudflare Workers Inline Client Example - -Simple example demonstrating accessing Rivet Actors via Cloudflare Workers without exposing a public API. This uses the `createInlineClient` function to connect directly to your Durable Object. - -## Getting Started - -Install dependencies: - -```sh -pnpm install -``` - -Start the development server: - -```sh -pnpm run dev -``` - -In a separate terminal, test the endpoint: - -```sh -pnpm run client-http -``` - -Or: - -```sh -pnpm run client-rivetkit -``` - -## License - -Apache 2.0 diff --git a/examples/cloudflare-workers-inline-client/package.json b/examples/cloudflare-workers-inline-client/package.json deleted file mode 100644 index c02f508085..0000000000 --- a/examples/cloudflare-workers-inline-client/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "example-cloudflare-workers-inline-client", - "version": "2.0.21", - "private": true, - "type": "module", - "scripts": { - "dev": "wrangler dev", - "deploy": "wrangler deploy", - "check-types": "tsc --noEmit", - "client-http": "tsx scripts/client-http.ts", - "client-rivetkit": "tsx scripts/client-rivetkit.ts" - }, - "devDependencies": { - "@cloudflare/workers-types": "^4.20250129.0", - "@types/node": "^22.13.9", - "tsx": "^3.12.7", - "typescript": "^5.5.2", - "wrangler": "^4.22.0" - }, - "dependencies": { - "rivetkit": "workspace:*", - "@rivetkit/cloudflare-workers": "workspace:*" - }, - "stableVersion": "0.8.0" -} diff --git a/examples/cloudflare-workers-inline-client/scripts/client-http.ts b/examples/cloudflare-workers-inline-client/scripts/client-http.ts deleted file mode 100644 index bdd5d51e5d..0000000000 --- a/examples/cloudflare-workers-inline-client/scripts/client-http.ts +++ /dev/null @@ -1,24 +0,0 @@ -const baseUrl = process.env.BASE_URL ?? "http://localhost:8787"; - -async function main() { - console.log("🚀 Cloudflare Workers Client Demo"); - - try { - for (let i = 0; i < 3; i++) { - // Increment counter - console.log("Incrementing counter..."); - const response = await fetch(`${baseUrl}/increment/demo`, { - method: "POST", - }); - const result = await response.text(); - console.log(result); - } - - console.log("✅ Demo completed!"); - } catch (error) { - console.error("❌ Error:", error); - process.exit(1); - } -} - -main().catch(console.error); diff --git a/examples/cloudflare-workers-inline-client/scripts/client-rivetkit.ts b/examples/cloudflare-workers-inline-client/scripts/client-rivetkit.ts deleted file mode 100644 index 0cd1fbce46..0000000000 --- a/examples/cloudflare-workers-inline-client/scripts/client-rivetkit.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { createClient } from "rivetkit/client"; -import type { registry } from "../src/registry"; - -// Create RivetKit client -const client = createClient( - process.env.RIVETKIT_ENDPOINT ?? "http://localhost:8787/rivet", -); - -async function main() { - console.log("🚀 Cloudflare Workers Client Demo"); - - try { - const counter = client.counter.getOrCreate("demo").connect(); - - for (let i = 0; i < 3; i++) { - // Increment counter - console.log("Incrementing counter..."); - const result1 = await counter.increment(1); - console.log("New count:", result1); - } - - await counter.dispose(); - - console.log("✅ Demo completed!"); - } catch (error) { - console.error("❌ Error:", error); - process.exit(1); - } -} - -main().catch(console.error); diff --git a/examples/cloudflare-workers-inline-client/src/index.ts b/examples/cloudflare-workers-inline-client/src/index.ts deleted file mode 100644 index 0c7db7d9fc..0000000000 --- a/examples/cloudflare-workers-inline-client/src/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { createInlineClient } from "@rivetkit/cloudflare-workers"; -import { registry } from "./registry"; - -const { - client, - fetch: rivetFetch, - ActorHandler, -} = createInlineClient(registry); - -// IMPORTANT: Your Durable Object must be exported here -export { ActorHandler }; - -export default { - fetch: async (request, env, ctx) => { - const url = new URL(request.url); - - // Custom request handler - if ( - request.method === "POST" && - url.pathname.startsWith("/increment/") - ) { - const name = url.pathname.slice("/increment/".length); - - const counter = client.counter.getOrCreate(name); - const newCount = await counter.increment(1); - - return new Response(`New Count: ${newCount}`, { - headers: { "Content-Type": "text/plain" }, - }); - } - - // Optional: If you want to access Rivet Actors publicly, mount the path - if (url.pathname.startsWith("/rivet")) { - const strippedPath = url.pathname.substring("/rivet".length); - url.pathname = strippedPath; - console.log("URL", url.toString()); - const modifiedRequest = new Request(url.toString(), request); - return rivetFetch(modifiedRequest, env, ctx); - } - - return new Response("Not Found", { status: 404 }); - }, -} satisfies ExportedHandler; diff --git a/examples/cloudflare-workers-inline-client/src/registry.ts b/examples/cloudflare-workers-inline-client/src/registry.ts deleted file mode 100644 index 4afe732a3c..0000000000 --- a/examples/cloudflare-workers-inline-client/src/registry.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { actor, setup } from "rivetkit"; - -export const counter = actor({ - state: { count: 0 }, - actions: { - increment: (c, x: number) => { - c.state.count += x; - c.broadcast("newCount", c.state.count); - return c.state.count; - }, - }, -}); - -export const registry = setup({ - use: { counter }, -}); diff --git a/examples/cloudflare-workers-inline-client/turbo.json b/examples/cloudflare-workers-inline-client/turbo.json deleted file mode 100644 index 29d4cb2625..0000000000 --- a/examples/cloudflare-workers-inline-client/turbo.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://turbo.build/schema.json", - "extends": ["//"] -} diff --git a/examples/cloudflare-workers-inline-client/wrangler.json b/examples/cloudflare-workers-inline-client/wrangler.json deleted file mode 100644 index f5b84c4ef6..0000000000 --- a/examples/cloudflare-workers-inline-client/wrangler.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "rivetkit-cloudflare-workers-example", - "main": "src/index.ts", - "compatibility_date": "2025-01-20", - "compatibility_flags": ["nodejs_compat"], - "migrations": [ - { - "tag": "v1", - "new_sqlite_classes": ["ActorHandler"] - } - ], - "durable_objects": { - "bindings": [ - { - "name": "ACTOR_DO", - "class_name": "ActorHandler" - } - ] - }, - "kv_namespaces": [ - { - "binding": "ACTOR_KV", - "id": "example_namespace", - "preview_id": "example_namespace_preview" - } - ], - "observability": { - "enabled": true - } -} diff --git a/examples/cloudflare-workers/src/registry.ts b/examples/cloudflare-workers/src/registry.ts index 4afe732a3c..24277ebeb8 100644 --- a/examples/cloudflare-workers/src/registry.ts +++ b/examples/cloudflare-workers/src/registry.ts @@ -1,7 +1,7 @@ import { actor, setup } from "rivetkit"; export const counter = actor({ - state: { count: 0 }, + state: { count: 0, connectionCount: 0, messageCount: 0 }, actions: { increment: (c, x: number) => { c.state.count += x; diff --git a/examples/cloudflare-workers/wrangler.json b/examples/cloudflare-workers/wrangler.json index f5b84c4ef6..29b055cf3f 100644 --- a/examples/cloudflare-workers/wrangler.json +++ b/examples/cloudflare-workers/wrangler.json @@ -6,7 +6,7 @@ "migrations": [ { "tag": "v1", - "new_sqlite_classes": ["ActorHandler"] + "new_classes": ["ActorHandler"] } ], "durable_objects": { diff --git a/examples/counter-next-js/.gitignore b/examples/counter-next-js/.gitignore deleted file mode 100644 index 5ef6a52078..0000000000 --- a/examples/counter-next-js/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/examples/counter-next-js/README.md b/examples/counter-next-js/README.md deleted file mode 100644 index 4409287b70..0000000000 --- a/examples/counter-next-js/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Counter for RivetKit (Next.js) - -Example Next.js project demonstrating basic actor state management and real-time updates with [RivetKit](https://rivetkit.org). - -This example combines the counter functionality from the basic counter example with a Next.js application structure. - -[Learn More →](https://github.com/rivet-dev/rivetkit) - -[Discord](https://rivet.dev/discord) — [Documentation](https://rivetkit.org) — [Issues](https://github.com/rivet-dev/rivetkit/issues) - -## Getting Started - -### Prerequisites - -- Node.js - -### Installation - -```sh -git clone https://github.com/rivet-dev/rivetkit -cd rivetkit/examples/counter-next-js -pnpm install -``` - -### Development - -```sh -pnpm dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the counter in action. - -The counter is shared across all clients using the same Counter ID. Try opening the page in multiple tabs or browsers to see real-time synchronization! - -### Testing with the Connect Script - -Run the connect script to interact with the counter from the command line: - -```sh -pnpm connect -``` - -This will connect to the counter and increment it every second. You'll see the updates in both the terminal and the web interface! - -## Features - -- Real-time counter synchronization across multiple clients -- Next.js 15 with App Router -- TypeScript support -- Customizable counter IDs for multiple independent counters - -## License - -Apache 2.0 diff --git a/examples/counter-next-js/next.config.ts b/examples/counter-next-js/next.config.ts deleted file mode 100644 index 7921f35d74..0000000000 --- a/examples/counter-next-js/next.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - /* config options here */ -}; - -export default nextConfig; diff --git a/examples/counter-next-js/package.json b/examples/counter-next-js/package.json deleted file mode 100644 index 5397b59dbe..0000000000 --- a/examples/counter-next-js/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "example-counter-next-js", - "version": "2.0.21", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint", - "connect": "tsx scripts/connect.ts" - }, - "dependencies": { - "react": "19.1.0", - "react-dom": "19.1.0", - "next": "15.4.5", - "@rivetkit/next-js": "workspace:*", - "rivetkit": "workspace:*" - }, - "devDependencies": { - "typescript": "^5", - "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", - "tsx": "^3.12.7" - }, - "stableVersion": "0.8.0" -} diff --git a/examples/counter-next-js/scripts/connect.ts b/examples/counter-next-js/scripts/connect.ts deleted file mode 100644 index 9d00969e18..0000000000 --- a/examples/counter-next-js/scripts/connect.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createClient } from "rivetkit/client"; -import type { registry } from "../src/rivet/registry"; - -async function main() { - const client = createClient("http://localhost:3000/api/rivet"); - - const counter = client.counter.getOrCreate().connect(); - - counter.on("newCount", (count: number) => console.log("Event:", count)); - - while (true) { - const out = await counter.increment(1); - console.log("RPC:", out); - - await new Promise((resolve) => setTimeout(resolve, 1000)); - } -} - -main(); diff --git a/examples/counter-next-js/src/app/api/rivet/[...all]/route.ts b/examples/counter-next-js/src/app/api/rivet/[...all]/route.ts deleted file mode 100644 index a9ebd3e0a7..0000000000 --- a/examples/counter-next-js/src/app/api/rivet/[...all]/route.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { toNextHandler } from "@rivetkit/next-js"; -import { registry } from "@/rivet/registry"; - -export const maxDuration = 300; - -export const { GET, POST, PUT, PATCH, HEAD, OPTIONS } = toNextHandler(registry); diff --git a/examples/counter-next-js/src/app/globals.css b/examples/counter-next-js/src/app/globals.css deleted file mode 100644 index 87993fb782..0000000000 --- a/examples/counter-next-js/src/app/globals.css +++ /dev/null @@ -1,220 +0,0 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', Roboto, sans-serif; - background: #000000; - min-height: 100vh; - display: flex; - align-items: center; - justify-content: center; - color: #ffffff; - padding: 20px; -} - -.counter-app { - width: 100%; - max-width: 500px; - margin: 0 auto; -} - -.counter-container { - background: #1c1c1e; - border-radius: 16px; - border: 1px solid #2c2c2e; - overflow: hidden; -} - -.counter-header { - padding: 24px; - border-bottom: 1px solid #2c2c2e; - display: flex; - justify-content: space-between; - align-items: center; -} - -.counter-header h1 { - font-size: 24px; - font-weight: 600; - color: #ffffff; -} - -.status-indicator { - display: flex; - align-items: center; - gap: 8px; - font-size: 14px; - font-weight: 500; -} - -.status-dot { - width: 8px; - height: 8px; - border-radius: 50%; - transition: background-color 0.2s ease; -} - -.status-indicator.connected .status-dot { - background-color: #30d158; -} - -.status-indicator.disconnected .status-dot { - background-color: #ff3b30; -} - -.status-indicator.connected { - color: #30d158; -} - -.status-indicator.disconnected { - color: #ff3b30; -} - -.counter-settings { - padding: 24px; - border-bottom: 1px solid #2c2c2e; -} - -.setting-group { - margin-bottom: 0; -} - -.setting-group label { - display: block; - font-size: 14px; - font-weight: 600; - color: #8e8e93; - margin-bottom: 8px; -} - -.setting-input { - width: 100%; - padding: 10px 14px; - border: 1px solid #3a3a3c; - border-radius: 8px; - font-size: 14px; - transition: all 0.2s ease; - background: #2c2c2e; - color: #ffffff; -} - -.setting-input:focus { - outline: none; - border-color: #007aff; - box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1); -} - -.setting-input::placeholder { - color: #8e8e93; -} - -.counter-display { - padding: 48px 24px; - text-align: center; - background: #000000; -} - -.count-value { - font-size: 72px; - font-weight: 700; - color: #007aff; - line-height: 1; - margin-bottom: 12px; - font-variant-numeric: tabular-nums; -} - -.count-label { - font-size: 16px; - color: #8e8e93; - font-weight: 500; -} - -.counter-controls { - padding: 24px; - display: flex; - gap: 12px; - border-bottom: 1px solid #2c2c2e; -} - -.counter-button { - flex: 1; - padding: 16px; - border: none; - border-radius: 12px; - font-size: 18px; - font-weight: 600; - cursor: pointer; - transition: all 0.2s ease; - color: white; -} - -.counter-button.increment-1 { - background: #007aff; -} - -.counter-button.increment-1:hover:not(:disabled) { - background: #0056cc; - transform: translateY(-2px); -} - -.counter-button.increment-5 { - background: #34c759; -} - -.counter-button.increment-5:hover:not(:disabled) { - background: #28a745; - transform: translateY(-2px); -} - -.counter-button.increment-10 { - background: #ff9500; -} - -.counter-button.increment-10:hover:not(:disabled) { - background: #e68600; - transform: translateY(-2px); -} - -.counter-button:disabled { - background: #3a3a3c; - cursor: not-allowed; - color: #8e8e93; - transform: none; -} - -.info-box { - padding: 24px; - background: #2c2c2e; -} - -.info-box p { - font-size: 14px; - color: #8e8e93; - line-height: 1.6; - margin-bottom: 8px; -} - -.info-box p:last-child { - margin-bottom: 0; -} - -@media (max-width: 480px) { - .counter-header h1 { - font-size: 20px; - } - - .count-value { - font-size: 56px; - } - - .counter-controls { - flex-direction: column; - } - - .counter-button { - width: 100%; - } -} diff --git a/examples/counter-next-js/src/app/layout.tsx b/examples/counter-next-js/src/app/layout.tsx deleted file mode 100644 index c4f1400687..0000000000 --- a/examples/counter-next-js/src/app/layout.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; - -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - -export const metadata: Metadata = { - title: "Rivet Counter", - description: "Real-time counter powered by RivetKit", -}; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - ); -} diff --git a/examples/counter-next-js/src/app/page.tsx b/examples/counter-next-js/src/app/page.tsx deleted file mode 100644 index 97cba5bdfc..0000000000 --- a/examples/counter-next-js/src/app/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Counter } from "@/components/Counter"; - -export default function Home() { - return ; -} diff --git a/examples/counter-next-js/src/components/Counter.tsx b/examples/counter-next-js/src/components/Counter.tsx deleted file mode 100644 index 2ed06b99d6..0000000000 --- a/examples/counter-next-js/src/components/Counter.tsx +++ /dev/null @@ -1,103 +0,0 @@ -"use client"; - -import { createRivetKit } from "@rivetkit/next-js/client"; -import { useEffect, useState } from "react"; -import type { registry } from "../rivet/registry"; - -export const { useActor } = createRivetKit({ - endpoint: process.env.NEXT_PUBLIC_RIVET_ENDPOINT ?? "http://localhost:3000/api/rivet", - namespace: process.env.NEXT_PUBLIC_RIVET_NAMESPACE, - token: process.env.NEXT_PUBLIC_RIVET_TOKEN, -}); - -export function Counter() { - const [counterId, setCounterId] = useState("default"); - const [count, setCount] = useState(0); - const [isConnected, setIsConnected] = useState(false); - - const counter = useActor({ - name: "counter", - key: [counterId], - }); - - useEffect(() => { - if (counter.connection) { - setIsConnected(true); - counter.connection.getCount().then(setCount); - } else { - setIsConnected(false); - } - }, [counter.connection]); - - counter.useEvent("newCount", (newCount: number) => { - setCount(newCount); - }); - - const increment = async (amount: number) => { - if (counter.connection) { - await counter.connection.increment(amount); - } - }; - - return ( -
-
-
-

Counter Demo

-
-
- {isConnected ? 'Connected' : 'Disconnected'} -
-
- -
-
- - setCounterId(e.target.value)} - placeholder="Enter counter ID" - className="setting-input" - /> -
-
- -
-
{count}
-

Current Count

-
- -
- - - -
- -
-

This counter is shared across all clients using the same Counter ID.

-

Try opening this page in multiple tabs or browsers!

-
-
-
- ); -} diff --git a/examples/counter-next-js/src/rivet/registry.ts b/examples/counter-next-js/src/rivet/registry.ts deleted file mode 100644 index 1e6cdc4fca..0000000000 --- a/examples/counter-next-js/src/rivet/registry.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { actor, setup } from "rivetkit"; - -const counter = actor({ - state: { - count: 0, - }, - actions: { - increment: (c, x: number) => { - c.state.count += x; - c.broadcast("newCount", c.state.count); - return c.state.count; - }, - getCount: (c) => { - return c.state.count; - }, - }, -}); - -export const registry = setup({ - use: { counter }, -}); diff --git a/examples/counter-next-js/tsconfig.json b/examples/counter-next-js/tsconfig.json deleted file mode 100644 index 7df89e76da..0000000000 --- a/examples/counter-next-js/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/examples/counter/scripts/connect.ts b/examples/counter/scripts/connect.ts index 42208adced..805b957ada 100644 --- a/examples/counter/scripts/connect.ts +++ b/examples/counter/scripts/connect.ts @@ -8,12 +8,15 @@ async function main() { counter.on("newCount", (count: number) => console.log("Event:", count)); - while (true) { - const out = await counter.increment(1); + for (let i = 0; i < 5; i++) { + const out = await counter.increment(5); console.log("RPC:", out); await new Promise((resolve) => setTimeout(resolve, 1000)); } + + await new Promise((resolve) => setTimeout(resolve, 10000)); + await counter.dispose(); } main(); diff --git a/examples/crdt/README.md b/examples/crdt/README.md deleted file mode 100644 index c0b8ce1de5..0000000000 --- a/examples/crdt/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# CRDT Collaborative Editor for RivetKit - -Example project demonstrating real-time collaborative editing using Conflict-free Replicated Data Types (CRDTs) with [RivetKit](https://rivetkit.org). - -[Learn More →](https://github.com/rivet-dev/rivetkit) - -[Discord](https://rivet.dev/discord) — [Documentation](https://rivetkit.org) — [Issues](https://github.com/rivet-dev/rivetkit/issues) - -## Getting Started - -### Prerequisites - -- Node.js 18+ - -### Installation - -```sh -git clone https://github.com/rivet-dev/rivetkit -cd rivetkit/examples/crdt -npm install -``` - -### Development - -```sh -npm run dev -``` - -Open your browser to `http://localhost:3000` - -## Features - -- **Real-time Collaborative Editing**: Multiple users can edit the same document simultaneously -- **Conflict Resolution**: Uses Yjs CRDTs to automatically resolve editing conflicts -- **Persistent State**: Document changes are automatically persisted -- **Multiple Documents**: Switch between different collaborative documents -- **Live Connection Status**: See when you're connected to the collaboration server - -## How it works - -This example demonstrates how to build a collaborative editor using: - -1. **Yjs**: A high-performance CRDT implementation for building collaborative applications -2. **RivetKit Actors**: Manage document state and synchronize changes between clients -3. **Real-time Updates**: Use RivetKit's event system for instant synchronization -4. **Conflict-free Merging**: Yjs automatically handles concurrent edits without conflicts - -## Usage - -1. Start the development server -2. Open multiple browser tabs to `http://localhost:3000` -3. Start typing in any tab - changes will appear in real-time across all tabs -4. Try editing the same text simultaneously to see conflict resolution in action -5. Switch between different documents using the document ID field - -## Architecture - -- **Backend**: RivetKit actor that manages Yjs document state and broadcasts updates -- **Frontend**: React application with Yjs integration for local document management -- **Synchronization**: Binary diffs are sent between clients for efficient updates - -## License - -Apache 2.0 \ No newline at end of file diff --git a/examples/crdt/src/frontend/App.tsx b/examples/crdt/src/frontend/App.tsx deleted file mode 100644 index 26e4942a61..0000000000 --- a/examples/crdt/src/frontend/App.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import { createRivetKit } from "@rivetkit/react"; -import { useEffect, useRef, useState } from "react"; -import * as Y from "yjs"; -import { applyUpdate, encodeStateAsUpdate } from "yjs"; -import type { registry } from "../backend/registry"; - -const { useActor } = createRivetKit("http://localhost:8080"); - -function YjsEditor({ documentId }: { documentId: string }) { - const yjsDocument = useActor({ - name: "yjsDocument", - key: [documentId], - }); - - const [isLoading, setIsLoading] = useState(true); - const [text, setText] = useState(""); - - const yDocRef = useRef(null); - const updatingFromServer = useRef(false); - const updatingFromLocal = useRef(false); - const observationInitialized = useRef(false); - - useEffect(() => { - const yDoc = new Y.Doc(); - yDocRef.current = yDoc; - setIsLoading(false); - - return () => { - yDoc.destroy(); - }; - }, [yjsDocument.connection]); - - useEffect(() => { - const yDoc = yDocRef.current; - if (!yDoc || observationInitialized.current) return; - - const yText = yDoc.getText("content"); - - yText.observe(() => { - if (!updatingFromServer.current) { - setText(yText.toString()); - - if (yjsDocument.connection && !updatingFromLocal.current) { - updatingFromLocal.current = true; - - const update = encodeStateAsUpdate(yDoc); - yjsDocument.connection.applyUpdate(update).finally(() => { - updatingFromLocal.current = false; - }); - } - } - }); - - observationInitialized.current = true; - }, [yjsDocument.connection]); - - yjsDocument.useEvent("initialState", ({ update }: { update: Uint8Array }) => { - const yDoc = yDocRef.current; - if (!yDoc) return; - - updatingFromServer.current = true; - - try { - applyUpdate(yDoc, update); - - const yText = yDoc.getText("content"); - setText(yText.toString()); - } catch (error) { - console.error("Error applying initial update:", error); - } finally { - updatingFromServer.current = false; - } - }); - - yjsDocument.useEvent("update", ({ update }: { update: Uint8Array }) => { - const yDoc = yDocRef.current; - if (!yDoc) return; - - updatingFromServer.current = true; - - try { - applyUpdate(yDoc, update); - - const yText = yDoc.getText("content"); - setText(yText.toString()); - } catch (error) { - console.error("Error applying update:", error); - } finally { - updatingFromServer.current = false; - } - }); - - const handleTextChange = (e: React.ChangeEvent) => { - if (!yDocRef.current) return; - - const newText = e.target.value; - const yText = yDocRef.current.getText("content"); - - if (newText !== yText.toString()) { - updatingFromLocal.current = true; - - yDocRef.current.transact(() => { - yText.delete(0, yText.length); - yText.insert(0, newText); - }); - - updatingFromLocal.current = false; - } - }; - - if (isLoading) { - return
Loading collaborative document...
; - } - - return ( -
-
-

Document: {documentId}

-
- {yjsDocument.connection ? 'Connected' : 'Disconnected'} -
-
-