diff --git a/examples/hono-bun/README.md b/examples/hono-bun/README.md new file mode 100644 index 000000000..8fed209f0 --- /dev/null +++ b/examples/hono-bun/README.md @@ -0,0 +1,37 @@ +# Hono + Bun Integration for RivetKit + +Example project demonstrating Hono web framework with Bun runtime integration 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 + +- Bun + +### Installation + +```sh +git clone https://github.com/rivet-dev/rivetkit +cd rivetkit/examples/hono-bun +npm install +``` + +### Development + +```sh +npm run dev +``` + +Test the server by running: + +```sh +curl -X POST http://localhost:8080/increment/test +``` + +## License + +Apache 2.0 diff --git a/examples/hono-bun/package.json b/examples/hono-bun/package.json new file mode 100644 index 000000000..ab92c3857 --- /dev/null +++ b/examples/hono-bun/package.json @@ -0,0 +1,28 @@ +{ + "name": "example-hono-bun", + "version": "2.0.9", + "private": true, + "type": "module", + "scripts": { + "dev": "bun --watch src/server.ts", + "check-types": "tsc --noEmit", + "connect": "tsx scripts/connect.ts" + }, + "devDependencies": { + "@types/bun": "^1.1.15", + "rivetkit": "workspace:*", + "typescript": "^5.5.2" + }, + "dependencies": { + "hono": "^4.7.0" + }, + "keywords": [ + "rivetkit", + "hono", + "bun", + "actor", + "stateful", + "example" + ], + "stableVersion": "0.8.0" +} diff --git a/examples/hono-bun/scripts/connect.ts b/examples/hono-bun/scripts/connect.ts new file mode 100644 index 000000000..8ce6b7516 --- /dev/null +++ b/examples/hono-bun/scripts/connect.ts @@ -0,0 +1,22 @@ +import { createClient } from "rivetkit/client"; +import type { Registry } from "../src/registry"; + +async function main() { + const client = createClient("http://localhost:8080/rivet"); + + const counter = client.counter.getOrCreate().connect(); + + counter.on("newCount", (count: number) => console.log("Event:", count)); + + 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/hono-bun/src/registry.ts b/examples/hono-bun/src/registry.ts new file mode 100644 index 000000000..3e3d61c37 --- /dev/null +++ b/examples/hono-bun/src/registry.ts @@ -0,0 +1,15 @@ +import { actor, setup } from "rivetkit"; + +export const counter = actor({ + state: { count: 0 }, + actions: { + increment: (c, x: number) => { + c.state.count += x; + return c.state.count; + }, + }, +}); + +export const registry = setup({ + use: { counter }, +}); diff --git a/examples/hono-bun/src/server.ts b/examples/hono-bun/src/server.ts new file mode 100644 index 000000000..0cb8ffa96 --- /dev/null +++ b/examples/hono-bun/src/server.ts @@ -0,0 +1,38 @@ +import { Hono } from "hono"; +import { upgradeWebSocket, websocket } from "hono/bun"; +import { registry } from "./registry"; + +const { client, fetch } = registry.start({ + basePath: "/rivet", + // Hono requires using Hono.serve + disableDefaultServer: true, + // Override endpoint + overrideServerAddress: "http://localhost:8080/rivet", + // Specify Hono-specific upgradeWebSocket + getUpgradeWebSocket: () => upgradeWebSocket, +}); + +// Setup router +const app = new Hono(); + +app.use("/rivet/*", async (c) => { + return await fetch(c.req.raw, c.env); +}); + +// Example HTTP endpoint +app.post("/increment/:name", async (c) => { + const name = c.req.param("name"); + + const counter = client.counter.getOrCreate(name); + const newCount = await counter.increment(1); + + return c.text(`New Count: ${newCount}`); +}); + +Bun.serve({ + port: 8080, + fetch: app.fetch, + websocket, +}); + +console.log("Listening at http://localhost:8080"); diff --git a/examples/hono-bun/tsconfig.json b/examples/hono-bun/tsconfig.json new file mode 100644 index 000000000..0879c6aae --- /dev/null +++ b/examples/hono-bun/tsconfig.json @@ -0,0 +1,43 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "esnext", + /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "lib": ["esnext"], + /* Specify what JSX code is generated. */ + "jsx": "react-jsx", + + /* Specify what module code is generated. */ + "module": "esnext", + /* Specify how TypeScript looks up a file from a given module specifier. */ + "moduleResolution": "bundler", + /* Specify type package names to be included without being referenced in a source file. */ + "types": ["bun"], + /* Enable importing .json files */ + "resolveJsonModule": true, + + /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ + "allowJs": true, + /* Enable error reporting in type-checked JavaScript files. */ + "checkJs": false, + + /* Disable emitting files from a compilation. */ + "noEmit": true, + + /* Ensure that each file can be safely transpiled without relying on other imports. */ + "isolatedModules": true, + /* Allow 'import x from y' when a module doesn't have a default export. */ + "allowSyntheticDefaultImports": true, + /* Ensure that casing is correct in imports. */ + "forceConsistentCasingInFileNames": true, + + /* Enable all strict type-checking options. */ + "strict": true, + + /* Skip type checking all .d.ts files. */ + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/examples/hono-bun/turbo.json b/examples/hono-bun/turbo.json new file mode 100644 index 000000000..95960709b --- /dev/null +++ b/examples/hono-bun/turbo.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b98a7363..06c482bdd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -422,7 +422,7 @@ importers: version: 0.31.2 drizzle-orm: specifier: ^0.44.2 - version: 0.44.2(@cloudflare/workers-types@4.20250619.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(kysely@0.28.2) + version: 0.44.2(@cloudflare/workers-types@4.20250619.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(bun-types@1.2.23(@types/react@19.1.8))(kysely@0.28.2) devDependencies: '@types/node': specifier: ^22.13.9 @@ -555,6 +555,22 @@ importers: specifier: ^5.5.2 version: 5.8.3 + examples/hono-bun: + dependencies: + hono: + specifier: 4.9.8 + version: 4.9.8 + devDependencies: + '@types/bun': + specifier: ^1.1.15 + version: 1.2.23(@types/react@19.1.8) + rivetkit: + specifier: workspace:* + version: link:../../packages/rivetkit + typescript: + specifier: ^5.5.2 + version: 5.8.3 + examples/hono-react: dependencies: '@rivetkit/react': @@ -1095,7 +1111,7 @@ importers: version: 24.0.4 drizzle-orm: specifier: ^0.44.2 - version: 0.44.2(@cloudflare/workers-types@4.20250619.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(kysely@0.28.2) + version: 0.44.2(@cloudflare/workers-types@4.20250619.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(bun-types@1.2.23(@types/react@19.1.8))(kysely@0.28.2) tsup: specifier: ^8.3.6 version: 8.5.0(@microsoft/api-extractor@7.52.8(@types/node@24.0.4))(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) @@ -2788,6 +2804,9 @@ packages: '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + '@types/bun@1.2.23': + resolution: {integrity: sha512-le8ueOY5b6VKYf19xT3McVbXqLqmxzPXHsQT/q9JHgikJ2X22wyTW3g3ohz2ZMnp7dod6aduIiq8A14Xyimm0A==} + '@types/chai@5.2.2': resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} @@ -3087,6 +3106,11 @@ packages: resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} engines: {node: '>=6.14.2'} + bun-types@1.2.23: + resolution: {integrity: sha512-R9f0hKAZXgFU3mlrA0YpE/fiDvwV0FT9rORApt2aQVWSuJDzZOyB5QLc0N/4HF57CS8IXJ6+L5E4W1bW6NS2Aw==} + peerDependencies: + '@types/react': ^19 + bundle-require@5.1.0: resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -6258,6 +6282,12 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 22.15.32 + '@types/bun@1.2.23(@types/react@19.1.8)': + dependencies: + bun-types: 1.2.23(@types/react@19.1.8) + transitivePeerDependencies: + - '@types/react' + '@types/chai@5.2.2': dependencies: '@types/deep-eql': 4.0.2 @@ -6696,6 +6726,11 @@ snapshots: dependencies: node-gyp-build: 4.8.4 + bun-types@1.2.23(@types/react@19.1.8): + dependencies: + '@types/node': 22.15.32 + '@types/react': 19.1.8 + bundle-require@5.1.0(esbuild@0.25.5): dependencies: esbuild: 0.25.5 @@ -6884,12 +6919,13 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.44.2(@cloudflare/workers-types@4.20250619.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(kysely@0.28.2): + drizzle-orm@0.44.2(@cloudflare/workers-types@4.20250619.0)(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(bun-types@1.2.23(@types/react@19.1.8))(kysely@0.28.2): optionalDependencies: '@cloudflare/workers-types': 4.20250619.0 '@opentelemetry/api': 1.9.0 '@types/better-sqlite3': 7.6.13 better-sqlite3: 11.10.0 + bun-types: 1.2.23(@types/react@19.1.8) kysely: 0.28.2 dunder-proto@1.0.1: