diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ad774ad4..fc08e0520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -307,7 +307,7 @@ ### Features -* **@actor-core/cli:** add cli ([#642](https://github.com/rivet-gg/actor-core/issues/642)) ([d919f1a](https://github.com/rivet-gg/actor-core/commit/d919f1aa11972f0513f6ad5851965b7f469624cd)) +* **rivetkit/cli:** add cli ([#642](https://github.com/rivet-gg/actor-core/issues/642)) ([d919f1a](https://github.com/rivet-gg/actor-core/commit/d919f1aa11972f0513f6ad5851965b7f469624cd)) * add config validation ([#648](https://github.com/rivet-gg/actor-core/issues/648)) ([3323988](https://github.com/rivet-gg/actor-core/commit/3323988f6ab3d5d9ba99ba113f6b8e4a7f4c5ec7)) * add cors support ([#647](https://github.com/rivet-gg/actor-core/issues/647)) ([ef13939](https://github.com/rivet-gg/actor-core/commit/ef13939f57c333d19b1cafc29b003bce1ccb8cf9)) * add release candidate support to publish script ([#660](https://github.com/rivet-gg/actor-core/issues/660)) ([f6c6adc](https://github.com/rivet-gg/actor-core/commit/f6c6adc8dd8fe9ceb237ba55be7f5953fe8047ec)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b16bad821..2aeaf98ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,6 +25,6 @@ $ ./scripts/e2e-publish.ts - Modifies yarnrc to point to the local registry, publishes the package, and then restores the original yarnrc. 5. ```bash -$ yarn workspace @actor-core/cli run test +$ yarn workspace rivetkit/cli run test ``` - Runs the tests for the CLI, which includes running the CLI (inside Docker) with different arguments and checking the output. diff --git a/clients/python/pyproject.toml b/clients/python/pyproject.toml index 029033576..48b7d73c6 100644 --- a/clients/python/pyproject.toml +++ b/clients/python/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "actor-core-client" +name = "@rivetkit/actor-client" version = "0.9.0-rc.1" authors = [ { name="Rivet Gaming, LLC", email="developer@rivet.gg" }, @@ -41,4 +41,4 @@ features = ["pyo3/extension-module"] testpaths = ["tests"] python_files = ["test_*.py"] log_cli = true -log_level = "DEBUG" \ No newline at end of file +log_level = "DEBUG" diff --git a/clients/python/tests/common.py b/clients/python/tests/common.py index c1df6a45b..530c35953 100644 --- a/clients/python/tests/common.py +++ b/clients/python/tests/common.py @@ -42,7 +42,7 @@ def start_mock_server(): # Build actor-core logger.info("Building actor-core") subprocess.run( - ["yarn", "build", "-F", "actor-core"], + ["yarn", "build", "-F", "@rivetkit/actor"], cwd=repo_root, check=True ) @@ -58,7 +58,7 @@ def start_mock_server(): # Pack packages packages = [ - ("actor-core", repo_root / "packages/actor-core"), + ("@rivetkit/actor", repo_root / "packages/actor-core"), ("nodejs", repo_root / "packages/platforms/nodejs"), ("memory", repo_root / "packages/drivers/memory"), ("file-system", repo_root / "packages/drivers/file-system") @@ -66,7 +66,7 @@ def start_mock_server(): logger.info("Packing packages (3 total)") for name, path in packages: - output_path = vendor_dir / f"actor-core-{name}.tgz" + output_path = vendor_dir / f"@rivetkit/actor-{name}.tgz" subprocess.run( ["yarn", "pack", "--out", str(output_path)], cwd=path, @@ -88,7 +88,7 @@ def start_mock_server(): port = get_free_port() server_script = f""" import {{ app }} from "./actors/app.ts"; -import {{ serve }} from "@actor-core/nodejs"; +import {{ serve }} from "@rivetkit/nodejs"; serve(app, {{ port: {port}, mode: "memory" }}); """ @@ -98,15 +98,15 @@ def start_mock_server(): # Create package.json logger.info("Creating package.json") package_json = { - "name": "actor-core-python-test", + "name": "@rivetkit/actor-python-test", "packageManager": "yarn@4.2.2", "private": True, "type": "module", "dependencies": { - "actor-core": f"file:{vendor_dir}/actor-core-actor-core.tgz", - "@actor-core/nodejs": f"file:{vendor_dir}/actor-core-nodejs.tgz", - "@actor-core/memory": f"file:{vendor_dir}/actor-core-memory.tgz", - "@actor-core/file-system": f"file:{vendor_dir}/actor-core-file-system.tgz", + "@rivetkit/actor": f"file:{vendor_dir}/actor-core-actor-core.tgz", + "@rivetkit/nodejs": f"file:{vendor_dir}/actor-core-nodejs.tgz", + "@rivetkit/memory": f"file:{vendor_dir}/actor-core-memory.tgz", + "@rivetkit/file-system": f"file:{vendor_dir}/actor-core-file-system.tgz", }, "devDependencies": { "tsx": "^3.12.7" diff --git a/clients/rust/Cargo.toml b/clients/rust/Cargo.toml index 3eeffac58..56060c271 100644 --- a/clients/rust/Cargo.toml +++ b/clients/rust/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "actor-core-client" +name = "@rivetkit/actor-client" version = "0.9.0-rc.1" description = "Rust client for ActorCore - the Stateful Serverless Framework for building AI agents, realtime apps, and game servers" edition = "2021" diff --git a/clients/rust/tests/e2e.rs b/clients/rust/tests/e2e.rs index dd8fd3fab..60c10b022 100644 --- a/clients/rust/tests/e2e.rs +++ b/clients/rust/tests/e2e.rs @@ -26,7 +26,7 @@ impl MockServer { // Run `yarn build -F actor-core` in the root of this repo let status = Command::new("yarn") - .args(["build", "-F", "actor-core"]) + .args(["build", "-F", "@rivetkit/actor"]) .current_dir(&repo_root) .status() .expect("Failed to build actor-core"); @@ -46,7 +46,7 @@ impl MockServer { // Define packages to pack let packages = [ - ("actor-core", repo_root.join("packages/actor-core")), + ("@rivetkit/actor", repo_root.join("packages/actor-core")), ("nodejs", repo_root.join("packages/platforms/nodejs")), ("memory", repo_root.join("packages/drivers/memory")), ("file-system", repo_root.join("packages/drivers/file-system")), @@ -54,7 +54,7 @@ impl MockServer { // Pack each package to the vendor directory for (name, path) in packages.iter() { - let output_path = vendor_dir.join(format!("actor-core-{}.tgz", name)); + let output_path = vendor_dir.join(format!("@rivetkit/actor-{}.tgz", name)); println!( "Packing {} from {} to {}", name, @@ -86,7 +86,7 @@ impl MockServer { // Write the server script let server_script = r#" import { app } from "./actors/app.ts"; -import { serve } from "@actor-core/nodejs"; +import { serve } from "@rivetkit/nodejs"; serve(app, { port: PORT, mode: "memory" }); "# @@ -98,24 +98,24 @@ serve(app, { port: PORT, mode: "memory" }); let package_json_path = server_dir.join("package.json"); let package_json = format!( r#"{{ - "name": "actor-core-rust-test", + "name": "@rivetkit/actor-rust-test", "packageManager": "yarn@4.2.2", "private": true, "type": "module", "dependencies": {{ - "actor-core": "file:{}", - "@actor-core/nodejs": "file:{}", - "@actor-core/memory": "file:{}", - "@actor-core/file-system": "file:{}" + "@rivetkit/actor": "file:{}", + "@rivetkit/nodejs": "file:{}", + "@rivetkit/memory": "file:{}", + "@rivetkit/file-system": "file:{}" }}, "devDependencies": {{ "tsx": "^3.12.7" }} }}"#, - vendor_dir.join("actor-core-actor-core.tgz").display(), - vendor_dir.join("actor-core-nodejs.tgz").display(), - vendor_dir.join("actor-core-memory.tgz").display(), - vendor_dir.join("actor-core-file-system.tgz").display() + vendor_dir.join("@rivetkit/actor-actor-core.tgz").display(), + vendor_dir.join("@rivetkit/actor-nodejs.tgz").display(), + vendor_dir.join("@rivetkit/actor-memory.tgz").display(), + vendor_dir.join("@rivetkit/actor-file-system.tgz").display() ); std::fs::write(&package_json_path, package_json).expect("Failed to write package.json"); diff --git a/docs/concepts/actions.mdx b/docs/actor/actions.mdx similarity index 91% rename from docs/concepts/actions.mdx rename to docs/actor/actions.mdx index 1dabd38a8..e82a1c4c6 100644 --- a/docs/concepts/actions.mdx +++ b/docs/actor/actions.mdx @@ -17,7 +17,7 @@ actor. Actions are defined in the `actions` object when creating an actor: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const mathUtils = actor({ state: {}, @@ -37,7 +37,7 @@ Each action receives a context object (commonly named `c`) as its first paramete You can define helper functions outside the actions object to keep your code organized. These functions cannot be called directly by clients: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; // Private helper function - not callable by clients const calculateFee = (amount) => { @@ -69,7 +69,7 @@ Actions have a single return value. To stream realtime data in response to an ac Calling actions from the client is simple: ```typescript -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("http://localhost:6420"); @@ -89,7 +89,7 @@ The actor client includes type safety out of the box. When you use `createClient ```typescript src/index.ts -import { setup } from "actor-core"; +import { setup } from "@rivetkit/actor"; // Create simple counter const counter = actor({ @@ -111,7 +111,7 @@ export type App = typeof app; ``` ```typescript client.ts -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("http://localhost:6420"); @@ -142,7 +142,7 @@ For example: ```typescript actor.ts -import { actor, UserError } from "actor-core"; +import { actor, UserError } from "@rivetkit/actor"; const user = actor({ state: { users: [] }, @@ -190,7 +190,7 @@ Data schemas are not validated by default. For production applications, use a li For example, to validate action parameters: ```typescript -import { actor, UserError } from "actor-core"; +import { actor, UserError } from "@rivetkit/actor"; import { z } from "zod"; // Define schema for action parameters @@ -219,7 +219,7 @@ const counter = actor({ ``` - Native runtime type validation is coming soon to ActorCore. + Native runtime type validation is coming soon to RivetKit. ## Authentication @@ -230,10 +230,10 @@ By default, clients can call all actions on an actor without restriction. Make s When writing complex logic for actions, you may want to extract parts of your implementation into separate helper functions. When doing this, you'll need a way to properly type the context parameter. -ActorCore provides the `ActionContextOf` utility type for exactly this purpose: +RivetKit provides the `ActionContextOf` utility type for exactly this purpose: ```typescript -import { actor, ActionContextOf } from "actor-core"; +import { actor, ActionContextOf } from "@rivetkit/actor"; const counter = actor({ state: { count: 0 }, diff --git a/docs/concepts/authentication.mdx b/docs/actor/authentication.mdx similarity index 95% rename from docs/concepts/authentication.mdx rename to docs/actor/authentication.mdx index 6ce91d1d9..a6ca32a02 100644 --- a/docs/concepts/authentication.mdx +++ b/docs/actor/authentication.mdx @@ -14,7 +14,7 @@ Throwing an error in `onBeforeConnect` or `createConnState` will abort the conne Here's a basic example: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const exampleActor = actor({ state: { @@ -46,7 +46,7 @@ const exampleActor = actor({ After authentication, you can access the connection state in any action through the context object: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const authenticatedActor = actor({ state: { @@ -80,7 +80,7 @@ const authenticatedActor = actor({ ### With API Server Authentication ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const apiAuthenticatedActor = actor({ state: { @@ -118,7 +118,7 @@ When authentication fails, throwing an error in `createConnState` will prevent t ### With JWT Authentication ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; import jwt from "jsonwebtoken"; const JWT_SECRET = process.env.JWT_SECRET; diff --git a/docs/concepts/connections.mdx b/docs/actor/connections.mdx similarity index 94% rename from docs/concepts/connections.mdx rename to docs/actor/connections.mdx index c6caff43c..5f1813a0a 100644 --- a/docs/concepts/connections.mdx +++ b/docs/actor/connections.mdx @@ -14,7 +14,7 @@ For example: ```typescript actor.ts -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const gameRoom = actor({ state: {}, @@ -39,7 +39,7 @@ const gameRoom = actor({ ``` ```typescript client.ts -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("http://localhost:6420"); @@ -57,7 +57,7 @@ There are two ways to define an actor's connection state: ### Method 1: `ConnState` constant ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ state: { messages: [] }, @@ -84,7 +84,7 @@ const chatRoom = actor({ The data returned from `createConnState` is used as the initial state of the connection. The connection state can be accessed through `conn.state`. ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ state: { messages: [] }, @@ -131,7 +131,7 @@ This is frequently used with `conn.send(name, event)` to send messages directly For example: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ state: { users: {} }, @@ -158,7 +158,7 @@ const chatRoom = actor({ Connections can be disconnected from within an action: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const secureRoom = actor({ state: {}, diff --git a/docs/concepts/events.mdx b/docs/actor/events.mdx similarity index 90% rename from docs/concepts/events.mdx rename to docs/actor/events.mdx index 473b0d483..982047417 100644 --- a/docs/concepts/events.mdx +++ b/docs/actor/events.mdx @@ -18,7 +18,7 @@ Actors can publish events to all connected clients with `c.broadcast(name, data) ```typescript chat_room.ts -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ state: {}, @@ -31,7 +31,7 @@ const chatRoom = actor({ ``` ```typescript client.ts -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("http://localhost:6420"); @@ -48,7 +48,7 @@ Actors can send messages to specific client connections. All connections are ava ```typescript chat_room.ts -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ state: {}, @@ -64,7 +64,7 @@ const chatRoom = actor({ ``` ```typescript client.ts -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("http://localhost:6420"); @@ -87,7 +87,7 @@ Clients can subscribe to events that will happen repeatedly using `actor.on(name ```typescript client.ts -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("http://localhost:6420"); @@ -99,7 +99,7 @@ chatRoom.on('newMessage', ({ message }) => { ``` ```typescript chat_room.ts -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ state: {}, @@ -122,7 +122,7 @@ Clients can listen for an event only one time with `actor.once(name, callback)`. ```typescript client.ts -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("http://localhost:6420"); @@ -137,7 +137,7 @@ await chatRoom.requestJoin(); ``` ```typescript chat_room.ts -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ state: { diff --git a/docs/concepts/lifecycle.mdx b/docs/actor/lifecycle.mdx similarity index 95% rename from docs/concepts/lifecycle.mdx rename to docs/actor/lifecycle.mdx index 9e9365d03..4c7879e08 100644 --- a/docs/concepts/lifecycle.mdx +++ b/docs/actor/lifecycle.mdx @@ -20,7 +20,7 @@ The `createVars` function or `vars` constant defines ephemeral variables for the The `createVars` function can also receive driver-specific context as its second parameter, allowing access to driver capabilities like Rivet KV or Cloudflare Durable Object storage. ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; // Using vars constant const counter1 = actor({ @@ -63,7 +63,7 @@ const exampleActor = actor({ The `onCreate` hook is called at the same time as `createState`, but unlike `createState`, it doesn't return any value. Use this hook for initialization logic that doesn't affect the initial state. ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; // Using state constant const counter1 = actor({ @@ -106,7 +106,7 @@ This is called after the actor has been initialized but before any connections a Use this hook to set up any resources or start any background tasks, such as `setInterval`. ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const counter = actor({ state: { count: 0 }, @@ -135,7 +135,7 @@ const counter = actor({ Called whenever the actor's state changes. This is often used to broadcast state updates. ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const counter = actor({ state: { count: 0 }, @@ -173,7 +173,7 @@ The `onBeforeConnect` hook is called whenever a new client connects to the actor The `onBeforeConnect` hook does NOT return connection state - it's used solely for validation. ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ state: { messages: [] }, @@ -218,7 +218,7 @@ Connections cannot interact with the actor until this method completes successfu Executed after the client has successfully connected. ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ state: { users: {}, messages: [] }, @@ -250,7 +250,7 @@ Messages will not be processed for this actor until this hook succeeds. Errors t Called when a client disconnects from the actor. Use this to clean up any connection-specific resources. ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ state: { users: {}, messages: [] }, @@ -278,7 +278,7 @@ const chatRoom = actor({ Actors can be shut down gracefully with `c.shutdown()`. Clients will be gracefully disconnected. ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const temporaryRoom = actor({ state: { @@ -325,10 +325,10 @@ This action is permanent and cannot be reverted. ## Using `ActorContext` Type Externally -When extracting logic from lifecycle hooks or actions into external functions, you'll often need to define the type of the context parameter. ActorCore provides helper types that make it easy to extract and pass these context types to external functions. +When extracting logic from lifecycle hooks or actions into external functions, you'll often need to define the type of the context parameter. RivetKit provides helper types that make it easy to extract and pass these context types to external functions. ```typescript -import { actor, ActorContextOf } from "actor-core"; +import { actor, ActorContextOf } from "@rivetkit/actor"; const myActor = actor({ state: { count: 0 }, @@ -348,7 +348,7 @@ See [Helper Types](/concepts/types) for more details on using `ActorContextOf`. ## Full Example ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const counter = actor({ // Initialize state diff --git a/docs/concepts/metadata.mdx b/docs/actor/metadata.mdx similarity index 94% rename from docs/concepts/metadata.mdx rename to docs/actor/metadata.mdx index ee8a121e6..32acd737e 100644 --- a/docs/concepts/metadata.mdx +++ b/docs/actor/metadata.mdx @@ -20,7 +20,7 @@ For example: ```typescript chat_room.ts -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ state: { @@ -53,7 +53,7 @@ export default chatRoom; ``` ```typescript client.ts -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("http://localhost:6420"); diff --git a/docs/actor/overview.mdx b/docs/actor/overview.mdx new file mode 100644 index 000000000..a205cefac --- /dev/null +++ b/docs/actor/overview.mdx @@ -0,0 +1,281 @@ +--- +title: Rivet Actors +icon: square-info +sidebarTitle: "Overview" +description: A library for building stateful, scalable, realtime backend applications. +--- + +import CreateActorCli from "/snippets/create-actor-cli.mdx"; + +Actors combine compute and storage into unified entities for simplified architecture. Actors seamlessly integrate with your existing infrastructure or can serve as a complete standalone solution. + +## Concepts + +The core concepts that power Rivet Actor applications: + +- **State Is Automatically Persisted**: State automatically persists between restarts, upgrades, & crashes +- **State Is Stored In-Memory**: State is stored in memory for high-performance reads/writes while also automatically persisted +- **Isolated State Ownership**: Actors only manage their own state, which can only be modified by the actor itself +- **Communicates via Actions**: How clients and other actors interact with an actor +- **Actions Are Low-Latency**: Actions provide WebSocket-like performance for time-sensitive operations +- **Broadcast Updates With Events**: Actors can publish real-time updates to connected clients + +## Quickstart + +Run this to get started: + + + +## Code Example + +Here's a complete chat room actor that maintains state and handles messages. We'll explore each component in depth throughout this document: + +```typescript chat_room.ts +import { actor } from "@rivetkit/actor"; + +// Define a chat room actor +const chatRoom = actor({ + // Initialize state when the actor is first created + createState: () => ({ + messages: [] + }), + + // Define actions clients can call + actions: { + // Action to send a message + sendMessage: (c, sender, text) => { + // Update state + c.state.messages.push({ sender, text }); + + // Broadcast to all connected clients + c.broadcast("newMessage", { sender, text }); + }, + + // Action to get chat history + getHistory: (c) => { + return c.state.messages; + } + } +}); + +export default chatRoom; +``` + +## Using the App + +To start using your actor, create an app and serve it: + +```typescript app.ts +import { setup, serve } from "@rivetkit/actor"; +import chatRoom from "./chat_room"; + +// Create the application +const app = setup({ + actors: { chatRoom } +}); + +// Start serving on default port +serve(app); + +// Export the app type for client usage +export type App = typeof app; +``` + +## Key Actor Components + +### State + +Actors maintain state that's stored in memory and automatically persisted. State is defined either as a constant or via a `createState` function: + +```typescript +import { actor } from "@rivetkit/actor"; + +// Method 1: State constant +const counter1 = actor({ + state: { count: 0 }, + actions: { + // ... + } +}); + +// Method 2: CreateState function +const counter2 = actor({ + createState: () => ({ count: 0 }), + actions: { + // ... + } +}); +``` + +Update state by modifying `c.state` in your actions: + +```typescript +import { actor } from "@rivetkit/actor"; + +const counter = actor({ + state: { count: 0 }, + actions: { + // Example of state update in an action + increment: (c) => { + c.state.count += 1; + return c.state.count; + } + } +}); +``` + +These changes are durable and are automatically persisted across updates, restarts, and crashes. + +Learn more about [state management](/actor/state). + +### Actions + +Actions are functions defined in your actor configuration that clients & other actors can call: + +```typescript +import { actor } from "@rivetkit/actor"; + +const mathUtils = actor({ + state: {}, + actions: { + multiplyByTwo: (c, x) => { + return x * 2; + } + } +}); +``` + +Each action receives a context object (commonly named `c`) as its first parameter, which provides access to state, connections, and other utilities. + +Learn more about [actions](/actor/actions). + +### Events + +Actors can broadcast events to connected clients: + +```typescript +import { actor } from "@rivetkit/actor"; + +const inventory = actor({ + createState: () => ({ + items: [] + }), + + actions: { + addItem: (c, item) => { + // Add to state + c.state.items.push(item); + + // Notify all clients about the new item + c.broadcast("itemAdded", { item }); + } + } +}); +``` + +You can also send events to specific clients: + +```typescript +import { actor } from "@rivetkit/actor"; + +const messageService = actor({ + state: {}, + actions: { + sendPrivateMessage: (c, userId, text) => { + // Send to a specific connection + const conn = c.conns.find(conn => conn.params.userId === userId); + if (conn) { + conn.send("privateMessage", { text }); + } + } + } +}); +``` + +Learn more about [events](/actor/events). + +## Actor Tags + +Tags are key-value pairs attached to actors that serve two purposes: + +1. **Actor Discovery**: Find specific actors using `client.get(tags)` +2. **Organization**: Group related actors for management purposes + +For example, you can query chat rooms by tag like: + +```typescript client.ts +await client.chatRoom.get({ channel: "random" }); +``` + +### Common Tag Patterns + +```typescript +import { createClient } from "@rivetkit/actor/client"; +import type { App } from "./src/index"; + +const client = createClient("http://localhost:6420"); + +// Game room with ID parameter +const gameRoom = await client.gameRoom.get({ roomId: "ABC123" }); + +// User profile with ID +const userProfile = await client.userProfile.get({ profileId: "1234" }); + +// Document with multiple parameters +const document = await client.document.get({ + workspaceId: "team-alpha", + documentId: "budget-2024" +}); +``` + +## Actor Lifecycle + +Actors are created automatically when needed and persist until explicitly shutdown. + +To shut down an actor, use `c.shutdown()` from within an action: + +```typescript +import { actor } from "@rivetkit/actor"; + +const chatRoom = actor({ + createState: () => ({ + messages: [] + }), + actions: { + closeRoom: (c) => { + // Do any cleanup needed + c.broadcast("roomClosed"); + + // Shutdown the actor + c.shutdown(); + } + } +}); +``` + +Learn more about the [actor lifecycle](/actor/lifecycle). + +## Documentation + +Learn more about Rivet Actors: + + + + Get started with Rivet Actors in minutes + + + Understand how actor state is managed, persisted, and accessed. + + + Define and implement actor actions (RPCs) for client interaction. + + + Real-time communication with events and broadcasts. + + + Managing the creation, execution, and termination of actors. + + + Schedule tasks and alarms with actors for time-based operations. + + \ No newline at end of file diff --git a/docs/actor/quickstart.mdx b/docs/actor/quickstart.mdx new file mode 100644 index 000000000..e5c3ddef0 --- /dev/null +++ b/docs/actor/quickstart.mdx @@ -0,0 +1,122 @@ +--- +title: Quickstart +icon: forward +description: Start building awesome documentation in under 5 minutes +--- + + + + +```sh +npm install rivetkit/actor +``` + + + + +```ts +import { actor, setup } from "@rivetkit/actor"; + +const myActor = actor({ + +}); +``` + + + + + + +```ts Rivet +// TODO: +``` + +```ts Cloudflare Workers +// TODO: +``` + +```ts Redis +// TODO: +``` + +```ts Postgres +// TODO: +``` + + + + + + + + +```ts Hono +const app = new Hono(); +app.route("rivetkit", setup({ myWf, myActor })); +serve(app); +``` + +```ts Express.js +const app = new Hono(); +app.route("rivetkit", setup({ myWf, myActor })); +serve(app); +``` + + + + + + + +```sh +node src/server.ts +``` + + + + + + + +```ts Node.js +const client = createClient(); +client.myWf.getOrCreate().sendEvent("foo"); +``` + +```ts React +const client = createClient(); +client.myWf.getOrCreate().sendEvent("foo"); +``` + + + + + + + + + +```sh Rivet +rivet deploy +``` + +```sh Cloudflare Workers +rivet deploy +``` + + + + + + + + +## Next Steps + + + + + + + + diff --git a/docs/concepts/schedule.mdx b/docs/actor/schedule.mdx similarity index 98% rename from docs/concepts/schedule.mdx rename to docs/actor/schedule.mdx index b5a5fa96a..026037308 100644 --- a/docs/concepts/schedule.mdx +++ b/docs/actor/schedule.mdx @@ -38,7 +38,7 @@ Currently, scheduling can only trigger public actions. If the scheduled action i ## Full Example ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const reminderService = actor({ state: { diff --git a/docs/concepts/state.mdx b/docs/actor/state.mdx similarity index 92% rename from docs/concepts/state.mdx rename to docs/actor/state.mdx index 123e30330..c86470848 100644 --- a/docs/concepts/state.mdx +++ b/docs/actor/state.mdx @@ -19,7 +19,7 @@ There are two ways to define an actor's initial state: **Method 1: Static Initial State** ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; // Simple state with a constant const counter = actor({ @@ -35,7 +35,7 @@ const counter = actor({ **Method 2: Dynamic Initial State** ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; // State with initialization logic const counter = actor({ @@ -57,7 +57,7 @@ The `createState` function is called once when the actor is first created. See [ To update state, modify the `state` property on the context object (`c.state`) in your actions: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const counter = actor({ state: { count: 0 }, @@ -89,7 +89,7 @@ Actors automatically handle persisting state transparently. This happens at the In the rare occasion you need to force a state change mid-action, you can use `c.saveState()`. This should only be used if your action makes an important state change that needs to be persisted before the action completes. ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const criticalProcess = actor({ state: { @@ -133,7 +133,7 @@ If you need a shared state between multiple actors, you have two options: ## Ephemeral Variables -In addition to persisted state, ActorCore provides a way to store ephemeral data that is not saved to permanent storage using `vars`. This is useful for temporary data that only needs to exist while the actor is running or data that cannot be serialized. +In addition to persisted state, RivetKit provides a way to store ephemeral data that is not saved to permanent storage using `vars`. This is useful for temporary data that only needs to exist while the actor is running or data that cannot be serialized. `vars` is designed to complement `state`, not replace it. Most actors should use both: `state` for critical business data and `vars` for ephemeral or non-serializable data. @@ -144,7 +144,7 @@ There are two ways to define an actor's initial vars: **Method 1: Static Initial Variables** ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; // Define vars as a constant const counter = actor({ @@ -169,7 +169,7 @@ When using static `vars`, all values must be compatible with `structuredClone()` **Method 2: Dynamic Initial Variables** ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; // Define vars with initialization logic const counter = actor({ @@ -194,7 +194,7 @@ const counter = actor({ Vars can be accessed and modified through the context object with `c.vars`: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; import { createNanoEvents } from "nanoevents"; const counter = actor({ diff --git a/docs/concepts/types.mdx b/docs/actor/types.mdx similarity index 82% rename from docs/concepts/types.mdx rename to docs/actor/types.mdx index b33b3ffed..feacdb211 100644 --- a/docs/concepts/types.mdx +++ b/docs/actor/types.mdx @@ -2,18 +2,18 @@ title: Helper Types --- -ActorCore provides several TypeScript helper types to make it easier to work with actors in a type-safe way. +RivetKit provides several TypeScript helper types to make it easier to work with actors in a type-safe way. ## `Context` Types -When working with actors, you often need to access the context object. ActorCore provides helper types to extract the context types from actor definitions. +When working with actors, you often need to access the context object. RivetKit provides helper types to extract the context types from actor definitions. ### `ActorContextOf` Extracts the full actor context type from an actor definition. This is the type of the context object (`c`) available in lifecycle hooks such as `onCreate`, `onStart`, etc. ```typescript -import { actor, ActorContextOf } from "actor-core"; +import { actor, ActorContextOf } from "@rivetkit/actor"; const chatRoom = actor({ state: { messages: [] }, @@ -40,7 +40,7 @@ function processChatRoomContext(context: ChatRoomContext) { Extracts the action context type from an actor definition. This is the type of the context object (`c`) available in action handlers. ```typescript -import { actor, ActionContextOf } from "actor-core"; +import { actor, ActionContextOf } from "@rivetkit/actor"; const counter = actor({ state: { count: 0 }, diff --git a/docs/clients/javascript.mdx b/docs/clients/javascript.mdx index 0edc2a330..f99e67a8d 100644 --- a/docs/clients/javascript.mdx +++ b/docs/clients/javascript.mdx @@ -9,7 +9,7 @@ import StepRunStudio from "/snippets/step-run-studio.mdx"; import StepDeploy from "/snippets/step-deploy.mdx"; import SetupNextSteps from "/snippets/setup-next-steps.mdx"; -The ActorCore JavaScript client allows you to connect to and interact with actors from browser and Node.js applications. +The RivetKit JavaScript client allows you to connect to and interact with actors from browser and Node.js applications. @@ -49,24 +49,24 @@ The ActorCore JavaScript client allows you to connect to and interact with actor - - Install the ActorCore client and Node.js platform packages: + + Install the RivetKit client and Node.js platform packages: ```sh npm - npm install actor-core + npm install rivetkit ``` ```sh pnpm - pnpm add actor-core + pnpm add rivetkit ``` ```sh yarn - yarn add actor-core + yarn add rivetkit ``` ```sh bun - bun add actor-core + bun add rivetkit ``` @@ -77,7 +77,7 @@ The ActorCore JavaScript client allows you to connect to and interact with actor Create a file `src/client.ts` in your project to connect to your actor: ```typescript src/client.ts - import { createClient } from "actor-core/client"; + import { createClient } from "@rivetkit/actor/client"; import type { App } from "../actors/app"; async function main() { diff --git a/docs/clients/python.mdx b/docs/clients/python.mdx index 7af03c7ce..356e0e5c9 100644 --- a/docs/clients/python.mdx +++ b/docs/clients/python.mdx @@ -9,7 +9,7 @@ import StepRunStudio from "/snippets/step-run-studio.mdx"; import StepDeploy from "/snippets/step-deploy.mdx"; import SetupNextSteps from "/snippets/setup-next-steps.mdx"; -The ActorCore Python client provides a way to connect to and interact with actors from Python applications. +The RivetKit Python client provides a way to connect to and interact with actors from Python applications. @@ -32,10 +32,10 @@ The ActorCore Python client provides a way to connect to and interact with actor - Install the ActorCore client package: + Install the RivetKit client package: ```sh - pip install actor-core-client + pip install rivetkit-client ``` @@ -104,7 +104,7 @@ The ActorCore Python client provides a way to connect to and interact with actor In the code above, subscription is done with `on_event` callbacks, but you can also subscribe directly with `receive()` calls, using the `SimpleClient` (and `AsyncSimpleClient`) - interfaces. See our [sample usage](https://github.com/rivet-gg/actor-core/tree/main/clients/python/tests/test_e2e_simple_async.py) for more details. + interfaces. See our [sample usage](https://github.com/rivet-gg/rivetkit/tree/main/clients/python/tests/test_e2e_simple_async.py) for more details. diff --git a/docs/clients/rust.mdx b/docs/clients/rust.mdx index b19084841..ff3e723d5 100644 --- a/docs/clients/rust.mdx +++ b/docs/clients/rust.mdx @@ -9,7 +9,7 @@ import StepRunStudio from "/snippets/step-run-studio.mdx"; import StepDeploy from "/snippets/step-deploy.mdx"; import SetupNextSteps from "/snippets/setup-next-steps.mdx"; -The ActorCore Rust client provides a way to connect to and interact with actors from Rust applications. +The RivetKit Rust client provides a way to connect to and interact with actors from Rust applications. @@ -26,10 +26,10 @@ The ActorCore Rust client provides a way to connect to and interact with actors - Add ActorCore client & related dependencies to your project: + Add RivetKit client & related dependencies to your project: ```sh - cargo add actor-core-client + cargo add rivetkit-client cargo add serde_json cargo add tokio --features full ``` diff --git a/docs/concepts/cors.mdx b/docs/concepts/cors.mdx index 11f0f4c8d..f2512bc1b 100644 --- a/docs/concepts/cors.mdx +++ b/docs/concepts/cors.mdx @@ -14,7 +14,7 @@ You'll need to configure CORS when: ## Example ```ts -import { setup } from "actor-core"; +import { setup } from "@rivetkit/actor"; import counter from "./counter"; const app = setup({ diff --git a/docs/experimental/concepts/edge.mdx b/docs/concepts/edge.mdx similarity index 90% rename from docs/experimental/concepts/edge.mdx rename to docs/concepts/edge.mdx index ca3094952..f9e92df8c 100644 --- a/docs/experimental/concepts/edge.mdx +++ b/docs/concepts/edge.mdx @@ -1,10 +1,9 @@ --- title: Edge Networking +description: Actors run near your users on your provider's global network (if supported). icon: globe --- -Actors run near your users on Rivet's global network by default. - ## Region selection ### Automatic region selection @@ -21,7 +20,7 @@ By default, actors will choose the optimal region based on the client's location The region an actor is created in can be overridden using region options: ```typescript client.ts -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("http://localhost:6420"); diff --git a/docs/concepts/external-sql.mdx b/docs/concepts/external-sql.mdx index aca9deed3..fa512e08d 100644 --- a/docs/concepts/external-sql.mdx +++ b/docs/concepts/external-sql.mdx @@ -36,7 +36,7 @@ There are several options for places to host your SQL database: Here's a basic example of how you might set up a connection to a PostgreSQL database using the `pg` library: ```typescript actor.ts -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; import { Pool } from "pg"; // Create a database connection pool @@ -106,7 +106,7 @@ export default databaseActor; Here's an example using Drizzle ORM for more type-safe database operations: ```typescript actor.ts -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; import { drizzle } from "drizzle-orm/node-postgres"; import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core"; import { Pool } from "pg"; @@ -170,4 +170,4 @@ const userActor = actor({ }); export default userActor; -``` \ No newline at end of file +``` diff --git a/docs/concepts/interacting-with-actors.mdx b/docs/concepts/interacting-with-actors.mdx index 6d2196471..beaa92533 100644 --- a/docs/concepts/interacting-with-actors.mdx +++ b/docs/concepts/interacting-with-actors.mdx @@ -11,7 +11,7 @@ The first step is to create a client that will connect to your actor service: ```typescript TypeScript -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "../src/index"; // Create a client with the connection address and app type @@ -41,7 +41,7 @@ client = ActorClient("http://localhost:6420") ## Finding & Connecting to Actors -ActorCore provides several methods to connect to actors: +RivetKit provides several methods to connect to actors: ### `get(tags, opts)` - Find or Create @@ -414,7 +414,7 @@ chat_room = await client.get( The actor can access these parameters in the `onBeforeConnect` or `createConnState` hook: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ state: { messages: [] }, @@ -519,7 +519,7 @@ Default is `["websocket", "sse"]`, which automatically negotiates the best avail ## Error Handling -ActorCore provides specific error types to help you handle different failure scenarios: +RivetKit provides specific error types to help you handle different failure scenarios: ### Action Errors @@ -540,7 +540,7 @@ try { These errors can be thrown from within the actor with `UserError`: ```typescript -import { actor, UserError } from "actor-core"; +import { actor, UserError } from "@rivetkit/actor"; const documentActor = actor({ state: { content: "" }, @@ -561,7 +561,7 @@ const documentActor = actor({ }); ``` -ActorCore doesn't expose internal errors to clients for security, helping to prevent the exposure of sensitive information or internal implementation details. +RivetKit doesn't expose internal errors to clients for security, helping to prevent the exposure of sensitive information or internal implementation details. ### Other Errors diff --git a/docs/concepts/logging.mdx b/docs/concepts/logging.mdx index cb4e46059..c31856863 100644 --- a/docs/concepts/logging.mdx +++ b/docs/concepts/logging.mdx @@ -46,7 +46,7 @@ Consider this example: ```typescript structured_logging.ts -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const counter = actor({ state: { count: 0 }, @@ -64,7 +64,7 @@ const counter = actor({ ``` ```typescript unstructured_logging.ts -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const counter = actor({ state: { count: 0 }, @@ -92,7 +92,7 @@ Additionally, structured logs can be parsed and queried at scale using tools lik The logger is available in all lifecycle hooks: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const loggingExample = actor({ state: { events: [] }, diff --git a/docs/concepts/overview.mdx b/docs/concepts/overview.mdx index a5e8686d5..a8d76f791 100644 --- a/docs/concepts/overview.mdx +++ b/docs/concepts/overview.mdx @@ -18,7 +18,7 @@ Run this to get started: Here's a complete chat room actor that maintains state and handles messages. We'll explore each component in depth throughout this document: ```typescript chat_room.ts -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; // Define a chat room actor const chatRoom = actor({ @@ -53,7 +53,7 @@ export default chatRoom; To start using your actor, create an app and serve it: ```typescript app.ts -import { setup, serve } from "actor-core"; +import { setup, serve } from "@rivetkit/actor"; import chatRoom from "./chat_room"; // Create the application @@ -75,7 +75,7 @@ export type App = typeof app; Actors maintain state that's stored in memory and automatically persisted. State is defined either as a constant or via a `createState` function: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; // Method 1: State constant const counter1 = actor({ @@ -97,7 +97,7 @@ const counter2 = actor({ Update state by modifying `c.state` in your actions: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const counter = actor({ state: { count: 0 }, @@ -120,7 +120,7 @@ Learn more about [state management](/concepts/state). Actions are functions defined in your actor configuration that clients & other actors can call: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const mathUtils = actor({ state: {}, @@ -141,7 +141,7 @@ Learn more about [actions](/concepts/actions). Actors can broadcast events to connected clients: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const inventory = actor({ createState: () => ({ @@ -163,7 +163,7 @@ const inventory = actor({ You can also send events to specific clients: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const messageService = actor({ state: {}, @@ -197,7 +197,7 @@ await client.chatRoom.get({ channel: "random" }); ### Common Tag Patterns ```typescript -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("http://localhost:6420"); @@ -222,7 +222,7 @@ Actors are created automatically when needed and persist until explicitly shutdo To shut down an actor, use `c.shutdown()` from within an action: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const chatRoom = actor({ createState: () => ({ diff --git a/docs/concepts/testing.mdx b/docs/concepts/testing.mdx index 5f749afec..d3a3a0c2e 100644 --- a/docs/concepts/testing.mdx +++ b/docs/concepts/testing.mdx @@ -3,11 +3,11 @@ title: Testing icon: vial-circle-check --- -ActorCore provides a straightforward testing framework to build reliable and maintainable applications. This guide covers how to write effective tests for your actor-based services. +RivetKit provides a straightforward testing framework to build reliable and maintainable applications. This guide covers how to write effective tests for your actor-based services. ## Setup -To set up testing with ActorCore: +To set up testing with RivetKit: ```bash # Install Vitest @@ -19,12 +19,12 @@ npm test ## Basic Testing Setup -ActorCore includes a test helper called `setupTest` that configures a test environment with in-memory drivers for your actors. This allows for fast, isolated tests without external dependencies. +RivetKit includes a test helper called `setupTest` that configures a test environment with in-memory drivers for your actors. This allows for fast, isolated tests without external dependencies. ```ts tests/my-actor.test.ts import { test, expect } from "vitest"; -import { setupTest } from "actor-core/test"; +import { setupTest } from "@rivetkit/actor/test"; import { app } from "../src/index"; test("my actor test", async (test) => { @@ -43,7 +43,7 @@ test("my actor test", async (test) => { ``` ```ts src/index.ts -import { actor, setup } from "actor-core"; +import { actor, setup } from "@rivetkit/actor"; const myActor = actor({ state: { value: "initial" }, @@ -73,7 +73,7 @@ The test framework uses in-memory drivers that persist state within each test, a ```ts tests/counter.test.ts import { test, expect } from "vitest"; -import { setupTest } from "actor-core/test"; +import { setupTest } from "@rivetkit/actor/test"; import { app } from "../src/index"; test("actor should persist state", async (test) => { @@ -92,7 +92,7 @@ test("actor should persist state", async (test) => { ``` ```ts src/index.ts -import { setup } from "actor-core"; +import { setup } from "@rivetkit/actor"; const counter = actor({ state: { count: 0 }, @@ -123,7 +123,7 @@ For actors that emit events, you can verify events are correctly triggered by su ```ts tests/chat-room.test.ts import { test, expect, vi } from "vitest"; -import { setupTest } from "actor-core/test"; +import { setupTest } from "@rivetkit/actor/test"; import { app } from "../src/index"; test("actor should emit events", async (test) => { @@ -145,7 +145,7 @@ test("actor should emit events", async (test) => { ``` ```ts src/index.ts -import { actor, setup } from "actor-core"; +import { actor, setup } from "@rivetkit/actor"; export const chatRoom = actor({ state: { @@ -174,12 +174,12 @@ export type App = typeof app; ## Testing Schedules -ActorCore's schedule functionality can be tested using Vitest's time manipulation utilities: +RivetKit's schedule functionality can be tested using Vitest's time manipulation utilities: ```ts tests/scheduler.test.ts import { test, expect, vi } from "vitest"; -import { setupTest } from "actor-core/test"; +import { setupTest } from "@rivetkit/actor/test"; import { app } from "../src/index"; test("scheduled tasks should execute", async (test) => { @@ -199,7 +199,7 @@ test("scheduled tasks should execute", async (test) => { ``` ```ts src/index.ts -import { actor, setup } from "actor-core"; +import { actor, setup } from "@rivetkit/actor"; const scheduler = actor({ state: { @@ -241,4 +241,4 @@ The `setupTest` function automatically calls `vi.useFakeTimers()`, allowing you 3. **Mock time**: Use Vitest's timer mocks for testing scheduled operations. 4. **Use realistic data**: Test with data that resembles production scenarios. -ActorCore's testing framework automatically handles server setup and teardown, so you can focus on writing effective tests for your business logic. +RivetKit's testing framework automatically handles server setup and teardown, so you can focus on writing effective tests for your business logic. diff --git a/docs/concepts/topology.mdx b/docs/concepts/topology.mdx index deeee00e7..95ce856b7 100644 --- a/docs/concepts/topology.mdx +++ b/docs/concepts/topology.mdx @@ -1,8 +1,9 @@ --- title: Topologies +icon: list-tree --- -ActorCore supports three topologies that define how actors are distributed and scale. +RivetKit supports three topologies that define how actors are distributed and scale. Each platform configures a default topology appropriate for that environment. In most cases, you can rely on these defaults unless you have specific distribution needs. diff --git a/docs/docs.json b/docs/docs.json index ef3bcd006..0123eb688 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1,11 +1,11 @@ { "$schema": "https://mintlify.com/docs.json", - "name": "ActorCore", - "description": "Stateful Serverless that runs anywhere. The easiest way to build stateful, AI agent, collaborative, or local-first applications.", - "theme": "palm", + "name": "RivetKit", + "description": "Lightweight libraries for backends", + "theme": "maple", "logo": { - "dark": "/logo/dark.svg", - "light": "/logo/light.svg" + "dark": "/images/logo/dark.svg", + "light": "/images/logo/light.svg" }, "styling": { "eyebrows": "breadcrumbs", @@ -25,30 +25,59 @@ "links": [ { "label": "Changelog", - "href": "https://github.com/rivet-gg/actor-core/releases" + "href": "https://github.com/rivet-gg/rivetkit/releases" }, { "label": "Discussions", - "href": "https://github.com/rivet-gg/actor-core/discussions" + "href": "https://github.com/rivet-gg/rivetkit/discussions" } ], "primary": { "type": "github", - "href": "https://github.com/rivet-gg/actor-core" + "href": "https://github.com/rivet-gg/rivetkit" } }, "navigation": { - "tabs": [ + "groups": [ { - "tab": "Home", - "pages": ["introduction"] + "group": "Getting Started", + "pages": [ + "introduction" + ] + }, + { + "group": "Actors", + "pages": [ +"actor/overview", +"actor/quickstart", +"actor/state", +"actor/actions", +"actor/events", + { + "group": "More", + "pages": [ +"actor/schedule", + +"actor/lifecycle", +"actor/connections", +"actor/authentication", + { + "group": "Advanced", + "pages": [ +"actor/metadata", +"actor/types" + + ] + } + ] + } + ] }, { - "tab": "Documentation", + "group": "Integrations", "pages": [ - "overview", { - "group": "Get Started", + "group": "Frameworks", "pages": [ "frameworks/react", "clients/javascript", @@ -66,37 +95,43 @@ ] }, { - "group": "Concepts", + "group": "Libraries & Services", "pages": [ - "concepts/overview", - "concepts/interacting-with-actors", - "concepts/state", - "concepts/actions", - "concepts/events", - "concepts/lifecycle", - "concepts/schedule", - "concepts/authentication", - { - "group": "More", - "pages": [ - "concepts/connections", - "concepts/metadata", - "concepts/testing", - "concepts/logging", - "concepts/cors", - "concepts/scaling", - "concepts/external-sql", - { - "group": "Advanced", - "pages": ["concepts/types", "concepts/topology"] - } - ] - } + "integrations/hono", + "integrations/resend" ] }, + { + "group": "Drivers", + "pages": [ + "drivers/overview", + "drivers/file-system", + "drivers/memory", + "drivers/redis", + "drivers/rivet", + "drivers/cloudflare-workers", + "drivers/build" + ] + } + ] + }, { "group": "Reference", "pages": [ + { + "group": "Common Concepts", + "pages": [ + "concepts/cors", + "concepts/external-sql", + "concepts/interacting-with-actors", + "concepts/logging", + "concepts/overview", + "concepts/scaling", + "concepts/testing", + "concepts/edge", + "concepts/topology" + ] + }, { "group": "LLM Prompts", "pages": [ @@ -109,43 +144,31 @@ "llm/llms-full" ] }, - "support/enterprise" - ] - } - ] - }, - { - "tab": "Integrations", - "pages": [ - "integrations/overview", { - "group": "Integrations", - "pages": [ - "integrations/hono", - "integrations/resend" - ] + "group": "API", + "openapi": { + "source": "./openapi.json", + "directory": "api" + } }, - { - "group": "Drivers", - "pages": [ - "drivers/overview", - "drivers/file-system", - "drivers/memory", - "drivers/redis", - "drivers/rivet", - "drivers/cloudflare-workers", - "drivers/build" + "support/enterprise" ] } - ] - } ] }, + "contextual": { + "options": [ + "copy", + "view", + "chatgpt", + "claude" + ] + }, "footer": { "socials": { "bluesky": "https://bsky.app/profile/rivet.gg", "x": "https://x.com/rivet_gg", - "github": "https://github.com/rivet-gg/actor-core", + "github": "https://github.com/rivet-gg/rivetkit", "discord": "https://rivet.gg/discord" } }, @@ -158,7 +181,7 @@ "redirects": [ { "source": "/changelog/overview", - "destination": "https://github.com/rivet-gg/actor-core/releases" + "destination": "https://github.com/rivet-gg/rivetkit/releases" }, { "source": "/concepts/manage", diff --git a/docs/drivers/build.mdx b/docs/drivers/build.mdx index 3c43a8ea8..5f325217f 100644 --- a/docs/drivers/build.mdx +++ b/docs/drivers/build.mdx @@ -2,7 +2,7 @@ title: Building Drivers --- -Each driver implements common interfaces defined by ActorCore, including: +Each driver implements common interfaces defined by RivetKit, including: - **ActorDriver**: Manages actor state, lifecycle, and persistence - **ManagerDriver**: Handles actor discovery, routing, and scaling @@ -13,9 +13,9 @@ Each driver implements common interfaces defined by ActorCore, including: Get started by looking at source code for the driver interfaces and existing drivers: - **Driver Interfaces** - - **ActorDriver*** `packages/actor-core/src/actor/runtime/driver.ts` - - **ManagerDriver*** `packages/actor-core/src/actor/runtime/driver.ts` - - **CoordinateDriver**: `packages/actor-core/src/topologies/coordinate/driver.ts` + - **ActorDriver*** `packages/rivetkit/src/actor/runtime/driver.ts` + - **ManagerDriver*** `packages/rivetkit/src/actor/runtime/driver.ts` + - **CoordinateDriver**: `packages/rivetkit/src/topologies/coordinate/driver.ts` - **Driver Implementations** - **Memory driver**: `packages/drivers/memory/` - **Redis driver**: `packages/drivers/redis/` diff --git a/docs/drivers/cloudflare-workers.mdx b/docs/drivers/cloudflare-workers.mdx index 3b5679dbc..c1e522fd8 100644 --- a/docs/drivers/cloudflare-workers.mdx +++ b/docs/drivers/cloudflare-workers.mdx @@ -20,7 +20,7 @@ The Cloudflare Workers Driver is an implementation that uses Cloudflare's Durabl ## Usage -There's no need to explicitly configure drivers when using ActorCore with Cloudflare Workers. The platform package automatically sets up the appropriate drivers. +There's no need to explicitly configure drivers when using RivetKit with Cloudflare Workers. The platform package automatically sets up the appropriate drivers. See the [Cloudflare Workers Platform](/platforms/cloudflare-workers) documentation for complete setup instructions. diff --git a/docs/drivers/file-system.mdx b/docs/drivers/file-system.mdx index d472bb5b2..cd2143994 100644 --- a/docs/drivers/file-system.mdx +++ b/docs/drivers/file-system.mdx @@ -30,22 +30,22 @@ The File System Driver is a simple file-based implementation designed for develo ```bash - npm install @actor-core/file-system @actor-core/nodejs + npm install rivetkit/file-system rivetkit/nodejs ``` ```bash - yarn add @actor-core/file-system @actor-core/nodejs + yarn add rivetkit/file-system rivetkit/nodejs ``` ```bash - pnpm add @actor-core/file-system @actor-core/nodejs + pnpm add rivetkit/file-system rivetkit/nodejs ``` ```bash - bun add @actor-core/file-system @actor-core/nodejs + bun add rivetkit/file-system rivetkit/nodejs ``` @@ -55,8 +55,8 @@ The File System Driver is a simple file-based implementation designed for develo Create a simple server using the File System driver: ```typescript src/index.ts - import { serve } from "@actor-core/nodejs" - import { FileSystemManagerDriver, FileSystemActorDriver, FileSystemGlobalState } from "@actor-core/file-system"; + import { serve } from "@rivetkit/nodejs" + import { FileSystemManagerDriver, FileSystemActorDriver, FileSystemGlobalState } from "@rivetkit/file-system"; const fsState = new FileSystemGlobalState(); serve(app, { diff --git a/docs/drivers/memory.mdx b/docs/drivers/memory.mdx index 995ee0c3b..7d15b7a80 100644 --- a/docs/drivers/memory.mdx +++ b/docs/drivers/memory.mdx @@ -30,22 +30,22 @@ The Memory Driver is a simple in-memory implementation designed for development ```bash - npm install @actor-core/memory @actor-core/nodejs + npm install rivetkit/memory rivetkit/nodejs ``` ```bash - yarn add @actor-core/memory @actor-core/nodejs + yarn add rivetkit/memory rivetkit/nodejs ``` ```bash - pnpm add @actor-core/memory @actor-core/nodejs + pnpm add rivetkit/memory rivetkit/nodejs ``` ```bash - bun add @actor-core/memory @actor-core/nodejs + bun add rivetkit/memory rivetkit/nodejs ``` @@ -55,8 +55,8 @@ The Memory Driver is a simple in-memory implementation designed for development Create a simple server using the Memory driver: ```typescript src/index.ts - import { serve } from "@actor-core/nodejs" - import { MemoryManagerDriver, MemoryActorDriver, MemoryGlobalState } from "@actor-core/memory"; + import { serve } from "@rivetkit/nodejs" + import { MemoryManagerDriver, MemoryActorDriver, MemoryGlobalState } from "@rivetkit/memory"; const memoryState = new MemoryGlobalState(); serve(app, { diff --git a/docs/drivers/overview.mdx b/docs/drivers/overview.mdx index e9e74d8c5..622da8e42 100644 --- a/docs/drivers/overview.mdx +++ b/docs/drivers/overview.mdx @@ -5,7 +5,7 @@ sidebarTitle: Overview import DriverNote from '/snippets/driver-note.mdx'; -Drivers in ActorCore the infrastructure layer between your actor code and the underlying systems. +Drivers in RivetKit the infrastructure layer between your actor code and the underlying systems. diff --git a/docs/drivers/redis.mdx b/docs/drivers/redis.mdx index 13f944741..9d191d95b 100644 --- a/docs/drivers/redis.mdx +++ b/docs/drivers/redis.mdx @@ -9,7 +9,7 @@ The Redis Driver is a production-ready implementation that uses Redis for actor - ActorCore requires AOF (Append Only File) persistence to be enabled on your Redis server. See the [Redis Persistence Documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/#append-only-file) for setup instructions. + RivetKit requires AOF (Append Only File) persistence to be enabled on your Redis server. See the [Redis Persistence Documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/#append-only-file) for setup instructions. ## Compatibility @@ -30,22 +30,22 @@ The Redis Driver is a production-ready implementation that uses Redis for actor ```bash - npm install @actor-core/redis @actor-core/nodejs ioredis + npm install rivetkit/redis rivetkit/nodejs ioredis ``` ```bash - yarn add @actor-core/redis @actor-core/nodejs ioredis + yarn add rivetkit/redis rivetkit/nodejs ioredis ``` ```bash - pnpm add @actor-core/redis @actor-core/nodejs ioredis + pnpm add rivetkit/redis rivetkit/nodejs ioredis ``` ```bash - bun add @actor-core/redis @actor-core/nodejs ioredis + bun add rivetkit/redis rivetkit/nodejs ioredis ``` @@ -55,10 +55,10 @@ The Redis Driver is a production-ready implementation that uses Redis for actor Create a Redis connection and set up your server: ```typescript src/index.ts - import { serve } from "@actor-core/nodejs" - import { RedisManagerDriver } from "@actor-core/redis/manager"; - import { RedisActorDriver } from "@actor-core/redis/actor"; - import { RedisCoordinateDriver } from "@actor-core/redis/coordinate"; + import { serve } from "@rivetkit/nodejs" + import { RedisManagerDriver } from "@rivetkit/redis/manager"; + import { RedisActorDriver } from "@rivetkit/redis/actor"; + import { RedisCoordinateDriver } from "@rivetkit/redis/coordinate"; import Redis from "ioredis"; // Create a Redis connection @@ -109,9 +109,9 @@ The Redis driver requires an [ioredis](https://github.com/redis/ioredis) connect ```typescript import Redis from "ioredis"; -import { RedisManagerDriver } from "@actor-core/redis/manager"; -import { RedisActorDriver } from "@actor-core/redis/actor"; -import { RedisCoordinateDriver } from "@actor-core/redis/coordinate"; +import { RedisManagerDriver } from "@rivetkit/redis/manager"; +import { RedisActorDriver } from "@rivetkit/redis/actor"; +import { RedisCoordinateDriver } from "@rivetkit/redis/coordinate"; // Create a Redis connection const redis = new Redis({ diff --git a/docs/drivers/rivet.mdx b/docs/drivers/rivet.mdx index 67c671f39..84542ff77 100644 --- a/docs/drivers/rivet.mdx +++ b/docs/drivers/rivet.mdx @@ -4,7 +4,7 @@ title: Rivet import DriverNote from '/snippets/driver-note.mdx'; -The Rivet Driver is a production-ready implementation that uses Rivet's managed infrastructure for actor state persistence, coordination, and communication. It provides a fully managed environment for ActorCore with built-in scaling, monitoring, and global distribution. +The Rivet Driver is a production-ready implementation that uses Rivet's managed infrastructure for actor state persistence, coordination, and communication. It provides a fully managed environment for RivetKit with built-in scaling, monitoring, and global distribution. @@ -19,7 +19,7 @@ The Rivet Driver is a production-ready implementation that uses Rivet's managed ## Usage -There's no need to explicitly configure drivers when using ActorCore with Rivet. The platform package automatically sets up the appropriate drivers based on your project configuration. +There's no need to explicitly configure drivers when using RivetKit with Rivet. The platform package automatically sets up the appropriate drivers based on your project configuration. See the [Rivet Platform](/platforms/rivet) documentation for complete setup instructions. diff --git a/docs/examples/chat.mdx b/docs/examples/chat.mdx deleted file mode 100644 index fa1d67683..000000000 --- a/docs/examples/chat.mdx +++ /dev/null @@ -1,398 +0,0 @@ -# Realtime Chat App - -In this guide, we're building a realtime chat application. The app consists of: - -- **ChatRoom Actor**: A server-side component that: - - Uses tags to create separate chat channels - - Stores the message history in persistent state - - Provides methods for sending messages and retrieving history - - Broadcasts new messages to all connected clients - -- **Web Client**: A browser-based UI that: - - Prompts users for username and channel name - - Connects to the appropriate channel via tags - - Displays the chat interface - - Loads message history on connection - - Shows new messages in realtime - - Allows users to send messages - -## Set up your project -Create a new actor project: - -```sh -npx create-actor@latest chat-room -p rivet -t counter -``` - -This command creates a new Rivet actor project with the necessary configuration files and dependencies. We're using the counter template as a starting point and will modify it for our chat application. - -## Define the Chat Room actor -Create a file called `src/chat-room.ts` and add the base class structure: - -```typescript -import { Actor, type Rpc } from "actor-core"; - -// State managed by the actor -export interface State { - messages: { username: string; message: string }[]; -} - -export default class ChatRoom extends Actor { - // Methods will be added in the following steps -} -``` - -### Step 1: Initialize the actor state - -First, add the `_onInitialize` method to set up the initial state: - -```typescript -export default class ChatRoom extends Actor { - _onInitialize() { - return { messages: [] }; - } -} -``` - -This method runs when the actor is first created, initializing an empty messages array. - -### Step 2: Add message sending functionality - -Next, add the method to send messages: - -```typescript -export default class ChatRoom extends Actor { - // ...previous code... - - sendMessage( - _rpc: Rpc, - username: string, - message: string - ): void { - // Save message to persistent storage - this._state.messages.push({ username, message }); - - // Broadcast message to all connected clients - this._broadcast("newMessage", username, message); - } -} -``` - -This method: -- Takes a username and message as parameters -- Adds the message to the actor's state for persistence -- Broadcasts the message to all connected clients - -### Step 3: Add history retrieval - -Finally, add a method to retrieve chat history: - -```typescript -export default class ChatRoom extends Actor { - // ...previous code... - - getHistory(_rpc: Rpc): { username: string; message: string }[] { - return this._state.messages; - } -} -``` - -This method allows clients to fetch all previous messages when they connect. - - -```typescript -import { Actor, type Rpc } from "actor-core"; - -// State managed by the actor -export interface State { - messages: { username: string; message: string }[]; -} - -export default class ChatRoom extends Actor { - _onInitialize(): State { - return { messages: [] }; - } - - sendMessage( - _rpc: Rpc, - username: string, - message: string - ): void { - // Save message to persistent storage - this._state.messages.push({ username, message }); - - // Broadcast message to all connected clients - // Event name is 'newMessage', clients can listen for this event - this._broadcast("newMessage", username, message); - } - - getHistory(_rpc: Rpc): { username: string; message: string }[] { - return this._state.messages; - } -} -``` - - -### Step 4: Deploy to Rivet -Deploy your actor with: - -```sh -cd chat-room -npm run deploy -``` - -Follow the prompts to: -1. Sign in to your Rivet account -2. Create or select a project -3. Choose an environment - -After deployment, you'll receive your Actor Manager URL, which clients will use to connect to your chat room. - -## Build a web client -Create a simple web client to interact with your chat room: - -### Step 1: Create the HTML structure - -```html - - - - Rivet Chat Room - - - -

Rivet Chat Room

-
    -
    - - -
    - - -``` - -### Step 2: Add the client script - -Add this script tag just before the closing `` tag: - -```html - -``` - -### Step 3: Load messages and listen for updates - -Update your init function and add the addMessage helper function: - -```html - -``` - -### Step 4: Handle sending messages - -Add the form submit handler to your init function: - -```html - -``` - - -```html - - - - Rivet Chat Room - - - - -

    Rivet Chat Room

    -
      -
      - - -
      - - -``` -
      - diff --git a/docs/examples/live-cursors.mdx b/docs/examples/live-cursors.mdx deleted file mode 100644 index 48a818518..000000000 --- a/docs/examples/live-cursors.mdx +++ /dev/null @@ -1,335 +0,0 @@ ---- -title: Realtime Collaborative Cursor ---- - -Collaborative cursors showing multiple users interacting in real-time - -In this guide, we're building a realtime collaborative cursor application and deploying to Rivet. The app consists of: - -- **CursorRoom Actor**: A server-side component that: - - Tracks cursor positions for all connected users - - Maintains a list of active cursors with their positions - - Handles user connections and disconnections - - Broadcasts cursor updates to all connected clients - -- **Web Client**: A Next.js-based UI that: - - Generates unique user IDs, usernames, and colors - - Tracks and broadcasts cursor movements - - Displays all connected users' cursors in realtime - - Shows a list of active cursors with their coordinates - - Updates cursor positions with efficient throttling - - -image of the cursor app architecture with nextjs, actorcore, and rivet - -## Getting Started - -### Create and Deploy the Project - -1. Create a new Rivet actor project: -```sh -npx create-actor@latest cursor-demo -p rivet -t cursors -``` - -2. Deploy the actors to Rivet: -```sh -cd cursor-demo -npm run deploy -``` - -3. Follow the deployment prompts to: - - Sign in to your Rivet account - - Create or select a project - - Choose an environment - -After deployment, you'll receive your Actor Manager URL, which clients will use to connect to your cursor room. - -### Local Development - -For local development: - -1. Start both development servers: -```sh -yarn dev -``` - -This will start: -- ActorCore server on port 6420 -- Next.js development server on port 3000 - -2. Open multiple browser tabs/windows to `http://localhost:3000` to see the cursors interact - -## About the Project - -The collaborative cursor application demonstrates real-time state synchronization using ActorCore's distributed actor system. Let's explore how it works: - -### The CursorRoom Actor - -The CursorRoom actor (`src/cursor-room.ts`) is the central coordinator that manages the shared state of all cursors: - -```typescript -import { Actor, type Rpc } from "actor-core"; - -// State managed by the actor -export interface State { - cursors: { - [userId: string]: { - username: string; - color: string; - x: number; - y: number; - }; - }; -} - -export default class CursorRoom extends Actor { - // Methods will be added in the following steps -} -``` - -It maintains a state object that maps user IDs to their cursor information, including username, color, and coordinates. - -#### Initialize the Actor State - -First, we define the initialization method: - -```typescript -export default class CursorRoom extends Actor { - _onInitialize() { - return { cursors: {} }; - } -} -``` - -This method runs when the actor is first created and: -- Creates an empty cursors object to store user cursor data -- Prepares the actor to track multiple users in real-time -- Sets up the initial shared state - -#### Add Real-time Cursor Updates - -Next, we implement the cursor update functionality: - -```typescript -export default class CursorRoom extends Actor { - // ...previous code... - - updateCursor( - _rpc: Rpc, - userId: string, - username: string, - color: string, - x: number, - y: number - ): void { - // Update cursor position in state - this._state.cursors[userId] = { username, color, x, y }; - - // Broadcast update to all connected clients - this._broadcast("cursorUpdate", userId, username, color, x, y); - } -} -``` - -This method provides real-time synchronization by: -- Taking user information and cursor coordinates as parameters -- Updating the cursor position in the actor's shared state -- Broadcasting the update to all connected clients instantly -- Ensuring all users see cursor movements in real-time - -#### Handle User Disconnections - -Finally, we add disconnection handling: - -```typescript -export default class CursorRoom extends Actor { - // ...previous code... - - removeCursor(_rpc: Rpc, userId: string): void { - delete this._state.cursors[userId]; - this._broadcast("cursorRemove", userId); - } -} -``` - - -```typescript -import { Actor, type Rpc } from "actor-core"; - -export interface State { - cursors: { - [userId: string]: { - username: string; - color: string; - x: number; - y: number; - }; - }; -} - -export default class CursorRoom extends Actor { - _onInitialize(): State { - return { cursors: {} }; - } - - updateCursor( - _rpc: Rpc, - userId: string, - username: string, - color: string, - x: number, - y: number - ): void { - this._state.cursors[userId] = { username, color, x, y }; - this._broadcast("cursorUpdate", userId, username, color, x, y); - } - - removeCursor(_rpc: Rpc, userId: string): void { - delete this._state.cursors[userId]; - this._broadcast("cursorRemove", userId); - } -} -``` - - -### Real-Time Communication Flow - -The actor system enables efficient real-time updates through the following flow: - -1. **Cursor Movement**: - - Client throttles movement events (16ms intervals) - - Sends position updates to the CursorRoom actor - - Actor broadcasts the update to all connected clients - -2. **Update Reception**: - - Clients receive broadcast events from the actor - - Update their local state with new cursor positions - - Re-render cursor visuals and list accordingly - -3. **User Disconnection**: - - Actor detects the disconnection - - Removes user's cursor from shared state - - Broadcasts removal to all clients - - Clients remove cursor from display - -### React Components - -The client-side React components (`src/components/`) interact with the actor system to provide the real-time cursor experience: - -#### CursorList Component (`src/components/CursorList.tsx`) - -```typescript -function CursorList({ cursors }) { - return ( -
      - {Object.entries(cursors).map(([userId, cursor]) => ( -
      -
      - {cursor.username} - - ({Math.round(cursor.x)}, {Math.round(cursor.y)}) - -
      - ))} -
      - ); -} -``` - -This component: -- Receives cursor data from the actor's state -- Displays a real-time list of all connected users -- Updates automatically when the actor broadcasts changes - -#### CursorPointers Component (`src/components/CursorPointers.tsx`) - -```typescript -function CursorPointers({ cursors, currentUserId }) { - return ( -
      - {Object.entries(cursors).map(([userId, cursor]) => { - if (userId === currentUserId) return null; - - return ( -
      -
      {cursor.username}
      -
      - ); - })} -
      - ); -} -``` - -This component: -- Renders visual cursor indicators for all users except the current one -- Updates positions based on actor state changes -- Provides smooth animations through CSS transforms - -#### App Component Integration (`src/components/App.tsx`) - -```typescript -function App() { - const [cursors, setCursors] = useState({}); - const { actor } = actorCore.useActor("cursorRoom"); - - // Handle cursor updates - actorCore.useActorEvent( - { actor, event: "cursorUpdate" }, - (userId, username, color, x, y) => { - setCursors(prev => ({ - ...prev, - [userId]: { username, color, x, y } - })); - } - ); - - // Handle cursor removals - actorCore.useActorEvent( - { actor, event: "cursorRemove" }, - (userId) => { - setCursors(prev => { - const next = { ...prev }; - delete next[userId]; - return next; - }); - } - ); - - // Throttled cursor update handler - const updateCursor = useCallback( - throttle((x, y) => { - actor?.updateCursor(userId, username, color, x, y); - }, 16), - [actor] - ); - - return ( -
      updateCursor(e.clientX, e.clientY)}> - - -
      - ); -} -``` - -The App component: -- Uses ActorCore React hooks to connect to the CursorRoom actor -- Handles real-time state updates through actor events -- Throttles cursor position updates for efficiency -- Coordinates the CursorList and CursorPointers components \ No newline at end of file diff --git a/docs/experimental/concepts/errors.mdx b/docs/experimental/concepts/errors.mdx deleted file mode 100644 index 1333ed77b..000000000 --- a/docs/experimental/concepts/errors.mdx +++ /dev/null @@ -1 +0,0 @@ -TODO diff --git a/docs/experimental/concepts/multiplayer.mdx b/docs/experimental/concepts/multiplayer.mdx deleted file mode 100644 index f112fc669..000000000 --- a/docs/experimental/concepts/multiplayer.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Multiplayer -icon: gamepad-modern ---- - -TODO - diff --git a/docs/experimental/frameworks/react.mdx b/docs/experimental/frameworks/react.mdx deleted file mode 100644 index b3a9b23b8..000000000 --- a/docs/experimental/frameworks/react.mdx +++ /dev/null @@ -1,393 +0,0 @@ ---- -title: React & RSC -icon: react ---- - - -This integration is unstable and not recommended for production use. If you're looking for a stable integration, please let us know by upvoting the issue on our [GitHub repository](). - - - -## Getting Started - -First, add Rivet's Actor Client SDK to your project. - - - -```bash npm -npm i @rivet-gg/actor-client --save -``` - -```bash yarn -yarn add @rivet-gg/actor-client -``` - -```bash pnpm -pnpm add @rivet-gg/actor-client -``` - -```bash jsr -npx jsr add @rivet-gg/actor-client -``` - - -[Generate a Actor Manager endpoint by using the `rivet` CLI and guide here](/platforms/rivet). Then add the generated URL to your env file. - -### Create React App / Vite - -If you're using Create React App or Vite, you can put `ActorClientProvider` in `App.tsx`. - -```tsx App.tsx -import { Client } from "@rivet-gg/actor-client"; -import { ActorClientProvider } from "@rivet-gg/actor-client/unstable-react"; - -export const App = () => { - const [actorClient] = useState(() => new ActorClient({ - endpoint: process.env.VITE_APP_ACTOR_ENDPOINT, - })); - - return ( - - /* your other context providers / your app */ - - ); -}; -``` - -### Next.js Pages Router - -If you're using Next.js Pages Router you can put `ActorClientProvider` in `_app.tsx`, or in the page where you're going to use actors. - -```tsx {{"title": "pages/_app.tsx"}} -import { Client } from "@rivet-gg/actor-client"; -import { ActorClientProvider } from "@rivet-gg/actor-client/unstable-react"; - -export const App = ({ Component, pageProps }) => { - const [actorClient] = useState(() => new ActorClient({ - endpoint: process.env.NEXT_PUBLIC_APP_ACTOR_ENDPOINT, - })); - - return ( - - - - ); -}; -``` - -### Next.js App Router - -If you're using Next.js app router you can put `ActorClientProvider` in `layout.tsx`, or in the layout where you're going to use actors. -We recommend creating a new component for your providers as shown below, because this SDK needs to be initialized on the client side with `use client` directive. - - -```tsx {{"title":"src/app/providers.tsx"}} -'use client'; -import { Client } from "@rivet-gg/actor-client"; -import { ActorClientProvider } from '@rivet-gg/actor-client/unstable-react'; - -export function Providers({ - children, -}) { - const [actorClient] = useState(() => new ActorClient({ - endpoint: process.env.NEXT_PUBLIC_APP_ACTOR_ENDPOINT, - })); - - return ( - - {children} - - ) -} -``` - -```tsx {{"title": "src/app/layout.tsx"}} -import { Providers } from './providers'; - -export default function Layout({ - children, -}) { - return ( - - - {children} - - - ) -} -``` - - -## Usage - -### Creating an Actor - -`useActor` is a hook that allows you to interact with actors in your React components. Pass to it a name of your actor, [and optionally any other create parameters](https://rivet.gg/docs/manage#options). It will connect to the actor, once the component is mounted, and disconnect when the component is unmounted. - -The hook also provides the actor's state, error, and loading status. Actor created by `useActor` is available to that particular component tree only. If you call `useActor` somewhere else (even with the same arguments), it will create a new actor instance. To share the actor between components, you can use React's context or a state management library. It's safe to pass `actor` instance to child components as a prop, as it does not change between renders. - -Optionally, if you're using TypeScript, you can pass your Actor type to the hook to get the correct types for [actions](https://rivet.gg/docs/actions). - - -```tsx {{"title": "JavaScript"}} -import { useActor } from "@rivet-gg/actor-client/unstable-react"; - -export const MyComponent = () => { - const [{ isLoading, error, actor }] = useActor({ name: "counter" }); // [!code focus] - /* ... */ -}; -``` -```tsx {{"title": "TypeScript"}} -import { useActor } from "@rivet-gg/actor-client/unstable-react"; -import type CounterActor from "../your-path-to-actors-directory/your-actor-file-name"; // replace with your actor file path // [!code focus] - -export const MyComponent = () => { - const [{ isLoading, error, actor }] = useActor({ name: "counter" }); // [!code focus] - /* ... */ -}; -``` - - -### Calling Actions - -To call any defined action on the actor, you can use the `actor.actionName` syntax. Make sure to replace `actionName` with the name of the action you want to call. Be aware that these methods are asynchronous and return a promise. - - -```tsx {{"title": "JavaScript"}} -import { useActor } from "@rivet-gg/actor-client/unstable-react"; - -export const MyComponent = () => { - const [{ isLoading, error, actor }] = useActor({ name: "counter" }); - - return ( -
      - // [!code focus] -
      - ); -}; -``` -```tsx {{"title": "TypeScript"}} -import { useActor } from "@rivet-gg/actor-client/unstable-react"; -import type CounterActor from "../your-path-to-actors-directory/your-actor-file-name"; // replace with your actor file path - -export const MyComponent = () => { - const [{ isLoading, error, actor }] = useActor({ name: "counter" }); - - - return ( -
      - // [!code focus] -
      - ); -}; -``` -
      - -### Events - -To subscribe to actor events changes, you can use the `actor.on` method. - -```tsx {{"title": "TypeScript"}} -import { useActor } from "@rivet-gg/actor-client/unstable-react"; - -export const MyComponent = () => { - const [{ isLoading, error, actor }] = useActor({ name: "counter" }); - - useEffect(() => { - const unsubscribe = actor.on("your event name", (event) => { // [!code focus] - console.log(event); // [!code focus] - }); // [!code focus] - - return () => { // [!code focus] - unsubscribe(); // [!code focus] - }; // [!code focus] - }, []); -}; - -``` - -### Troubleshooting - -#### JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists - -If you're getting this error, you need to add types for React. Normally React does not come with types, so you need to tell Deno to use the types from the different package. - -```tsx your-actor.tsx -// @deno-types=npm:@types/react -import React from "react"; -``` - - -### Advanced Usage - -#### Server Driven UI - -You can use the Actor actions to return React components. [This is done by using React Server Components paradigm](https://react.dev/reference/rsc/server-components). - -In order to render components from your Actor, you need to ensure that your Actor uses the RSC capabilities from the `@rivet-gg/actor` package. Your actions can receive props like any other React component. - -```tsx {{"title": "your-actor.tsx"}} -import { actor } from "@rivet-gg/actor"; -import { createRsc } from "@rivet-gg/actor/unstable-react"; // [!code focus] - -// Helper function to get messages -const getMessages = (state) => { - /* - your code for getting messages, - either from state or external source - */ - return state.messages || []; -}; - -// Create the actor with RSC support -const messageActor = actor({ - state: { messages: [] }, - - // Set up RSC capabilities - ...createRsc(), // [!code focus] - - actions: { - // Action that returns a React component - messages: (c, props: {limit: number}) => { // [!code focus] - const messages = getMessages(c.state); // [!code focus] - // [!code focus] - return
        // [!code focus] - {messages.slice(0, props.limit).map((message) => ( // [!code focus] -
      • {message.text}
      • // [!code focus] - ))} // [!code focus] -
      // [!code focus] - } // [!code focus] - } -}); - -export default messageActor; -``` - -Then, you can use the second return value of the `useActor` hook to use your RscActor's elements. Make sure to wrap those elements in `Suspense`. - -```tsx {{"title": "your-component.tsx"}} -import { useActor } from "@rivet-gg/actor-client/unstable-react"; -import type ActorName from "../your-path-to-actor.tsx"; // replace with your actor file path - -export const MyComponent = () => { - const [// [!code focus] - state,// [!code focus] - { messages: Messages }// [!code focus] - ] = useActor({ name: "actor-name" }); // [!code focus] - - return ( - // [!code focus] - // [!code focus] - // [!code focus] - ); -}; -``` - - -## Experimental Features - - - Methods described below are unstable and may change in the future, do not use them in production. - - -### Manual RSC update - -If you need to re-render the component when something happens in your actor, use `c.updateRsc()` method. This will trigger a re-render of the RSC elements in your actor. Behind the scenes, this method broadcasts an internal event `__rsc` that all RSC elements are listening to. - -```tsx your-actor.tsx -import { actor } from "@rivet-gg/actor"; -import { createRsc } from "@rivet-gg/actor/unstable-react"; - -const messageActor = actor({ - state: { messages: [] }, - - // Set up RSC capabilities - ...createRsc(), - - actions: { - // Action that triggers RSC update - updateMessages: (c) => { - // Trigger an update to re-render RSC components - c.updateRsc(); // [!code focus] - }, - - // RSC component action - messages: (c, props: {limit: number}) => { - /* ... */ - } - } -}); -``` - -### `unstable_createActorHooks` - -`unstable_createActorHooks` is a function that allows you to create type-safe hooks for your actors. It takes an actor name and returns a hook that can be used to interact with that actor. This is useful when you want to create a custom hook that encapsulates the logic for a specific actor and its types. - -```tsx {{"title": "your-actor-hooks.tsx"}} - -import { - unstable_createActorHooks -} from "@rivet-gg/actor-client/unstable-react"; - -export const { - useActor, - useActorEventCallback, - useActorRsc -} = unstable_createActorHooks("counter"); -``` - -#### `useActor` - -`useActor` is a hook that allows you to interact with actors in your React components. Pass to it a name of your actor, [and optionally any other create parameters](https://rivet.gg/docs/manage#options). It will connect to the actor, once the component is mounted, and disconnect when the component is unmounted. - -```tsx {{"title": "your-component.tsx"}} -import { useActor } from "./your-actor-hooks"; - -export const MyComponent = () => { - const [{ isLoading, error, actor }] = useActor(); // [!code focus] - - return ( -
      - // [!code focus] -
      - ); -}; -``` - -#### `useActorEventCallback` - -`useActorEventCallback` is a hook that allows you to subscribe to actor events changes. It takes an event name and a callback function that will be called when the event is triggered. You do not need to wrap the callback in `useCallback` as the hook will handle that for you. - -```tsx {{"title": "your-component.tsx"}} -import { useActorEventCallback } from "./your-actor-hooks"; - -export const MyComponent = ({ actor }) => { - useActorEventCallback({ actor, event: "your event name"}, (event) => { // [!code focus] - console.log(event); // [!code focus] - }); // [!code focus] - - return null; -}; -``` - -#### `useActorRsc` - -`useActorRsc` is a hook that allows you to use React Server Components from your actor. It takes an actor instance and returns a function that can be used to render the RSC elements. Make sure to wrap those elements in Suspense. The second return value of the hook is the function to refetch the RSC elements. - - - This hook won't re-render the component when the actor state changes. If you need to re-render the component when the actor state changes. Use the second return value of the hook to refetch the response. - - -```tsx {{"title": "your-component.tsx"}} -import { useActorRsc } from "./your-actor-hooks"; - -export const MyComponent = ({ actor }) => { - const [Messages, update] = useActorRsc({ actor, fn: 'messages' }); // [!code focus] - - return ( - // [!code focus] - // [!code focus] - // [!code focus] - ); -}; -``` diff --git a/docs/frameworks/react.mdx b/docs/frameworks/react.mdx index a271b1438..0f9ff5200 100644 --- a/docs/frameworks/react.mdx +++ b/docs/frameworks/react.mdx @@ -9,7 +9,7 @@ import StepRunStudio from "/snippets/step-run-studio.mdx"; import StepDeploy from "/snippets/step-deploy.mdx"; import SetupNextSteps from "/snippets/setup-next-steps.mdx"; -Learn how to create realtime, stateful React applications with ActorCore's actor model. +Learn how to create realtime, stateful React applications with RivetKit's actor model. @@ -38,28 +38,28 @@ Learn how to create realtime, stateful React applications with ActorCore's actor - - Navigate to your React project and install the ActorCore client and React packages: + + Navigate to your React project and install the RivetKit client and React packages: ```sh npm cd my-app - npm install actor-core @actor-core/react + npm install rivetkit rivetkit/react ``` ```sh pnpm cd my-app - pnpm add actor-core @actor-core/react + pnpm add rivetkit rivetkit/react ``` ```sh yarn cd my-app - yarn add actor-core @actor-core/react + yarn add rivetkit rivetkit/react ``` ```sh bun cd my-app - bun add actor-core @actor-core/react + bun add rivetkit rivetkit/react ``` @@ -67,17 +67,17 @@ Learn how to create realtime, stateful React applications with ActorCore's actor - Now modify your `src/App.tsx` file to connect to your ActorCore backend: + Now modify your `src/App.tsx` file to connect to your RivetKit backend: ```tsx src/App.tsx - import { createClient } from "actor-core/client"; - import { createReactActorCore } from "@actor-core/react"; + import { createClient } from "@rivetkit/actor/client"; + import { createReactRivetKit } from "@rivetkit/react"; import type { App } from "../actors/app"; import React, { useState } from "react"; // Replace with your endpoint URL after deployment const client = createClient("http://localhost:6420"); - const { useActor, useActorEvent } = createReactActorCore(client); + const { useActor, useActorEvent } = createReactRivetKit(client); function App() { // Connect to counter actor @@ -136,26 +136,26 @@ Learn how to create realtime, stateful React applications with ActorCore's actor ``` - Your React app should now be running and connected to your ActorCore backend. Open your browser to the URL shown in the terminal (typically http://localhost:5173) to see your application. + Your React app should now be running and connected to your RivetKit backend. Open your browser to the URL shown in the terminal (typically http://localhost:5173) to see your application. ## API Reference -The React integration leverages React's hooks system to provide an idiomatic way to interact with ActorCore in React applications. +The React integration leverages React's hooks system to provide an idiomatic way to interact with RivetKit in React applications. -### `createReactActorCore` +### `createReactRivetKit` -The main function that creates React hooks for interacting with ActorCore. It takes a client instance and returns hook functions. +The main function that creates React hooks for interacting with RivetKit. It takes a client instance and returns hook functions. ```tsx -const { useActor, useActorEvent } = createReactActorCore(client); +const { useActor, useActorEvent } = createReactRivetKit(client); ``` #### Parameters -- `client`: The ActorCore client created with `createClient`. +- `client`: The RivetKit client created with `createClient`. #### Returns @@ -212,14 +212,14 @@ This hook doesn't return a value. The subscription is automatically managed by t ### Simple Counter ```tsx -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactRivetKit } from "@rivetkit/react"; import type { App } from "../actors/app"; import { useState } from "react"; -// Connect to ActorCore +// Connect to RivetKit const client = createClient("http://localhost:6420"); -const { useActor, useActorEvent } = createReactActorCore(client); +const { useActor, useActorEvent } = createReactRivetKit(client); function Counter() { // Get actor and track count @@ -240,4 +240,4 @@ function Counter() { } ``` - \ No newline at end of file + diff --git a/docs/images/checks-passed.png b/docs/images/checks-passed.png deleted file mode 100644 index 3303c7736..000000000 Binary files a/docs/images/checks-passed.png and /dev/null differ diff --git a/docs/images/cursors-hero.png b/docs/images/cursors-hero.png deleted file mode 100644 index 705c227f7..000000000 Binary files a/docs/images/cursors-hero.png and /dev/null differ diff --git a/docs/images/hero-dark.gif b/docs/images/hero-dark.gif deleted file mode 100644 index f726b14a8..000000000 Binary files a/docs/images/hero-dark.gif and /dev/null differ diff --git a/docs/images/hero-dark.svg b/docs/images/hero-dark.svg deleted file mode 100644 index 419f5f9ca..000000000 --- a/docs/images/hero-dark.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/images/hero-light.svg b/docs/images/hero-light.svg deleted file mode 100644 index 419f5f9ca..000000000 --- a/docs/images/hero-light.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/images/logo/dark.svg b/docs/images/logo/dark.svg new file mode 100644 index 000000000..b4acf43f1 --- /dev/null +++ b/docs/images/logo/dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/images/logo/light.svg b/docs/images/logo/light.svg new file mode 100644 index 000000000..0972b49a5 --- /dev/null +++ b/docs/images/logo/light.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/images/nextjs-rivet.png b/docs/images/nextjs-rivet.png deleted file mode 100644 index 3c0856157..000000000 Binary files a/docs/images/nextjs-rivet.png and /dev/null differ diff --git a/docs/integrations/hono.mdx b/docs/integrations/hono.mdx index 4f7be010e..f84c8be25 100644 --- a/docs/integrations/hono.mdx +++ b/docs/integrations/hono.mdx @@ -2,14 +2,14 @@ title: Hono --- -[Hono](https://hono.dev/) is a lightweight web framework that works well with ActorCore across multiple deployment platforms. This guide explains how to integrate ActorCore with Hono on different platforms. +[Hono](https://hono.dev/) is a lightweight web framework that works well with RivetKit across multiple deployment platforms. This guide explains how to integrate RivetKit with Hono on different platforms. -## Mounting The ActorCore Router +## Mounting The RivetKit Router -When mounting the ActorCore router at a custom path, you **must** specify the same path in the router configuration using `basePath`: +When mounting the RivetKit router at a custom path, you **must** specify the same path in the router configuration using `basePath`: ```typescript -// Setup the ActorCore app +// Setup the RivetKit app const app = setup({ actors: { counter }, // IMPORTANT: Must specify the same basePath where your router is mounted @@ -27,13 +27,13 @@ This ensures that WebSocket connections and other functionality work correctly w ## Platform-Specific Examples -Each platform has specific requirements for integrating Hono with ActorCore. +Each platform has specific requirements for integrating Hono with RivetKit. ### Cloudflare Workers ```typescript -import { createRouter } from "@actor-core/cloudflare-workers"; -import { setup } from "actor-core"; +import { createRouter } from "@rivetkit/cloudflare-workers"; +import { setup } from "@rivetkit/actor"; import { Hono } from "hono"; import counter from "./counter"; @@ -44,7 +44,7 @@ const honoApp = new Hono(); honoApp.get("/", (c) => c.text("Welcome to my app!")); honoApp.get("/hello", (c) => c.text("Hello, world!")); -// Setup the ActorCore app +// Setup the RivetKit app const app = setup({ actors: { counter }, // IMPORTANT: Must specify the same basePath where your router is mounted @@ -54,7 +54,7 @@ const app = setup({ // Create a router and handler from the app const { router: actorRouter, ActorHandler } = createRouter(app); -// Mount the ActorCore router at /my-path +// Mount the RivetKit router at /my-path honoApp.route("/my-path", actorRouter); // Export the app type for client usage @@ -67,14 +67,14 @@ export { honoApp as default, ActorHandler }; Make sure to update your client connection URL to include the custom path: ```typescript -// If you mounted ActorCore at /my-path -import { createClient } from "actor-core/client"; +// If you mounted RivetKit at /my-path +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("https://your-worker.workers.dev/my-path"); ``` -For this to work with Cloudflare Workers, your `wrangler.json` **must** include specific Durable Object and KV namespace bindings with the exact names expected by ActorCore: +For this to work with Cloudflare Workers, your `wrangler.json` **must** include specific Durable Object and KV namespace bindings with the exact names expected by RivetKit: ```json { @@ -110,7 +110,7 @@ For this to work with Cloudflare Workers, your `wrangler.json` **must** include ```typescript import { serve } from "@hono/node-server"; import { Hono } from "hono"; -import { setup, createRouter } from "@actor-core/nodejs"; +import { setup, createRouter } from "@rivetkit/nodejs"; import counter from "./counter"; // Create your Hono app @@ -120,7 +120,7 @@ const honoApp = new Hono(); honoApp.get("/", (c) => c.text("Welcome to my app!")); honoApp.get("/hello", (c) => c.text("Hello, world!")); -// Setup the ActorCore app +// Setup the RivetKit app const app = setup({ actors: { counter }, // IMPORTANT: Must specify the same basePath where your router is mounted @@ -130,7 +130,7 @@ const app = setup({ // Create a router from the app const { router: actorRouter, injectWebSocket } = createRouter(app); -// Mount the ActorCore router at /my-path +// Mount the RivetKit router at /my-path honoApp.route("/my-path", actorRouter); // Export the app type for client usage @@ -151,8 +151,8 @@ console.log("Server running at http://localhost:6420"); Make sure to update your client connection URL to include the custom path: ```typescript -// If you mounted ActorCore at /my-path -import { createClient } from "actor-core/client"; +// If you mounted RivetKit at /my-path +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("http://localhost:6420/my-path"); @@ -162,7 +162,7 @@ const client = createClient("http://localhost:6420/my-path"); ```typescript import { Hono } from "hono"; -import { setup, createRouter } from "@actor-core/bun"; +import { setup, createRouter } from "@rivetkit/bun"; import counter from "./counter"; // Create your Hono app @@ -172,7 +172,7 @@ const honoApp = new Hono(); honoApp.get("/", (c) => c.text("Welcome to my app!")); honoApp.get("/hello", (c) => c.text("Hello, world!")); -// Setup the ActorCore app +// Setup the RivetKit app const app = setup({ actors: { counter }, // IMPORTANT: Must specify the same basePath where your router is mounted @@ -182,7 +182,7 @@ const app = setup({ // Create a router from the app const { router: actorRouter, webSocketHandler } = createRouter(app); -// Mount the ActorCore router at /my-path +// Mount the RivetKit router at /my-path honoApp.route("/my-path", actorRouter); // Export the app type for client usage @@ -200,8 +200,8 @@ export default { Make sure to update your client connection URL to include the custom path: ```typescript -// If you mounted ActorCore at /my-path -import { createClient } from "actor-core/client"; +// If you mounted RivetKit at /my-path +import { createClient } from "@rivetkit/actor/client"; import type { App } from "./src/index"; const client = createClient("http://localhost:6420/my-path"); diff --git a/docs/integrations/overview.mdx b/docs/integrations/overview.mdx deleted file mode 100644 index 5c2e76949..000000000 --- a/docs/integrations/overview.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: All Integrations -sidebarTitle: Overview ---- - -## Integrations - - - - - - -## Drivers - - - - - - - - - - diff --git a/docs/integrations/resend.mdx b/docs/integrations/resend.mdx index fd25dfb7c..3584ca86f 100644 --- a/docs/integrations/resend.mdx +++ b/docs/integrations/resend.mdx @@ -2,13 +2,13 @@ title: Resend --- -[Resend](https://resend.com) is an email API service that works seamlessly with ActorCore for handling emails and notifications. +[Resend](https://resend.com) is an email API service that works seamlessly with RivetKit for handling emails and notifications. ## Example -See how ActorCore and Resend can power engagement with daily streak notifications. +See how RivetKit and Resend can power engagement with daily streak notifications. -View on GitHub +View on GitHub ## Quickstart @@ -21,7 +21,7 @@ npm install resend ```typescript actors.ts -import { actor, setup } from "actor-core"; +import { actor, setup } from "@rivetkit/actor"; import { Resend } from "resend"; const resend = new Resend(process.env.RESEND_API_KEY); @@ -58,7 +58,7 @@ export type App = typeof app; ```typescript client.ts -import { createClient } from "actor-core"; +import { createClient } from "@rivetkit/actor"; import { App } from "./actors/app.ts"; const client = createClient("http://localhost:8787"); @@ -74,7 +74,7 @@ await userActor.sendExampleEmail(); ### Scheduling Emails -ActorCore's scheduling capabilities with Resend make it easy to send emails at specific times: +RivetKit's scheduling capabilities with Resend make it easy to send emails at specific times: ```typescript actors.ts @@ -212,11 +212,11 @@ await systemMonitor.configure("admin@example.com"); ## Testing -When testing actors that use Resend, you should mock the Resend API to avoid sending real emails during tests. ActorCore's testing utilities combined with Vitest make this straightforward: +When testing actors that use Resend, you should mock the Resend API to avoid sending real emails during tests. RivetKit's testing utilities combined with Vitest make this straightforward: ```typescript import { test, expect, vi, beforeEach } from "vitest"; -import { setupTest } from "actor-core/test"; +import { setupTest } from "@rivetkit/actor/test"; import { app } from "../actors/app"; // Create mock for send method diff --git a/docs/introduction.mdx b/docs/introduction.mdx index 5e64e72ce..cd9322f6c 100644 --- a/docs/introduction.mdx +++ b/docs/introduction.mdx @@ -1,7 +1,7 @@ --- -title: ActorCore -description: Stateful Serverless That Runs Anywhere -sidebarTitle: Introduction +title: Welcome to RivetKit +description: Lightweight libraries for backends +sidebarTitle: Welcome mode: custom --- @@ -12,30 +12,30 @@ import Quotes from "/snippets/landing-quotes.mdx"; import Manifesto from "/snippets/landing-manifesto.mdx"; import FAQ from "/snippets/landing-faq.mdx"; -
      +{/*
      */}
      + {/*

      + Open-source libraries for
      modern backend primitives +

      */} +

      - Stateful Serverless That Runs Anywhere + Lightweight Libraries for Backends

      - The easiest way to build{" "} - stateful,{" "} - AI agent,{" "} - collaborative, or{" "} - local-first{" "} - applications.
      - Deploy to Rivet, Cloudflare, Bun, Node.js, and more. + Simply install one package to get up and running.
      + Connect your database and scale to production.
      + **Just a library, no SaaS.**

      -
      - + -
      window.copyCommand && window.copyCommand(e.currentTarget)}> +
      + + + + {/*
      window.copyCommand && window.copyCommand(e.currentTarget)}>
      npx create-actor@latest
      -
      - -
      - -
      +
      */} + + {/* + */} + + {/*

      Long-Lived, Stateful Compute

      @@ -98,18 +221,17 @@ import FAQ from "/snippets/landing-faq.mdx";

      No servers to manage. Your code runs on-demand and scales automatically with usage.

      -
      +
      */} - +
      */} -
      +

      Reconsider What Your Backend Can Do

      -

      Build powerful applications with ActorCore's comprehensive feature set.

      +

      Build powerful applications with RivetKit's comprehensive feature set.

      @@ -120,7 +242,7 @@ import FAQ from "/snippets/landing-faq.mdx";

      Less Complexity, More Functionality

      -

      ActorCore provides a solid foundation with the features you'd expect for modern apps.

      +

      RivetKit provides a solid foundation with the features you'd expect for modern apps.

      @@ -134,7 +256,7 @@ import FAQ from "/snippets/landing-faq.mdx";
      -
      + {/*

      Supercharged Local Development with the Studio

      @@ -159,7 +281,7 @@ import FAQ from "/snippets/landing-faq.mdx"; Visit The Studio
      -
      +
      */}
      @@ -171,7 +293,7 @@ import FAQ from "/snippets/landing-faq.mdx";

      Join the Community

      -

      Help make ActorCore the universal way to build & scale stateful serverless applications.

      +

      Help make RivetKit the universal way to build & scale stateful serverless applications.

      @@ -180,7 +302,7 @@ import FAQ from "/snippets/landing-faq.mdx"; Discord - + X @@ -188,39 +310,39 @@ import FAQ from "/snippets/landing-faq.mdx"; Bluesky - + Discussions - + Issues
      -
      + {/*
      -
      +
      */} -
      + {/*
      -
      +
      */}
      -

      Performance in every act - thanks to ActorCore.

      +

      Performance in every act - thanks to Rivet Actors.

      diff --git a/docs/llm/claude.mdx b/docs/llm/claude.mdx index 78a2de8d9..7e56941d6 100644 --- a/docs/llm/claude.mdx +++ b/docs/llm/claude.mdx @@ -1,19 +1,19 @@ --- -title: "Using Claude Code with ActorCore" +title: "Using Claude Code with RivetKit" sidebarTitle: "Claude Code" --- -[Claude Code](https://claude.ai/code) is a powerful CLI tool from Anthropic that integrates Claude directly into your development workflow. When working with ActorCore, you can enhance Claude's understanding of the codebase by providing project-specific context. +[Claude Code](https://claude.ai/code) is a powerful CLI tool from Anthropic that integrates Claude directly into your development workflow. When working with RivetKit, you can enhance Claude's understanding of the codebase by providing project-specific context. -## Setting Up CLAUDE.md for ActorCore +## Setting Up CLAUDE.md for RivetKit The `CLAUDE.md` file serves as a memory file for Claude Code, containing important information about your project that persists between sessions. -To set up an effective `CLAUDE.md` file for ActorCore: +To set up an effective `CLAUDE.md` file for RivetKit: - Copy the ActorCore [prompt.txt](/llm/prompt) + Copy the RivetKit [prompt.txt](/llm/prompt) Create a `CLAUDE.md` file in your project root @@ -26,15 +26,15 @@ To set up an effective `CLAUDE.md` file for ActorCore: -## Example Commands for ActorCore +## Example Commands for RivetKit -Here are some useful ways to leverage Claude Code with ActorCore: +Here are some useful ways to leverage Claude Code with RivetKit: ### Understand the Codebase ```bash -# Get an overview of ActorCore's architecture -claude "explain the architecture of ActorCore and how the different topologies work" +# Get an overview of RivetKit's architecture +claude "explain the architecture of RivetKit and how the different topologies work" # Understand how actors communicate claude "explain how actors communicate with each other in the coordinate topology" diff --git a/docs/llm/cursor.mdx b/docs/llm/cursor.mdx index f1a4b3819..2deb2cacc 100644 --- a/docs/llm/cursor.mdx +++ b/docs/llm/cursor.mdx @@ -1,24 +1,24 @@ --- -title: "Using Cursor with ActorCore" +title: "Using Cursor with RivetKit" sidebarTitle: "Cursor" --- -This guide shows how to integrate ActorCore with Cursor to enhance your developer experience through AI-assisted coding. +This guide shows how to integrate RivetKit with Cursor to enhance your developer experience through AI-assisted coding. -## Setting Up Cursor Rules for ActorCore +## Setting Up Cursor Rules for RivetKit -Cursor rules allow you to customize how the AI assistant behaves when working with ActorCore code. By providing project-specific context, you'll get more accurate and relevant suggestions. +Cursor rules allow you to customize how the AI assistant behaves when working with RivetKit code. By providing project-specific context, you'll get more accurate and relevant suggestions. ### Project Rules (Recommended) Project rules are stored in the `.cursor/rules` directory and apply to specific files or folders in your project. -Open your ActorCore project in Cursor +Open your RivetKit project in Cursor Press `Cmd + Shift + P` (Mac) or `Ctrl + Shift + P` (Windows/Linux). - Select `New Cursor Rule` and name it `actorcore-development-guide`. + Select `New Cursor Rule` and name it `RivetKit-development-guide`. Set `Auto Attach` to `**/*.ts`. @@ -29,7 +29,7 @@ Project rules are stored in the `.cursor/rules` directory and apply to specific ### Global Rules -If you frequently work with ActorCore across multiple projects, you can add ActorCore-specific rules globally: +If you frequently work with RivetKit across multiple projects, you can add RivetKit-specific rules globally: @@ -42,15 +42,15 @@ If you frequently work with ActorCore across multiple projects, you can add Acto Global rules will apply to all of your projects when using Cursor. -## Example Queries for ActorCore +## Example Queries for RivetKit -Here are some useful prompts to try with Cursor when working with ActorCore: +Here are some useful prompts to try with Cursor when working with RivetKit: ### Understand the Codebase ``` -# Get an overview of ActorCore's architecture -Explain the architecture of ActorCore and how the different topologies work +# Get an overview of RivetKit's architecture +Explain the architecture of RivetKit and how the different topologies work # Understand how actors communicate Explain how actors communicate with each other in the coordinate topology diff --git a/docs/llm/docs-as-markdown.mdx b/docs/llm/docs-as-markdown.mdx index 97f81a056..02bd9ed3b 100644 --- a/docs/llm/docs-as-markdown.mdx +++ b/docs/llm/docs-as-markdown.mdx @@ -3,14 +3,14 @@ title: Docs As Markdown for LLMs sidebarTitle: Docs as Markdown --- -ActorCore documentation is designed to be easily accessible for Large Language Models (LLMs). All documentation pages are automatically available as plain Markdown files by simply appending `.md` to the URL. +RivetKit documentation is designed to be easily accessible for Large Language Models (LLMs). All documentation pages are automatically available as plain Markdown files by simply appending `.md` to the URL. ## How to Access -To access any ActorCore documentation page as Markdown: +To access any RivetKit documentation page as Markdown: -1. Navigate to the regular documentation URL (e.g., `https://actorcore.org/concepts/state`) -2. Append `.md` to the URL (e.g., `https://actorcore.org/concepts/state.md`) +1. Navigate to the regular documentation URL (e.g., `https://RivetKit.org/concepts/state`) +2. Append `.md` to the URL (e.g., `https://RivetKit.org/concepts/state.md`) This will return the raw Markdown content of the documentation page. @@ -20,7 +20,7 @@ This feature enables seamless integration with AI tools by providing documentati - **Context for LLM chats**: Importing relevant docs into LLM chat sessions for specific context - **Web crawling**: Enabling efficient crawling of documentation content -- **AI agents**: Building AI agents with access to ActorCore documentation +- **AI agents**: Building AI agents with access to RivetKit documentation ## Additional Resources diff --git a/docs/llm/prompt.mdx b/docs/llm/prompt.mdx index 2362bcb72..4f6d8c653 100644 --- a/docs/llm/prompt.mdx +++ b/docs/llm/prompt.mdx @@ -2,15 +2,15 @@ title: "prompt.txt" --- -## Using `prompt.txt` for ActorCore +## Using `prompt.txt` for RivetKit -The `prompt.txt` file provides LLMs with comprehensive information about ActorCore's conventions, terminology, and best practices. To use it: +The `prompt.txt` file provides LLMs with comprehensive information about RivetKit's conventions, terminology, and best practices. To use it: 1. Copy the contents below to your clipboard 2. Paste it into your preferred AI assistant (Claude, ChatGPT, Cursor rules, Windsurf Rules, etc.) -3. Ask your ActorCore development questions after the prompt +3. Ask your RivetKit development questions after the prompt -This structured information helps AI tools provide more accurate and contextually relevant guidance for your ActorCore development tasks. +This structured information helps AI tools provide more accurate and contextually relevant guidance for your RivetKit development tasks. ## AI Editor Guides @@ -25,14 +25,14 @@ Read the integration guide for your editor of choice: ## `prompt.txt` ````markdown prompt.txt -# ActorCore Development Guide +# RivetKit Development Guide -This guide contains essential information for working with the ActorCore project. +This guide contains essential information for working with the RivetKit project. ## Project Naming and Terminology -- Use `ActorCore` when referring to the project in documentation and plain English -- Use `actor-core` (kebab-case) when referring to the project in code, package names, and imports +- Use `RivetKit` when referring to the project in documentation and plain English +- Use `rivetkit` (kebab-case) when referring to the project in code, package names, and imports ### Core Concepts @@ -45,9 +45,9 @@ This guide contains essential information for working with the ActorCore project ## Build Commands - **Type Check:** `yarn check-types` - Verify TypeScript types -- **Check specific package:** `yarn check-types -F actor-core` - Check only specified package +- **Check specific package:** `yarn check-types -F rivetkit` - Check only specified package - **Build:** `yarn build` - Production build using Turbopack -- **Build specific package:** `yarn build -F actor-core` - Build only specified package +- **Build specific package:** `yarn build -F rivetkit` - Build only specified package - **Format:** `yarn fmt` - Format code with Biome ## Driver Implementations @@ -61,7 +61,7 @@ Available driver implementations: ## Platform Support -ActorCore supports multiple runtime environments: +RivetKit supports multiple runtime environments: - **NodeJS**: Standard Node.js server environment - **Cloudflare Workers**: Edge computing environment @@ -92,7 +92,7 @@ When importing from workspace packages, always check the package's `package.json ## Project Structure - Monorepo with Yarn workspaces and Turborepo -- Core code in `packages/actor-core/` +- Core code in `packages/rivetkit/` - Platform implementations in `packages/platforms/` - Driver implementations in `packages/drivers/` diff --git a/docs/llm/windsurf.mdx b/docs/llm/windsurf.mdx index b82314452..05f61bd76 100644 --- a/docs/llm/windsurf.mdx +++ b/docs/llm/windsurf.mdx @@ -1,20 +1,20 @@ --- -title: "Using Windsurf with ActorCore" +title: "Using Windsurf with RivetKit" sidebarTitle: "Windsurf" --- -This guide shows how to integrate ActorCore with Windsurf to enhance your developer experience through AI-assisted coding. +This guide shows how to integrate RivetKit with Windsurf to enhance your developer experience through AI-assisted coding. -## Setting Up Windsurf Rules for ActorCore +## Setting Up Windsurf Rules for RivetKit -Windsurf rules allow you to customize how the AI assistant (Cascade) behaves when working with ActorCore code. By providing project-specific context, you'll get more accurate and relevant suggestions. +Windsurf rules allow you to customize how the AI assistant (Cascade) behaves when working with RivetKit code. By providing project-specific context, you'll get more accurate and relevant suggestions. ### Workspace Rules (Recommended) Workspace rules are stored in the `.windsurfrules` file in your project root and apply to your local workspace. -Open your ActorCore project in Windsurf +Open your RivetKit project in Windsurf Navigate to "Windsurf - Settings" (at the bottom right) > "Settings" > "Cascade" > "Set Workspace AI Rules" and click "Edit Rules." @@ -27,7 +27,7 @@ Workspace rules are stored in the `.windsurfrules` file in your project root and ### Global Rules -If you frequently work with ActorCore across multiple projects, you can add ActorCore-specific rules globally: +If you frequently work with RivetKit across multiple projects, you can add RivetKit-specific rules globally: @@ -40,15 +40,15 @@ If you frequently work with ActorCore across multiple projects, you can add Acto Global rules will apply to all of your projects when using Windsurf. -## Example Queries for ActorCore +## Example Queries for RivetKit -Here are some useful prompts to try with Windsurf when working with ActorCore: +Here are some useful prompts to try with Windsurf when working with RivetKit: ### Understand the Codebase ``` -# Get an overview of ActorCore's architecture -Explain the architecture of ActorCore and how the different topologies work +# Get an overview of RivetKit's architecture +Explain the architecture of RivetKit and how the different topologies work # Understand how actors communicate Explain how actors communicate with each other in the coordinate topology diff --git a/docs/logo/dark.svg b/docs/logo/dark.svg deleted file mode 100644 index c11bc4cce..000000000 --- a/docs/logo/dark.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/logo/light.svg b/docs/logo/light.svg deleted file mode 100644 index 88758481c..000000000 --- a/docs/logo/light.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/overview.mdx b/docs/overview.mdx deleted file mode 100644 index c2b2658c4..000000000 --- a/docs/overview.mdx +++ /dev/null @@ -1,179 +0,0 @@ ---- -title: "Documentation Overview" -sidebarTitle: "Overview" ---- - -ActorCore is a framework for building stateful, scalable, realtime backend applications. Whether you're building multiplayer games, collaborative apps, AI agent backends, or any stateful service, ActorCore provides the tools and patterns to simplify your architecture. - -{/*## What are actors good for? - -Actors in ActorCore are ideal for applications requiring: - -- **Stateful Services**: Applications where maintaining state across interactions is critical. For example, **Collaborative Apps** with shared editing and automatic persistence. -- **Realtime Systems**: Applications requiring fast, in-memory state modifications or push updates to connected clients. For example, **Multiplayer Games** with game rooms and player state. -- **Long-Running Processes**: Tasks that execute over extended periods or in multiple steps. For example, **AI Agents** with ongoing conversations and stateful tool calls. -- **Durability**: Processes that must survive crashes and restarts without data loss. For example, **Durable Execution** workflows that continue after system restarts. -- **Horizontal Scalability**: Systems that need to scale by distributing load across many instances. For example, **Realtime Stream Processing** for stateful event handling. -- **Local-First Architecture**: Systems that synchronize state between offline clients. For example, **Local-First Sync** between devices.*/} - -## Core Concepts - -In ActorCore, each actor has these key characteristics: - -- **State Is Automatically Persisted**: State automatically persists between restarts, upgrades, & crashes -- **State Is Stored In-Memory**: State is stored in memory for high-performance reads/writes while also automatically persisted -- **Isolated State Ownership**: Actors only manage their own state, which can only be modified by the actor itself -- **Communicates via Actions**: How clients and other actors interact with an actor -- **Actions Are Low-Latency**: Actions provide WebSocket-like performance for time-sensitive operations -- **Broadcast Updates With Events**: Actors can publish real-time updates to connected clients - - -## Get Started - -Integrate ActorCore with your project: - - - - Seamlessly connect React applications to your ActorCore backend with ready-to-use hooks and components. - - - Connect to ActorCore from any JavaScript application with the official client library. - - - High-performance Rust client for systems programming and game development. - - - Easy to integrate Python client with support for both sync & async, as well as event based paradigms. - - - -## Deploy - -Choose your platform to deploy ActorCore: - - - - Deploy ActorCore applications on Rivet's purpose-built cloud platform with built-in scaling and management. - - - Run ActorCore at the edge with Cloudflare Workers for global low-latency applications. - - - Use ActorCore with the fast Bun JavaScript runtime for performance-focused development. - - - Run ActorCore in traditional Node.js environments for maximum compatibility. - - - -## Concepts - -Learn the core concepts that power ActorCore applications: - - - - Introduction to ActorCore concepts and architecture. - - - Learn how to connect to and interact with actors from clients. - - - Understand how actor state is managed, persisted, and accessed. - - - Define and implement actor actions (RPCs) for client interaction. - - - Real-time communication with events and broadcasts. - - - Managing the creation, execution, and termination of actors. - - - Schedule tasks and alarms with actors for time-based operations. - - - Implement secure authentication for your ActorCore applications. - - - -### More Concepts - - - - Manage client connections and sessions with actors. - - - Working with actor metadata for discovery and organization. - - - Comprehensive testing strategies for ActorCore applications. - - - Implement logging for debugging and monitoring. - - - Configure Cross-Origin Resource Sharing for web clients. - - - Strategies for scaling ActorCore applications horizontally. - - - Integrate with external SQL databases for additional persistence. - - - Type system and validation in ActorCore for robust applications. - - - Understanding actor topology models for distributed systems. - - - -## Reference - -Integrate ActorCore with Large Language Models: - - - - Use Cursor AI coding assistant with ActorCore. - - - Windsurf AI integration with ActorCore. - - - Claude AI integration with ActorCore. - - - Use ActorCore docs as markdown with LLMs for better assistance. - - - Prompting best practices for ActorCore with LLMs. - - - General LLM integration guide for ActorCore. - - - Comprehensive LLM integration guide for advanced use cases. - - - -## Support - - - - Report bugs and request features on GitHub. - - - Join the community discussions for help and ideas. - - - Stay up-to-date with the latest ActorCore releases. - - - Join the Rivet Discord server to chat with the community. - - - Learn about enterprise support options for ActorCore. - - - diff --git a/docs/platforms/bun.mdx b/docs/platforms/bun.mdx index b3c5b175b..f3f57db41 100644 --- a/docs/platforms/bun.mdx +++ b/docs/platforms/bun.mdx @@ -8,7 +8,7 @@ import ExtraNotes from "/snippets/platform-extra-notes.mdx"; import StepStartFramework from "/snippets/step-start-framework.mdx"; import StepUpdateClient from "/snippets/step-update-client.mdx"; -Bun provides a fast runtime environment for running ActorCore, with excellent performance for both development and production. +Bun provides a fast runtime environment for running RivetKit, with excellent performance for both development and production. @@ -21,15 +21,15 @@ Bun provides a fast runtime environment for running ActorCore, with excellent pe Install the Bun platform package: ```sh - bun add @actor-core/bun + bun add rivetkit/bun ``` - Create a file `src/index.ts` to start your ActorCore server: + Create a file `src/index.ts` to start your RivetKit server: ```typescript src/index.ts - import { serve } from "@actor-core/bun"; + import { serve } from "@rivetkit/bun"; import { app } from "../actors/app"; // Start the server with file-system driver (default) @@ -55,7 +55,7 @@ Bun provides a fast runtime environment for running ActorCore, with excellent pe ## Using Different Drivers -By default, ActorCore for Bun uses the file-system driver, which persists state between restarts. +By default, RivetKit for Bun uses the file-system driver, which persists state between restarts. For simple deployments, you can switch between drivers with: diff --git a/docs/platforms/cloudflare-workers.mdx b/docs/platforms/cloudflare-workers.mdx index c7ab0aee5..40eee56b8 100644 --- a/docs/platforms/cloudflare-workers.mdx +++ b/docs/platforms/cloudflare-workers.mdx @@ -9,7 +9,7 @@ import ExtraNotes from "/snippets/platform-extra-notes.mdx"; import StepStartFramework from "/snippets/step-start-framework.mdx"; import StepUpdateClient from "/snippets/step-update-client.mdx"; -The Cloudflare Workers platform with Durable Objects provides a robust environment for running ActorCore at the edge. +The Cloudflare Workers platform with Durable Objects provides a robust environment for running RivetKit at the edge. @@ -23,19 +23,19 @@ The Cloudflare Workers platform with Durable Objects provides a robust environme ```sh npm - npm install @actor-core/cloudflare-workers + npm install rivetkit/cloudflare-workers ``` ```sh pnpm - pnpm add @actor-core/cloudflare-workers + pnpm add rivetkit/cloudflare-workers ``` ```sh yarn - yarn add @actor-core/cloudflare-workers + yarn add rivetkit/cloudflare-workers ``` ```sh bun - bun add @actor-core/cloudflare-workers + bun add rivetkit/cloudflare-workers ``` @@ -44,7 +44,7 @@ The Cloudflare Workers platform with Durable Objects provides a robust environme Create a file `src/index.ts` to handle Cloudflare Workers integration: ```typescript src/index.ts - import { createHandler } from "@actor-core/cloudflare-workers"; + import { createHandler } from "@rivetkit/cloudflare-workers"; import { app } from "../actors/app"; // Create handlers for Cloudflare Workers @@ -108,7 +108,7 @@ The Cloudflare Workers platform with Durable Objects provides a robust environme npx wrangler deploy ``` - Your ActorCore application will be available at your Cloudflare Workers URL. + Your RivetKit application will be available at your Cloudflare Workers URL. @@ -121,7 +121,7 @@ The Cloudflare Workers platform with Durable Objects provides a robust environme You can access Cloudflare-specific features like the [DurableObjectState](https://developers.cloudflare.com/durable-objects/api/state/) and environment bindings from your actor: ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const myActor = actor({ // Load Cloudflare-specific variables diff --git a/docs/platforms/nodejs.mdx b/docs/platforms/nodejs.mdx index d4a58dfec..a943d8d15 100644 --- a/docs/platforms/nodejs.mdx +++ b/docs/platforms/nodejs.mdx @@ -8,7 +8,7 @@ import ExtraNotes from "/snippets/platform-extra-notes.mdx"; import StepStartFramework from "/snippets/step-start-framework.mdx"; import StepUpdateClient from "/snippets/step-update-client.mdx"; -Node.js provides a robust environment for running ActorCore, ideal for development and production deployments. +Node.js provides a robust environment for running RivetKit, ideal for development and production deployments. @@ -22,28 +22,28 @@ Node.js provides a robust environment for running ActorCore, ideal for developme ```sh npm - npm install @actor-core/nodejs + npm install rivetkit/nodejs ``` ```sh pnpm - pnpm add @actor-core/nodejs + pnpm add rivetkit/nodejs ``` ```sh yarn - yarn add @actor-core/nodejs + yarn add rivetkit/nodejs ``` ```sh bun - bun add @actor-core/nodejs + bun add rivetkit/nodejs ``` - Create a file `src/index.ts` to start your ActorCore server: + Create a file `src/index.ts` to start your RivetKit server: ```typescript src/index.ts - import { serve } from "@actor-core/nodejs"; + import { serve } from "@rivetkit/nodejs"; import { app } from "../actors/app"; // Start the server with file-system driver (default) @@ -70,7 +70,7 @@ Node.js provides a robust environment for running ActorCore, ideal for developme ## Using Different Drivers -By default, ActorCore for Node.js uses the file-system driver, which persists state between restarts. +By default, RivetKit for Node.js uses the file-system driver, which persists state between restarts. For simple deployments, you can switch between drivers with: diff --git a/docs/platforms/rivet.mdx b/docs/platforms/rivet.mdx index 5da2d27e4..6bdaa70ee 100644 --- a/docs/platforms/rivet.mdx +++ b/docs/platforms/rivet.mdx @@ -8,7 +8,7 @@ import StepUpdateClient from "/snippets/step-update-client.mdx"; import ExtraNotes from "/snippets/platform-extra-notes.mdx"; import SetupNextSteps from "/snippets/setup-next-steps.mdx"; -Rivet provides a fully managed cloud service for running ActorCore, with automatic scaling, global deployment, and built-in monitoring. +Rivet provides a fully managed cloud service for running RivetKit, with automatic scaling, global deployment, and built-in monitoring. @@ -22,41 +22,41 @@ Rivet provides a fully managed cloud service for running ActorCore, with automat ```sh npm - npm install @actor-core/rivet + npm install rivetkit/rivet ``` ```sh pnpm - pnpm add @actor-core/rivet + pnpm add rivetkit/rivet ``` ```sh yarn - yarn add @actor-core/rivet + yarn add rivetkit/rivet ``` ```sh bun - bun add @actor-core/rivet + bun add rivetkit/rivet ``` - Deploy your ActorCore application to Rivet: + Deploy your RivetKit application to Rivet: ```sh npm - npx @actor-core/cli@latest deploy rivet actors/app.ts + npx rivetkit/cli@latest deploy rivet actors/app.ts ``` ```sh pnpm - pnpm exec @actor-core/cli@latest deploy rivet actors/app.ts + pnpm exec rivetkit/cli@latest deploy rivet actors/app.ts ``` ```sh yarn - yarn @actor-core/cli@latest deploy rivet actors/app.ts + yarn rivetkit/cli@latest deploy rivet actors/app.ts ``` ```sh bun - bunx @actor-core/cli@latest deploy rivet actors/app.ts + bunx rivetkit/cli@latest deploy rivet actors/app.ts ``` @@ -79,7 +79,7 @@ Rivet provides a fully managed cloud service for running ActorCore, with automat [Rivet's `ActorContext`](https://rivet.gg/docs/javascript-runtime#the-actor-context-object) can be accessed from `createVars`. ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; const myActor = actor({ // Load Rivet-specific variables diff --git a/docs/snippets/cloudflare-deploy.mdx b/docs/snippets/cloudflare-deploy.mdx index 6e09ead37..6970b4a64 100644 --- a/docs/snippets/cloudflare-deploy.mdx +++ b/docs/snippets/cloudflare-deploy.mdx @@ -27,7 +27,7 @@ 4. Update `tests/client.ts` (or wherever your client lives) to use the deployed endpoint. Replace the local endpoint in `client.ts` with your Cloudflare Workers URL: ```typescript - import { createClient } from "actor-core/client"; + import { createClient } from "@rivetkit/actor/client"; import type { App } from "../src/index"; const client = createClient("https://your-worker-subdomain.workers.dev"); diff --git a/docs/snippets/examples/ai-agent-js.mdx b/docs/snippets/examples/ai-agent-js.mdx index ad96fc931..0df15dec8 100644 --- a/docs/snippets/examples/ai-agent-js.mdx +++ b/docs/snippets/examples/ai-agent-js.mdx @@ -1,5 +1,5 @@ ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; import { generateText, tool } from "ai"; import { openai } from "@ai-sdk/openai"; import { getWeather } from "./my-utils"; diff --git a/docs/snippets/examples/ai-agent-react.mdx b/docs/snippets/examples/ai-agent-react.mdx index 1adffc908..27c0bf127 100644 --- a/docs/snippets/examples/ai-agent-react.mdx +++ b/docs/snippets/examples/ai-agent-react.mdx @@ -1,12 +1,12 @@ ```typescript -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactRivetKit } from "@rivetkit/react"; import { useState, useEffect } from "react"; import type { App } from "../actors/app"; import type { Message } from "./actor"; const client = createClient("http://localhost:6420"); -const { useActor, useActorEvent } = createReactActorCore(client); +const { useActor, useActorEvent } = createReactRivetKit(client); export function AIAssistant() { const [{ actor }] = useActor("aiAgent", { tags: { conversationId: "default" } }); @@ -84,4 +84,4 @@ export function AIAssistant() {
      ); } -``` \ No newline at end of file +``` diff --git a/docs/snippets/examples/ai-agent-sqlite.mdx b/docs/snippets/examples/ai-agent-sqlite.mdx index 7b9327847..47f40aab4 100644 --- a/docs/snippets/examples/ai-agent-sqlite.mdx +++ b/docs/snippets/examples/ai-agent-sqlite.mdx @@ -1,6 +1,6 @@ ```typescript -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { generateText, tool } from "ai"; import { openai } from "@ai-sdk/openai"; import { getWeather } from "./my-utils"; diff --git a/docs/snippets/examples/chat-room-js.mdx b/docs/snippets/examples/chat-room-js.mdx index e92f144da..bb5954580 100644 --- a/docs/snippets/examples/chat-room-js.mdx +++ b/docs/snippets/examples/chat-room-js.mdx @@ -1,5 +1,5 @@ ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; export type Message = { sender: string; text: string; timestamp: number; } diff --git a/docs/snippets/examples/chat-room-react.mdx b/docs/snippets/examples/chat-room-react.mdx index 4cbfb4781..36909e89d 100644 --- a/docs/snippets/examples/chat-room-react.mdx +++ b/docs/snippets/examples/chat-room-react.mdx @@ -1,12 +1,12 @@ ```typescript -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactRivetKit } from "@rivetkit/react"; import { useState, useEffect } from "react"; import type { App } from "../actors/app"; import type { Message } from "./actor"; const client = createClient("http://localhost:6420"); -const { useActor, useActorEvent } = createReactActorCore(client); +const { useActor, useActorEvent } = createReactRivetKit(client); export function ChatRoom({ roomId = "general" }) { // Connect to specific chat room using tags diff --git a/docs/snippets/examples/chat-room-sqlite.mdx b/docs/snippets/examples/chat-room-sqlite.mdx index bdf35a46b..c44b8e33e 100644 --- a/docs/snippets/examples/chat-room-sqlite.mdx +++ b/docs/snippets/examples/chat-room-sqlite.mdx @@ -1,6 +1,6 @@ ```typescript -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { messages } from "./schema"; export type Message = { sender: string; text: string; timestamp: number; } diff --git a/docs/snippets/examples/crdt-js.mdx b/docs/snippets/examples/crdt-js.mdx index c3a02c2fc..1a3494a73 100644 --- a/docs/snippets/examples/crdt-js.mdx +++ b/docs/snippets/examples/crdt-js.mdx @@ -1,5 +1,5 @@ ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; import * as Y from 'yjs'; import { encodeStateAsUpdate, applyUpdate } from 'yjs'; diff --git a/docs/snippets/examples/crdt-react.mdx b/docs/snippets/examples/crdt-react.mdx index dd974bb30..f682c2302 100644 --- a/docs/snippets/examples/crdt-react.mdx +++ b/docs/snippets/examples/crdt-react.mdx @@ -1,13 +1,13 @@ ```typescript -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactRivetKit } from "@rivetkit/react"; import { useState, useEffect, useRef } from "react"; import * as Y from 'yjs'; import { applyUpdate, encodeStateAsUpdate } from 'yjs'; import type { App } from "../actors/app"; const client = createClient("http://localhost:6420"); -const { useActor, useActorEvent } = createReactActorCore(client); +const { useActor, useActorEvent } = createReactRivetKit(client); export function YjsEditor({ documentId = "shared-doc" }) { // Connect to specific document using tags diff --git a/docs/snippets/examples/crdt-sqlite.mdx b/docs/snippets/examples/crdt-sqlite.mdx index bba33652d..293e1f969 100644 --- a/docs/snippets/examples/crdt-sqlite.mdx +++ b/docs/snippets/examples/crdt-sqlite.mdx @@ -1,6 +1,6 @@ ```typescript -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import * as Y from 'yjs'; import { encodeStateAsUpdate, applyUpdate } from 'yjs'; import { documents } from "./schema"; @@ -95,4 +95,4 @@ function bufferToBase64(buffer: Uint8Array): string { } export default yjsDocument; -``` \ No newline at end of file +``` diff --git a/docs/snippets/examples/database-js.mdx b/docs/snippets/examples/database-js.mdx index 6d0fe90f3..0e9355073 100644 --- a/docs/snippets/examples/database-js.mdx +++ b/docs/snippets/examples/database-js.mdx @@ -1,5 +1,5 @@ ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; import { authenticate } from "./my-utils"; export type Note = { id: string; content: string; updatedAt: number }; diff --git a/docs/snippets/examples/database-react.mdx b/docs/snippets/examples/database-react.mdx index 184f72fda..9238bb68a 100644 --- a/docs/snippets/examples/database-react.mdx +++ b/docs/snippets/examples/database-react.mdx @@ -1,10 +1,10 @@ ```typescript -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactRivetKit } from "@rivetkit/react"; import { useState, useEffect } from "react"; const client = createClient("http://localhost:6420"); -const { useActor, useActorEvent } = createReactActorCore(client); +const { useActor, useActorEvent } = createReactRivetKit(client); export function NotesApp({ userId }: { userId: string }) { const [notes, setNotes] = useState>([]); diff --git a/docs/snippets/examples/database-sqlite.mdx b/docs/snippets/examples/database-sqlite.mdx index 088c2f3fd..5866a9185 100644 --- a/docs/snippets/examples/database-sqlite.mdx +++ b/docs/snippets/examples/database-sqlite.mdx @@ -1,6 +1,6 @@ ```typescript -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { notes } from "./schema"; import { authenticate } from "./my-utils"; @@ -84,4 +84,4 @@ const userNotes = actor({ }); export default userNotes; -``` \ No newline at end of file +``` diff --git a/docs/snippets/examples/document-js.mdx b/docs/snippets/examples/document-js.mdx index b83f67135..bb5147394 100644 --- a/docs/snippets/examples/document-js.mdx +++ b/docs/snippets/examples/document-js.mdx @@ -1,5 +1,5 @@ ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; export type Cursor = { x: number, y: number, userId: string }; diff --git a/docs/snippets/examples/document-react.mdx b/docs/snippets/examples/document-react.mdx index 97aa2373d..eb908969e 100644 --- a/docs/snippets/examples/document-react.mdx +++ b/docs/snippets/examples/document-react.mdx @@ -1,11 +1,11 @@ ```typescript -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactRivetKit } from "@rivetkit/react"; import { useState, useEffect } from "react"; import type { App } from "../actors/app"; const client = createClient("http://localhost:6420"); -const { useActor, useActorEvent } = createReactActorCore(client); +const { useActor, useActorEvent } = createReactRivetKit(client); export function DocumentEditor() { // Connect to actor for this document ID from URL diff --git a/docs/snippets/examples/document-sqlite.mdx b/docs/snippets/examples/document-sqlite.mdx index d07eb6233..194a5c0fd 100644 --- a/docs/snippets/examples/document-sqlite.mdx +++ b/docs/snippets/examples/document-sqlite.mdx @@ -1,6 +1,6 @@ ```typescript -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { documents, cursors } from "./schema"; export type Cursor = { x: number, y: number, userId: string }; @@ -86,4 +86,4 @@ const document = actor({ }); export default document; -``` \ No newline at end of file +``` diff --git a/docs/snippets/examples/game-js.mdx b/docs/snippets/examples/game-js.mdx index 7f9a89109..903224947 100644 --- a/docs/snippets/examples/game-js.mdx +++ b/docs/snippets/examples/game-js.mdx @@ -1,5 +1,5 @@ ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; export type Position = { x: number; y: number }; export type Input = { x: number; y: number }; diff --git a/docs/snippets/examples/game-react.mdx b/docs/snippets/examples/game-react.mdx index 76f4520b4..8aa3db942 100644 --- a/docs/snippets/examples/game-react.mdx +++ b/docs/snippets/examples/game-react.mdx @@ -1,11 +1,11 @@ ```tsx -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactRivetKit } from "@rivetkit/react"; import { useState, useEffect, useRef } from "react"; import type { Player } from "./actor"; const client = createClient("http://localhost:6420"); -const { useActor, useActorEvent } = createReactActorCore(client); +const { useActor, useActorEvent } = createReactRivetKit(client); export function MultiplayerGame() { const [{ actor, connectionId }] = useActor("gameRoom"); @@ -90,4 +90,4 @@ export function MultiplayerGame() {
      ); } -``` \ No newline at end of file +``` diff --git a/docs/snippets/examples/game-sqlite.mdx b/docs/snippets/examples/game-sqlite.mdx index a55f801a4..0898cefdf 100644 --- a/docs/snippets/examples/game-sqlite.mdx +++ b/docs/snippets/examples/game-sqlite.mdx @@ -1,6 +1,6 @@ ```typescript -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { players, gameSettings } from "./schema"; export type Position = { x: number; y: number }; @@ -168,4 +168,4 @@ const gameRoom = actor({ }); export default gameRoom; -``` \ No newline at end of file +``` diff --git a/docs/snippets/examples/rate-js.mdx b/docs/snippets/examples/rate-js.mdx index 9e136aec7..f190355c3 100644 --- a/docs/snippets/examples/rate-js.mdx +++ b/docs/snippets/examples/rate-js.mdx @@ -1,5 +1,5 @@ ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; // Simple rate limiter - allows 5 requests per minute const rateLimiter = actor({ @@ -37,4 +37,4 @@ const rateLimiter = actor({ }); export default rateLimiter; -``` \ No newline at end of file +``` diff --git a/docs/snippets/examples/rate-react.mdx b/docs/snippets/examples/rate-react.mdx index a25f3c0f4..191667769 100644 --- a/docs/snippets/examples/rate-react.mdx +++ b/docs/snippets/examples/rate-react.mdx @@ -1,11 +1,11 @@ ```typescript -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactRivetKit } from "@rivetkit/react"; import { useState } from "react"; import type { App } from "../actors/app"; const client = createClient("http://localhost:6420"); -const { useActor } = createReactActorCore(client); +const { useActor } = createReactRivetKit(client); export function RateLimiter() { // Connect to API rate limiter for user-123 @@ -40,4 +40,4 @@ export function RateLimiter() {
      ); } -``` \ No newline at end of file +``` diff --git a/docs/snippets/examples/rate-sqlite.mdx b/docs/snippets/examples/rate-sqlite.mdx index dba274ced..a4e73bf09 100644 --- a/docs/snippets/examples/rate-sqlite.mdx +++ b/docs/snippets/examples/rate-sqlite.mdx @@ -1,6 +1,6 @@ ```typescript -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { limiters } from "./schema"; // Simple rate limiter - allows 5 requests per minute @@ -66,4 +66,4 @@ const rateLimiter = actor({ }); export default rateLimiter; -``` \ No newline at end of file +``` diff --git a/docs/snippets/examples/stream-js.mdx b/docs/snippets/examples/stream-js.mdx index db6922a28..924e3be38 100644 --- a/docs/snippets/examples/stream-js.mdx +++ b/docs/snippets/examples/stream-js.mdx @@ -1,5 +1,5 @@ ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; export type StreamState = { topValues: number[]; diff --git a/docs/snippets/examples/stream-react.mdx b/docs/snippets/examples/stream-react.mdx index e58d65b63..c63da07c8 100644 --- a/docs/snippets/examples/stream-react.mdx +++ b/docs/snippets/examples/stream-react.mdx @@ -1,12 +1,12 @@ ```typescript -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactRivetKit } from "@rivetkit/react"; import { useState, useEffect } from "react"; import type { App } from "../actors/app"; import type { StreamState } from "./actor"; // Import shared types from actor const client = createClient("http://localhost:6420"); -const { useActor, useActorEvent } = createReactActorCore(client); +const { useActor, useActorEvent } = createReactRivetKit(client); export function StreamExample() { const [{ actor }] = useActor("streamProcessor"); @@ -52,4 +52,4 @@ export function StreamExample() {
      ); } -``` \ No newline at end of file +``` diff --git a/docs/snippets/examples/stream-sqlite.mdx b/docs/snippets/examples/stream-sqlite.mdx index 313cdedac..a2b7dc532 100644 --- a/docs/snippets/examples/stream-sqlite.mdx +++ b/docs/snippets/examples/stream-sqlite.mdx @@ -1,6 +1,6 @@ ```typescript -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { streams, streamValues } from "./schema"; export type StreamState = { topValues: number[]; }; diff --git a/docs/snippets/examples/sync-js.mdx b/docs/snippets/examples/sync-js.mdx index fb4cbdb6b..6bd859afd 100644 --- a/docs/snippets/examples/sync-js.mdx +++ b/docs/snippets/examples/sync-js.mdx @@ -1,5 +1,5 @@ ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; export type Contact = { id: string; name: string; email: string; phone: string; updatedAt: number; } diff --git a/docs/snippets/examples/sync-react.mdx b/docs/snippets/examples/sync-react.mdx index 1a0d1439f..93bf276bf 100644 --- a/docs/snippets/examples/sync-react.mdx +++ b/docs/snippets/examples/sync-react.mdx @@ -1,11 +1,11 @@ ```tsx -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactRivetKit } from "@rivetkit/react"; import { useState, useEffect, useRef } from "react"; import type { Contact } from "./actor"; const client = createClient("http://localhost:6420"); -const { useActor, useActorEvent } = createReactActorCore(client); +const { useActor, useActorEvent } = createReactRivetKit(client); export function ContactsApp() { const { actor } = useActor("contacts"); diff --git a/docs/snippets/examples/sync-sqlite.mdx b/docs/snippets/examples/sync-sqlite.mdx index 13d96824d..86db417d9 100644 --- a/docs/snippets/examples/sync-sqlite.mdx +++ b/docs/snippets/examples/sync-sqlite.mdx @@ -1,6 +1,6 @@ ```typescript -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { contacts } from "./schema"; export type Contact = { id: string; name: string; email: string; phone: string; updatedAt: number; } @@ -65,4 +65,4 @@ const contactSync = actor({ }); export default contactSync; -``` \ No newline at end of file +``` diff --git a/docs/snippets/examples/tenant-js.mdx b/docs/snippets/examples/tenant-js.mdx index c9ddab5f9..c6e13b69e 100644 --- a/docs/snippets/examples/tenant-js.mdx +++ b/docs/snippets/examples/tenant-js.mdx @@ -1,5 +1,5 @@ ```typescript -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; import { authenticate } from "./my-utils"; // Simple tenant organization actor diff --git a/docs/snippets/examples/tenant-react.mdx b/docs/snippets/examples/tenant-react.mdx index b403ec7d5..e2854cf69 100644 --- a/docs/snippets/examples/tenant-react.mdx +++ b/docs/snippets/examples/tenant-react.mdx @@ -1,12 +1,12 @@ ```typescript -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactRivetKit } from "@rivetkit/react"; import { useState, useEffect } from "react"; import type { App } from "../actors/app"; // Create client and hooks const client = createClient("http://localhost:6420"); -const { useActor } = createReactActorCore(client); +const { useActor } = createReactRivetKit(client); export function OrgDashboard({ orgId }: { orgId: string }) { // State for data diff --git a/docs/snippets/examples/tenant-sqlite.mdx b/docs/snippets/examples/tenant-sqlite.mdx index c5f3700bb..7028524f7 100644 --- a/docs/snippets/examples/tenant-sqlite.mdx +++ b/docs/snippets/examples/tenant-sqlite.mdx @@ -1,6 +1,6 @@ ```typescript -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { members, invoices } from "./schema"; import { authenticate } from "./my-utils"; @@ -50,4 +50,4 @@ const tenant = actor({ }); export default tenant; -``` \ No newline at end of file +``` diff --git a/docs/snippets/integration-existing-projects.mdx b/docs/snippets/integration-existing-projects.mdx index e24aeb540..c8baf0b03 100644 --- a/docs/snippets/integration-existing-projects.mdx +++ b/docs/snippets/integration-existing-projects.mdx @@ -1,3 +1,3 @@ - If you already have an existing application and want to mount ActorCore on a subpath, see our [Hono integration guide](/integrations/hono). Remember to specify the same path in `config.basePath` as where you mount the router. + If you already have an existing application and want to mount RivetKit on a subpath, see our [Hono integration guide](/integrations/hono). Remember to specify the same path in `config.basePath` as where you mount the router. diff --git a/docs/snippets/landing-comparison-table.mdx b/docs/snippets/landing-comparison-table.mdx index dd95b9dca..4cac8baf8 100644 --- a/docs/snippets/landing-comparison-table.mdx +++ b/docs/snippets/landing-comparison-table.mdx @@ -4,12 +4,12 @@ Feature ActorCore - ActorCore + RivetKit
      - ActorCore + RivetKit
      @@ -167,7 +167,7 @@
      - ActorCore + RivetKit
      @@ -237,7 +237,7 @@
      - ActorCore + RivetKit
      @@ -311,7 +311,7 @@
      - ActorCore + RivetKit
      @@ -383,7 +383,7 @@
      - ActorCore + RivetKit
      @@ -454,7 +454,7 @@
      - ActorCore ¹ + RivetKit ¹
      @@ -524,7 +524,7 @@
      - ActorCore + RivetKit
      diff --git a/docs/snippets/landing-faq.mdx b/docs/snippets/landing-faq.mdx index 363619854..4d4131b33 100644 --- a/docs/snippets/landing-faq.mdx +++ b/docs/snippets/landing-faq.mdx @@ -2,20 +2,20 @@ import { Icon } from "@/components/Icon";

      Frequently Asked Questions

      -

      Common questions about stateful serverless and ActorCore.

      +

      Common questions about stateful serverless and RivetKit.

      -

      ActorCore is a framework written in TypeScript that provides high-level functionality. Rivet is an open-source serverless platform written in Rust with features tailored for stateful serverless.

      -

      You can think of it as ActorCore is to Rivet as Next.js is to Vercel.

      -

      While Rivet is the primary maintainer of ActorCore, we intend for this to be community driven.

      +

      RivetKit is a framework written in TypeScript that provides high-level functionality. Rivet is an open-source serverless platform written in Rust with features tailored for stateful serverless.

      +

      You can think of it as RivetKit is to Rivet as Next.js is to Vercel.

      +

      While Rivet is the primary maintainer of RivetKit, we intend for this to be community driven.

      @@ -42,7 +42,7 @@ import { Icon } from "@/components/Icon";
      @@ -97,7 +97,7 @@ import { Icon } from "@/components/Icon";

      - Have more questions? Join our Discord or go to GitHub Discussions. + Have more questions? Join our Discord or go to GitHub Discussions.

      \ No newline at end of file diff --git a/docs/snippets/landing-manifesto.mdx b/docs/snippets/landing-manifesto.mdx index 368fbcb96..68ba9d51b 100644 --- a/docs/snippets/landing-manifesto.mdx +++ b/docs/snippets/landing-manifesto.mdx @@ -1,5 +1,5 @@
      -

      Why We Created ActorCore

      +

      Why We Created RivetKit

      Stateful serverless is the future of how applications will be architected.

      @@ -13,7 +13,7 @@ To popularize stateful serverless, we decided to build something that works for everyone. No vendor lock-in, no steep learning curve, and a community-driven approach that brings the best ideas from different ecosystems together.

      - At Rivet, we maintain an open-source runtime to run stateful serverless workloads – including ActorCore. We see maintaining ActorCore as a rising tide: more people will build applications this way, and we hope to provide the best deployment, monitoring, and collaboration solution for this architecture. + At Rivet, we maintain an open-source runtime to run stateful serverless workloads – including RivetKit. We see maintaining RivetKit as a rising tide: more people will build applications this way, and we hope to provide the best deployment, monitoring, and collaboration solution for this architecture.

      Nathan Flurry, Nicholas Kissel, and the Rivet Team diff --git a/docs/snippets/landing-quotes.mdx b/docs/snippets/landing-quotes.mdx index 1c74dcb75..51923fb0b 100644 --- a/docs/snippets/landing-quotes.mdx +++ b/docs/snippets/landing-quotes.mdx @@ -29,7 +29,7 @@

      @samk0_com

      -

      Great UX & DX possible thanks to @ActorCore_org

      +

      Great UX & DX possible thanks to @RivetKit_org

      Tweet media @@ -41,7 +41,7 @@

      @Social_Quotient

      -

      Loving ActorCore direction!

      +

      Loving RivetKit direction!

      @@ -63,7 +63,7 @@

      @Chinoman10_

      -

      Alternatively, some dude (@NathanFlurry) recently told me about @ActorCore_org, which optionally brings you vendor-flexibility (no lock-in since it's abstracted for you).

      +

      Alternatively, some dude (@NathanFlurry) recently told me about @RivetKit_org, which optionally brings you vendor-flexibility (no lock-in since it's abstracted for you).

      @@ -99,7 +99,7 @@

      @j0g1t

      -

      Your outie uses @ActorCore_org to develop realtime applications.

      +

      Your outie uses @RivetKit_org to develop realtime applications.

      Tweet media @@ -111,7 +111,7 @@

      @alistaiir

      -

      ActorCore looks super awesome.

      +

      RivetKit looks super awesome.

      @@ -121,7 +121,7 @@ {/* Tweet Button */}
      - We're working on publishing full examples related to these snippets. If you find an error, please create an issue. + We're working on publishing full examples related to these snippets. If you find an error, please create an issue.
      diff --git a/docs/snippets/landing-tech.mdx b/docs/snippets/landing-tech.mdx index f91f1a290..38f159fa3 100644 --- a/docs/snippets/landing-tech.mdx +++ b/docs/snippets/landing-tech.mdx @@ -2,7 +2,7 @@

      Runs On Your Stack

      -

      Deploy ActorCore anywhere - from serverless platforms to your own infrastructure with our flexible runtime options.

      +

      Deploy RivetKit anywhere - from serverless platforms to your own infrastructure with our flexible runtime options.

      Don't see the runtime you want? Add your own.

      @@ -24,17 +24,17 @@

      Compute

      - + Vercel Vercel On The Roadmap - + AWS Lambda AWS Lambda On The Roadmap - + Supabase Supabase Help Wanted @@ -57,7 +57,7 @@ Redis Redis - + Postgres Postgres Help Wanted @@ -80,8 +80,8 @@
      @@ -92,12 +92,12 @@ React React - + Next.js Next.js Help Wanted - + Vue Vue Help Wanted @@ -142,12 +142,12 @@ Resend Resend - + Better Auth Better Auth On The Roadmap - + AI SDK AI SDK On The Roadmap @@ -158,22 +158,22 @@

      Local-First Sync

      - + LiveStore LiveStore Available In June - + ZeroSync ZeroSync Help Wanted - + TinyBase TinyBase Help Wanted - + Yjs Yjs Help Wanted @@ -189,7 +189,7 @@
      @@ -297,7 +297,7 @@
      - actor-core dev + rivetkit dev
      diff --git a/docs/snippets/mvp-warning.mdx b/docs/snippets/mvp-warning.mdx index de9d6d53a..f5570703e 100644 --- a/docs/snippets/mvp-warning.mdx +++ b/docs/snippets/mvp-warning.mdx @@ -1,3 +1,3 @@ - ActorCore is still pre-v1.0. Please help us by report bugs on [GitHub Issues](https://github.com/rivet-gg/actor-core/issues)! + RivetKit is still pre-v1.0. Please help us by report bugs on [GitHub Issues](https://github.com/rivet-gg/rivetkit/issues)! diff --git a/docs/snippets/platform-extra-notes.mdx b/docs/snippets/platform-extra-notes.mdx index ed1089751..b88267b20 100644 --- a/docs/snippets/platform-extra-notes.mdx +++ b/docs/snippets/platform-extra-notes.mdx @@ -6,5 +6,5 @@ For production deployments, specify the exact domains that should be allowed to ## Integration with Existing Projects -If you already have an existing application and want to mount ActorCore on a subpath, see our [Hono integration guide](/integrations/hono). Remember to specify the same path in `config.basePath` as where you mount the router. +If you already have an existing application and want to mount RivetKit on a subpath, see our [Hono integration guide](/integrations/hono). Remember to specify the same path in `config.basePath` as where you mount the router. diff --git a/docs/snippets/setup-actor.mdx b/docs/snippets/setup-actor.mdx index d6acb3a5d..f6b22258a 100644 --- a/docs/snippets/setup-actor.mdx +++ b/docs/snippets/setup-actor.mdx @@ -1,5 +1,5 @@ ```typescript src/app.ts -import { actor, setup } from "actor-core"; +import { actor, setup } from "@rivetkit/actor"; // Create actor const counter = actor({ diff --git a/docs/snippets/step-define-actor.mdx b/docs/snippets/step-define-actor.mdx index 1031af0c6..07c1503f8 100644 --- a/docs/snippets/step-define-actor.mdx +++ b/docs/snippets/step-define-actor.mdx @@ -2,7 +2,7 @@ Create a file `actors/app.ts` in your project with your actor definition: ```typescript actors/app.ts - import { actor, setup } from "actor-core"; + import { actor, setup } from "@rivetkit/actor"; // Create actor const counter = actor({ diff --git a/docs/snippets/step-deploy.mdx b/docs/snippets/step-deploy.mdx index 7a2f477ce..8ae946791 100644 --- a/docs/snippets/step-deploy.mdx +++ b/docs/snippets/step-deploy.mdx @@ -1,4 +1,4 @@ - + Now that you have your project running, deploy your application to one of these platforms: diff --git a/docs/snippets/step-run-studio.mdx b/docs/snippets/step-run-studio.mdx index b2f8846f6..38efd0d78 100644 --- a/docs/snippets/step-run-studio.mdx +++ b/docs/snippets/step-run-studio.mdx @@ -1,26 +1,26 @@ - + Launch the development server with: ```sh npm - npx @actor-core/cli@latest dev actors/app.ts + npx rivetkit/cli@latest dev actors/app.ts ``` ```sh pnpm - pnpm exec @actor-core/cli@latest dev actors/app.ts + pnpm exec rivetkit/cli@latest dev actors/app.ts ``` ```sh yarn - yarn @actor-core/cli@latest dev actors/app.ts + yarn rivetkit/cli@latest dev actors/app.ts ``` ```sh bun - bunx @actor-core/cli@latest dev actors/app.ts + bunx rivetkit/cli@latest dev actors/app.ts ``` This will automatically start your app and open the studio in your browser. The studio supports hot-reloading, state inspection, visual RPC testing, and more debugging tools. - ![ActorCore Studio](/images/screenshots/studio/simple.png) + ![RivetKit Studio](/images/screenshots/studio/simple.png) diff --git a/docs/styles/cta.js b/docs/styles/cta.js index 42eabc801..b6bf9f7a3 100644 --- a/docs/styles/cta.js +++ b/docs/styles/cta.js @@ -1,22 +1,22 @@ // CTA titles array const CTA_TITLES = [ - "Performance in every act - thanks to ActorCore.", - "Scale without drama - only with ActorCore.", - "It's time your backend took center-stage - with ActorCore.", - "SQLite the spotlight on performance - with ActorCore.", - "Backend scalability: the SQL - starring ActorCore.", - "Take your state to the edge - ActorCore makes it easy.", - "No state fright - just scalability with ActorCore.", - "Act now, deploy at the edge - with ActorCore.", - "Lights, camera, serverless - powered by ActorCore.", - "Your backend deserves a standing ovation - ActorCore delivers.", - "Cue your backend's best performance - enter ActorCore.", - "Backend performance worth applauding - only with ActorCore.", - "Put your backend center-stage - with ActorCore.", - "Make your backend the main actor - with ActorCore.", - "Give your backend its big break - use ActorCore.", - "Serverless, with no intermissions - powered by ActorCore.", - "Set the stage for serverless success - with ActorCore." + "Performance in every act - thanks to Rivet Actors.", + "Scale without drama - only with Rivet Actors.", + "It's time your backend took center-stage - with Rivet Actors.", + "SQLite the spotlight on performance - with Rivet Actors.", + "Backend scalability: the SQL - starring Rivet Actors.", + "Take your state to the edge - Rivet Actors makes it easy.", + "No state fright - just scalability with Rivet Actors.", + "Act now, deploy at the edge - with Rivet Actors.", + "Lights, camera, serverless - powered by Rivet Actors.", + "Your backend deserves a standing ovation - Rivet Actors delivers.", + "Cue your backend's best performance - enter Rivet Actors.", + "Backend performance worth applauding - only with Rivet Actors.", + "Put your backend center-stage - with Rivet Actors.", + "Make your backend the main actor - with Rivet Actors.", + "Give your backend its big break - use Rivet Actors.", + "Serverless, with no intermissions - powered by Rivet Actors.", + "Set the stage for serverless success - with Rivet Actors." ]; function initializeAllCTAs() { diff --git a/docs/styles/particles.js b/docs/styles/particles.js index b85d746bc..edf9a925b 100644 --- a/docs/styles/particles.js +++ b/docs/styles/particles.js @@ -1,575 +1,575 @@ -// Configuration object for all particle behavior constants -const PARTICLE_CONFIG = { - // Particle appearance - CANVAS_SIZE: 40, - CONTAINER_HEIGHT: 1400, - BASE_OPACITY: 0.08, - ORANGE_COLOR: '#ff4f00', - - // Velocity thresholds - MIN_VELOCITY_THRESHOLD: 2, - MAX_VELOCITY_THRESHOLD: 10, - MAX_VELOCITY: 100, - - // Particle physics - DAMPING: 0.975, - MASS: 0.25, - SPRING_STRENGTH: 0.05, - ROTATION_SPEED: 0.01, - MAX_ROTATIONAL_VELOCITY: 0.5, - - // Distribution - PARTICLE_COUNT: 1000, - BASE_RADIUS: 900, - RADIUS_STD_DEV: 200, - - // Mouse interaction - FORCE_RADIUS: 300, - BASE_PUSH_FORCE: 0.001, - MOVEMENT_FORCE_MULTIPLIER: 1.2, - REPEL_FORCE_RATIO: 0.3, - MOVEMENT_FORCE_RATIO: 1.2, - MAX_MOUSE_VELOCITY: 5000, - - // Animation - TARGET_FPS: 60, - MAX_FRAME_TIME: 0.1, // seconds -}; - -// Global particle state -const GLOBAL_STATE = { - // Particle canvases - particleCanvases: {}, - - // Particles array - particles: [], - - // Animation reference - animationFrameId: null, - - // Current animation state - active: false, - - // Time tracking - lastFrameTime: 0, - - // Mouse tracking variables - mouseX: 0, - mouseY: 0, - lastMouseX: 0, - lastMouseY: 0, - mouseVX: 0, - mouseVY: 0, - lastMouseTime: 0 -}; - -// Canvas and context references -let canvas = null; -let ctx = null; - -// Global DOM elements -let wrapper = null; -let mouseMoveListener = null; -let resizeListener = null; - -// Private Particle class -class Particle { - static CANVAS_SIZE = PARTICLE_CONFIG.CANVAS_SIZE; - static CONTAINER_HEIGHT = PARTICLE_CONFIG.CONTAINER_HEIGHT; - static PARTICLE_RADIUS = PARTICLE_CONFIG.CANVAS_SIZE / 6; - static SHAPES = ['circle', 'square', 'triangle']; - static BASE_OPACITY = PARTICLE_CONFIG.BASE_OPACITY; - static ORANGE_COLOR = PARTICLE_CONFIG.ORANGE_COLOR; - static MIN_VELOCITY_THRESHOLD = PARTICLE_CONFIG.MIN_VELOCITY_THRESHOLD; - static MAX_VELOCITY_THRESHOLD = PARTICLE_CONFIG.MAX_VELOCITY_THRESHOLD; - - static initParticleCanvases() { - // Create offscreen canvases for each particle shape - Particle.SHAPES.forEach(shape => { - // Create white version - const whiteCanvas = document.createElement('canvas'); - whiteCanvas.width = Particle.CANVAS_SIZE; - whiteCanvas.height = Particle.CANVAS_SIZE; - const whiteCtx = whiteCanvas.getContext('2d'); - whiteCtx.fillStyle = 'white'; - whiteCtx.globalAlpha = 1.0; - - // Create orange version - const orangeCanvas = document.createElement('canvas'); - orangeCanvas.width = Particle.CANVAS_SIZE; - orangeCanvas.height = Particle.CANVAS_SIZE; - const orangeCtx = orangeCanvas.getContext('2d'); - orangeCtx.fillStyle = Particle.ORANGE_COLOR; - orangeCtx.globalAlpha = 1.0; - - // Draw the shape on both canvases - [whiteCtx, orangeCtx].forEach(ctx => { - switch (shape) { - case 'circle': - ctx.beginPath(); - ctx.arc(Particle.CANVAS_SIZE/2, Particle.CANVAS_SIZE/2, Particle.PARTICLE_RADIUS, 0, Math.PI * 2); - ctx.fill(); - break; - - case 'square': - const size = Particle.PARTICLE_RADIUS * 2; - ctx.fillRect( - Particle.CANVAS_SIZE/2 - Particle.PARTICLE_RADIUS, - Particle.CANVAS_SIZE/2 - Particle.PARTICLE_RADIUS, - size, - size - ); - break; - - case 'triangle': - const height = Particle.PARTICLE_RADIUS * 2; - const halfWidth = Particle.PARTICLE_RADIUS; - ctx.beginPath(); - ctx.moveTo(Particle.CANVAS_SIZE/2, Particle.CANVAS_SIZE/2 - height/2); - ctx.lineTo(Particle.CANVAS_SIZE/2 - halfWidth, Particle.CANVAS_SIZE/2 + height/2); - ctx.lineTo(Particle.CANVAS_SIZE/2 + halfWidth, Particle.CANVAS_SIZE/2 + height/2); - ctx.closePath(); - ctx.fill(); - break; - } - }); - - if (!GLOBAL_STATE.particleCanvases[shape]) { - GLOBAL_STATE.particleCanvases[shape] = {}; - } - GLOBAL_STATE.particleCanvases[shape].white = whiteCanvas; - GLOBAL_STATE.particleCanvases[shape].orange = orangeCanvas; - }); - } - - constructor() { - this.shape = Particle.SHAPES[Math.floor(Math.random() * Particle.SHAPES.length)]; - this.angle = Math.random() * Math.PI * 2; // Initial random angle - this.rotationSpeed = PARTICLE_CONFIG.ROTATION_SPEED; - - // Add random rotation and rotational velocity - this.rotation = Math.random() * Math.PI * 2; // Random initial rotation - this.rotationalVelocity = (Math.random() - 0.5) * PARTICLE_CONFIG.MAX_ROTATIONAL_VELOCITY; - - this.reset(); - } - - // Generate a normally distributed random number using Box-Muller transform - gaussianRandom(mean = 0, stdDev = 1) { - const u1 = Math.random(); - const u2 = Math.random(); - const z0 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2); - return z0 * stdDev + mean; - } - - reset() { - // Circle parameters - const baseRadius = PARTICLE_CONFIG.BASE_RADIUS; - const stdDev = PARTICLE_CONFIG.RADIUS_STD_DEV; - - // Generate random distance (but keep angle from constructor) - const randomDistance = this.gaussianRandom(0, stdDev); - this.radius = baseRadius + randomDistance; - - // Convert polar to cartesian coordinates - this.updateOriginPosition(); - - // Current position - this.x = this.originX; - this.y = this.originY; - - // Velocity (starts at 0) - this.vx = 0; - this.vy = 0; - - // Physics constants - this.damping = PARTICLE_CONFIG.DAMPING; - this.mass = PARTICLE_CONFIG.MASS; - this.springStrength = PARTICLE_CONFIG.SPRING_STRENGTH; - - // Size and opacity - this.size = Particle.PARTICLE_RADIUS * 2; - this.opacity = 1; - this.opacityFactor = Math.random(); - this.opacityBase = Math.min(0, Math.random() - 0.5); - } - - updateOriginPosition() { - const centerX = window.innerWidth / 2; - const centerY = 180; - this.originX = centerX + this.radius * Math.cos(this.angle); - this.originY = centerY + this.radius * Math.sin(this.angle); - } - - draw(ctx) { - // Skip rendering if particle is outside canvas bounds - if (this.x + Particle.CANVAS_SIZE/2 < 0 || - this.x - Particle.CANVAS_SIZE/2 > ctx.canvas.width || - this.y + Particle.CANVAS_SIZE/2 < 0 || - this.y - Particle.CANVAS_SIZE/2 > ctx.canvas.height) { - return; - } - - // Calculate opacity based on distance from center - const centerX = window.innerWidth / 2; - const centerY = 180; - const dx = this.x - centerX; - const dy = this.y - centerY; - const distance = Math.sqrt(dx * dx + dy * dy); - - // Fade out between 600px and 1200px from center - const minDistance = 600; - const maxDistance = 1200; - this.opacity = this.opacityBase + Math.max(0, Math.min(1, 1 - (distance - minDistance) / (maxDistance - minDistance))) * this.opacityFactor; - - // Calculate velocity magnitude directly - simpler and more accurate - const velocityMagnitude = Math.sqrt(this.vx * this.vx + this.vy * this.vy); - - // Calculate color mix based on velocity - lowered threshold for more visible effect - const colorMix = Math.min(1, (velocityMagnitude - Particle.MIN_VELOCITY_THRESHOLD) / (Particle.MAX_VELOCITY_THRESHOLD - Particle.MIN_VELOCITY_THRESHOLD) * 0.5); - - // Save the current context state - ctx.save(); - - // Translate to particle position and rotate - ctx.translate(this.x, this.y); - ctx.rotate(this.rotation); - - // Draw white (base) particle - if (this.opacity > 0) { - ctx.globalAlpha = this.opacity * Particle.BASE_OPACITY * (1 - colorMix); - ctx.drawImage( - GLOBAL_STATE.particleCanvases[this.shape].white, - -Particle.CANVAS_SIZE/2, - -Particle.CANVAS_SIZE/2 - ); - } - - // Draw orange (active) particle - if (colorMix > 0) { - // Ignore this.opacity since any fast particle will turn orange - ctx.globalAlpha = colorMix; - ctx.drawImage( - GLOBAL_STATE.particleCanvases[this.shape].orange, - -Particle.CANVAS_SIZE/2, - -Particle.CANVAS_SIZE/2 - ); - } - - // Restore the context state - ctx.restore(); - ctx.globalAlpha = 1; - } - - update(deltaTime) { - // Update the angle - this.angle += this.rotationSpeed * deltaTime; - if (this.angle > Math.PI * 2) { - this.angle -= Math.PI * 2; - } - - // Update rotation - this.rotation += this.rotationalVelocity * deltaTime; - if (this.rotation > Math.PI * 2) { - this.rotation -= Math.PI * 2; - } - - // Update origin position based on new angle - this.updateOriginPosition(); - - // Calculate spring force back to origin - const dx = this.originX - this.x; - const dy = this.originY - this.y; - const springForceX = dx * this.springStrength; - const springForceY = dy * this.springStrength; - - // Apply spring force (scaled by deltaTime) - this.vx += springForceX * deltaTime; - this.vy += springForceY * deltaTime; - - // Apply damping - this.vx *= Math.pow(this.damping, deltaTime * 60); - this.vy *= Math.pow(this.damping, deltaTime * 60); - - // Clamp velocity to prevent extreme speeds - const currentSpeed = Math.sqrt(this.vx * this.vx + this.vy * this.vy); - if (currentSpeed > 1000) { - this.vx = (this.vx / currentSpeed) * 1000; - this.vy = (this.vy / currentSpeed) * 1000; - } - - // Update position - this.x += this.vx; - this.y += this.vy; - } - - applyForce(fx, fy) { - this.vx += fx; - this.vy += fy; - } -} - -// Create the particle system and initialize everything needed -function createParticleSystem() { - console.log("[Particles] Creating particle system"); - - if (GLOBAL_STATE.active) { - console.log("[Particles] System already active, skipping creation"); - return; // Already active - } - - // Initialize particle canvases if needed - if (Object.keys(GLOBAL_STATE.particleCanvases).length === 0) { - Particle.initParticleCanvases(); - } - - // Create particles if needed - if (GLOBAL_STATE.particles.length === 0) { - console.log("[Particles] Creating particles"); - GLOBAL_STATE.particles = Array.from({ length: PARTICLE_CONFIG.PARTICLE_COUNT }, () => new Particle()); - } - - // Create wrapper element - wrapper = document.createElement('div'); - wrapper.setAttribute('data-particle-wrapper', 'true'); - Object.assign(wrapper.style, { - position: 'absolute', - top: '0', - left: '0', - width: '100%', - height: `${Particle.CONTAINER_HEIGHT}px`, - overflow: 'hidden', - pointerEvents: 'none', - zIndex: '-1' - }); - - // Create canvas - canvas = document.createElement('canvas'); - canvas.setAttribute('data-particles', 'true'); - Object.assign(canvas.style, { - position: 'absolute', - top: '0', - left: '0', - width: '100%', - height: `${Particle.CONTAINER_HEIGHT}px`, - pointerEvents: 'none', - zIndex: '-1' - }); - - // Set canvas size - canvas.width = window.innerWidth; - canvas.height = Particle.CONTAINER_HEIGHT; - - // Get context - ctx = canvas.getContext('2d'); - - // Add canvas to wrapper - wrapper.appendChild(canvas); - - // Add wrapper to body - if (document.body.firstChild) { - document.body.insertBefore(wrapper, document.body.firstChild); - } else { - document.body.appendChild(wrapper); - } - - // Set up mouse tracking - mouseMoveListener = (e) => { - const currentTime = performance.now(); - const deltaTime = (currentTime - GLOBAL_STATE.lastMouseTime) / 1000; - - // Use page coordinates for absolute positioning - GLOBAL_STATE.mouseX = e.pageX; - GLOBAL_STATE.mouseY = e.pageY; - - if (deltaTime > 0) { - GLOBAL_STATE.mouseVX = (GLOBAL_STATE.mouseX - GLOBAL_STATE.lastMouseX) / deltaTime; - GLOBAL_STATE.mouseVY = (GLOBAL_STATE.mouseY - GLOBAL_STATE.lastMouseY) / deltaTime; - - const maxVelocity = PARTICLE_CONFIG.MAX_MOUSE_VELOCITY; - GLOBAL_STATE.mouseVX = Math.max(Math.min(GLOBAL_STATE.mouseVX, maxVelocity), -maxVelocity); - GLOBAL_STATE.mouseVY = Math.max(Math.min(GLOBAL_STATE.mouseVY, maxVelocity), -maxVelocity); - } - - GLOBAL_STATE.lastMouseX = GLOBAL_STATE.mouseX; - GLOBAL_STATE.lastMouseY = GLOBAL_STATE.mouseY; - GLOBAL_STATE.lastMouseTime = currentTime; - }; - - document.addEventListener('mousemove', mouseMoveListener); - - // Handle resize - resizeListener = () => { - if (canvas) { - canvas.width = window.innerWidth; - canvas.height = Particle.CONTAINER_HEIGHT; - GLOBAL_STATE.particles.forEach(particle => particle.reset()); - } - }; - - window.addEventListener('resize', resizeListener); - - // Initialize timing - GLOBAL_STATE.lastFrameTime = performance.now(); - GLOBAL_STATE.lastMouseTime = performance.now(); - - // Start animation - startAnimation(); - - // Mark as active - GLOBAL_STATE.active = true; -} - -// Start the animation loop -function startAnimation() { - if (GLOBAL_STATE.animationFrameId !== null) { - // Animation already running - return; - } - - console.log("[Particles] Starting animation"); - - const TARGET_FPS = PARTICLE_CONFIG.TARGET_FPS; - const FRAME_TIME = 1000 / TARGET_FPS; - const FORCE_RADIUS = PARTICLE_CONFIG.FORCE_RADIUS; - - function animate() { - const currentTime = performance.now(); - const timeSinceLastFrame = currentTime - GLOBAL_STATE.lastFrameTime; - - // Only render if enough time has passed for next frame - if (timeSinceLastFrame >= FRAME_TIME) { - const deltaTime = Math.min(timeSinceLastFrame / 1000, PARTICLE_CONFIG.MAX_FRAME_TIME); - GLOBAL_STATE.lastFrameTime = currentTime; - - // Update all particles - GLOBAL_STATE.particles.forEach(particle => { - const dx = particle.x - GLOBAL_STATE.mouseX; - const dy = particle.y - GLOBAL_STATE.mouseY; - const distance = Math.sqrt(dx * dx + dy * dy); - - if (distance < FORCE_RADIUS) { - // Calculate mouse velocity magnitude - const mouseSpeed = Math.sqrt(GLOBAL_STATE.mouseVX * GLOBAL_STATE.mouseVX + GLOBAL_STATE.mouseVY * GLOBAL_STATE.mouseVY); - // Normalize mouse velocity to 0-1 range - const normalizedSpeed = Math.min(mouseSpeed / PARTICLE_CONFIG.MAX_VELOCITY, 1); - - // Calculate base force factor with smoother distance falloff - const forceFactor = Math.pow(1 - distance / FORCE_RADIUS, 1.5) * - normalizedSpeed * - PARTICLE_CONFIG.BASE_PUSH_FORCE * - PARTICLE_CONFIG.MOVEMENT_FORCE_MULTIPLIER * - deltaTime * 60; - - // Calculate repulsion direction (away from mouse) - const repelDirX = dx / distance; - const repelDirY = dy / distance; - - // Get normalized mouse movement direction - const mvx = GLOBAL_STATE.mouseVX / (mouseSpeed || 1); - const mvy = GLOBAL_STATE.mouseVY / (mouseSpeed || 1); - - // Calculate movement influence based on distance - const movementInfluence = Math.pow(1 - distance / FORCE_RADIUS, 1.2); - - // Combine forces with reduced repulsion and increased movement - const repelForce = PARTICLE_CONFIG.REPEL_FORCE_RATIO * mouseSpeed; - const moveForce = mouseSpeed * PARTICLE_CONFIG.MOVEMENT_FORCE_RATIO; - - // More emphasis on movement direction, less on repulsion - const fx = (repelDirX * repelForce * 0.5 + mvx * moveForce) * forceFactor * movementInfluence; - const fy = (repelDirY * repelForce * 0.5 + mvy * moveForce) * forceFactor * movementInfluence; - - particle.applyForce(fx, fy); - } - - particle.update(deltaTime); - }); - - // Make sure canvas and context exist before drawing - if (canvas && ctx) { - // Clear canvas - ctx.clearRect(0, 0, canvas.width, canvas.height); - - // Draw particles - GLOBAL_STATE.particles.forEach(particle => { - particle.draw(ctx); - }); - } - } - - // Continue animation - GLOBAL_STATE.animationFrameId = requestAnimationFrame(animate); - } - - GLOBAL_STATE.animationFrameId = requestAnimationFrame(animate); -} - -// Completely destroy the particle system -function destroyParticleSystem() { - console.log("[Particles] Destroying particle system"); - - // Cancel animation frame - if (GLOBAL_STATE.animationFrameId !== null) { - cancelAnimationFrame(GLOBAL_STATE.animationFrameId); - GLOBAL_STATE.animationFrameId = null; - } - - // Remove event listeners - if (mouseMoveListener) { - document.removeEventListener('mousemove', mouseMoveListener); - mouseMoveListener = null; - } - - if (resizeListener) { - window.removeEventListener('resize', resizeListener); - resizeListener = null; - } - - // Remove DOM elements - if (wrapper && document.body.contains(wrapper)) { - wrapper.remove(); - } - - // Clear references - canvas = null; - ctx = null; - wrapper = null; - - // Mark as inactive - GLOBAL_STATE.active = false; -} - -// Check if the page should show particles (.landing-root exists) -function shouldShowParticles() { - return document.querySelector('.landing-root') !== null; -} - -// Handle mutation observer updates -function observerInitialize() { - // Check if we should show or hide particles - if (shouldShowParticles()) { - if (!GLOBAL_STATE.active) { - console.log("[Particles] Landing page detected - creating particles"); - createParticleSystem(); - } - } else { - if (GLOBAL_STATE.active) { - console.log("[Particles] Not a landing page - destroying particles"); - destroyParticleSystem(); - } - } - - // Mark all containers as initialized - document.querySelectorAll('.particle-container:not([data-particle-initialized])').forEach(container => { - container.setAttribute('data-particle-initialized', 'true'); - }); -} - -// Compatibility function for observer-manager.js -function initializeAllParticles() { - observerInitialize(); -} - -// Initialize after a small delay -//setTimeout(observerInitialize, 50); +//// Configuration object for all particle behavior constants +//const PARTICLE_CONFIG = { +// // Particle appearance +// CANVAS_SIZE: 40, +// CONTAINER_HEIGHT: 1400, +// BASE_OPACITY: 0.08, +// ORANGE_COLOR: '#ff4f00', +// +// // Velocity thresholds +// MIN_VELOCITY_THRESHOLD: 2, +// MAX_VELOCITY_THRESHOLD: 10, +// MAX_VELOCITY: 100, +// +// // Particle physics +// DAMPING: 0.975, +// MASS: 0.25, +// SPRING_STRENGTH: 0.05, +// ROTATION_SPEED: 0.01, +// MAX_ROTATIONAL_VELOCITY: 0.5, +// +// // Distribution +// PARTICLE_COUNT: 1000, +// BASE_RADIUS: 900, +// RADIUS_STD_DEV: 200, +// +// // Mouse interaction +// FORCE_RADIUS: 300, +// BASE_PUSH_FORCE: 0.001, +// MOVEMENT_FORCE_MULTIPLIER: 1.2, +// REPEL_FORCE_RATIO: 0.3, +// MOVEMENT_FORCE_RATIO: 1.2, +// MAX_MOUSE_VELOCITY: 5000, +// +// // Animation +// TARGET_FPS: 60, +// MAX_FRAME_TIME: 0.1, // seconds +//}; +// +//// Global particle state +//const GLOBAL_STATE = { +// // Particle canvases +// particleCanvases: {}, +// +// // Particles array +// particles: [], +// +// // Animation reference +// animationFrameId: null, +// +// // Current animation state +// active: false, +// +// // Time tracking +// lastFrameTime: 0, +// +// // Mouse tracking variables +// mouseX: 0, +// mouseY: 0, +// lastMouseX: 0, +// lastMouseY: 0, +// mouseVX: 0, +// mouseVY: 0, +// lastMouseTime: 0 +//}; +// +//// Canvas and context references +//let canvas = null; +//let ctx = null; +// +//// Global DOM elements +//let wrapper = null; +//let mouseMoveListener = null; +//let resizeListener = null; +// +//// Private Particle class +//class Particle { +// static CANVAS_SIZE = PARTICLE_CONFIG.CANVAS_SIZE; +// static CONTAINER_HEIGHT = PARTICLE_CONFIG.CONTAINER_HEIGHT; +// static PARTICLE_RADIUS = PARTICLE_CONFIG.CANVAS_SIZE / 6; +// static SHAPES = ['circle', 'square', 'triangle']; +// static BASE_OPACITY = PARTICLE_CONFIG.BASE_OPACITY; +// static ORANGE_COLOR = PARTICLE_CONFIG.ORANGE_COLOR; +// static MIN_VELOCITY_THRESHOLD = PARTICLE_CONFIG.MIN_VELOCITY_THRESHOLD; +// static MAX_VELOCITY_THRESHOLD = PARTICLE_CONFIG.MAX_VELOCITY_THRESHOLD; +// +// static initParticleCanvases() { +// // Create offscreen canvases for each particle shape +// Particle.SHAPES.forEach(shape => { +// // Create white version +// const whiteCanvas = document.createElement('canvas'); +// whiteCanvas.width = Particle.CANVAS_SIZE; +// whiteCanvas.height = Particle.CANVAS_SIZE; +// const whiteCtx = whiteCanvas.getContext('2d'); +// whiteCtx.fillStyle = 'white'; +// whiteCtx.globalAlpha = 1.0; +// +// // Create orange version +// const orangeCanvas = document.createElement('canvas'); +// orangeCanvas.width = Particle.CANVAS_SIZE; +// orangeCanvas.height = Particle.CANVAS_SIZE; +// const orangeCtx = orangeCanvas.getContext('2d'); +// orangeCtx.fillStyle = Particle.ORANGE_COLOR; +// orangeCtx.globalAlpha = 1.0; +// +// // Draw the shape on both canvases +// [whiteCtx, orangeCtx].forEach(ctx => { +// switch (shape) { +// case 'circle': +// ctx.beginPath(); +// ctx.arc(Particle.CANVAS_SIZE/2, Particle.CANVAS_SIZE/2, Particle.PARTICLE_RADIUS, 0, Math.PI * 2); +// ctx.fill(); +// break; +// +// case 'square': +// const size = Particle.PARTICLE_RADIUS * 2; +// ctx.fillRect( +// Particle.CANVAS_SIZE/2 - Particle.PARTICLE_RADIUS, +// Particle.CANVAS_SIZE/2 - Particle.PARTICLE_RADIUS, +// size, +// size +// ); +// break; +// +// case 'triangle': +// const height = Particle.PARTICLE_RADIUS * 2; +// const halfWidth = Particle.PARTICLE_RADIUS; +// ctx.beginPath(); +// ctx.moveTo(Particle.CANVAS_SIZE/2, Particle.CANVAS_SIZE/2 - height/2); +// ctx.lineTo(Particle.CANVAS_SIZE/2 - halfWidth, Particle.CANVAS_SIZE/2 + height/2); +// ctx.lineTo(Particle.CANVAS_SIZE/2 + halfWidth, Particle.CANVAS_SIZE/2 + height/2); +// ctx.closePath(); +// ctx.fill(); +// break; +// } +// }); +// +// if (!GLOBAL_STATE.particleCanvases[shape]) { +// GLOBAL_STATE.particleCanvases[shape] = {}; +// } +// GLOBAL_STATE.particleCanvases[shape].white = whiteCanvas; +// GLOBAL_STATE.particleCanvases[shape].orange = orangeCanvas; +// }); +// } +// +// constructor() { +// this.shape = Particle.SHAPES[Math.floor(Math.random() * Particle.SHAPES.length)]; +// this.angle = Math.random() * Math.PI * 2; // Initial random angle +// this.rotationSpeed = PARTICLE_CONFIG.ROTATION_SPEED; +// +// // Add random rotation and rotational velocity +// this.rotation = Math.random() * Math.PI * 2; // Random initial rotation +// this.rotationalVelocity = (Math.random() - 0.5) * PARTICLE_CONFIG.MAX_ROTATIONAL_VELOCITY; +// +// this.reset(); +// } +// +// // Generate a normally distributed random number using Box-Muller transform +// gaussianRandom(mean = 0, stdDev = 1) { +// const u1 = Math.random(); +// const u2 = Math.random(); +// const z0 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2); +// return z0 * stdDev + mean; +// } +// +// reset() { +// // Circle parameters +// const baseRadius = PARTICLE_CONFIG.BASE_RADIUS; +// const stdDev = PARTICLE_CONFIG.RADIUS_STD_DEV; +// +// // Generate random distance (but keep angle from constructor) +// const randomDistance = this.gaussianRandom(0, stdDev); +// this.radius = baseRadius + randomDistance; +// +// // Convert polar to cartesian coordinates +// this.updateOriginPosition(); +// +// // Current position +// this.x = this.originX; +// this.y = this.originY; +// +// // Velocity (starts at 0) +// this.vx = 0; +// this.vy = 0; +// +// // Physics constants +// this.damping = PARTICLE_CONFIG.DAMPING; +// this.mass = PARTICLE_CONFIG.MASS; +// this.springStrength = PARTICLE_CONFIG.SPRING_STRENGTH; +// +// // Size and opacity +// this.size = Particle.PARTICLE_RADIUS * 2; +// this.opacity = 1; +// this.opacityFactor = Math.random(); +// this.opacityBase = Math.min(0, Math.random() - 0.5); +// } +// +// updateOriginPosition() { +// const centerX = window.innerWidth / 2; +// const centerY = 180; +// this.originX = centerX + this.radius * Math.cos(this.angle); +// this.originY = centerY + this.radius * Math.sin(this.angle); +// } +// +// draw(ctx) { +// // Skip rendering if particle is outside canvas bounds +// if (this.x + Particle.CANVAS_SIZE/2 < 0 || +// this.x - Particle.CANVAS_SIZE/2 > ctx.canvas.width || +// this.y + Particle.CANVAS_SIZE/2 < 0 || +// this.y - Particle.CANVAS_SIZE/2 > ctx.canvas.height) { +// return; +// } +// +// // Calculate opacity based on distance from center +// const centerX = window.innerWidth / 2; +// const centerY = 180; +// const dx = this.x - centerX; +// const dy = this.y - centerY; +// const distance = Math.sqrt(dx * dx + dy * dy); +// +// // Fade out between 600px and 1200px from center +// const minDistance = 600; +// const maxDistance = 1200; +// this.opacity = this.opacityBase + Math.max(0, Math.min(1, 1 - (distance - minDistance) / (maxDistance - minDistance))) * this.opacityFactor; +// +// // Calculate velocity magnitude directly - simpler and more accurate +// const velocityMagnitude = Math.sqrt(this.vx * this.vx + this.vy * this.vy); +// +// // Calculate color mix based on velocity - lowered threshold for more visible effect +// const colorMix = Math.min(1, (velocityMagnitude - Particle.MIN_VELOCITY_THRESHOLD) / (Particle.MAX_VELOCITY_THRESHOLD - Particle.MIN_VELOCITY_THRESHOLD) * 0.5); +// +// // Save the current context state +// ctx.save(); +// +// // Translate to particle position and rotate +// ctx.translate(this.x, this.y); +// ctx.rotate(this.rotation); +// +// // Draw white (base) particle +// if (this.opacity > 0) { +// ctx.globalAlpha = this.opacity * Particle.BASE_OPACITY * (1 - colorMix); +// ctx.drawImage( +// GLOBAL_STATE.particleCanvases[this.shape].white, +// -Particle.CANVAS_SIZE/2, +// -Particle.CANVAS_SIZE/2 +// ); +// } +// +// // Draw orange (active) particle +// if (colorMix > 0) { +// // Ignore this.opacity since any fast particle will turn orange +// ctx.globalAlpha = colorMix; +// ctx.drawImage( +// GLOBAL_STATE.particleCanvases[this.shape].orange, +// -Particle.CANVAS_SIZE/2, +// -Particle.CANVAS_SIZE/2 +// ); +// } +// +// // Restore the context state +// ctx.restore(); +// ctx.globalAlpha = 1; +// } +// +// update(deltaTime) { +// // Update the angle +// this.angle += this.rotationSpeed * deltaTime; +// if (this.angle > Math.PI * 2) { +// this.angle -= Math.PI * 2; +// } +// +// // Update rotation +// this.rotation += this.rotationalVelocity * deltaTime; +// if (this.rotation > Math.PI * 2) { +// this.rotation -= Math.PI * 2; +// } +// +// // Update origin position based on new angle +// this.updateOriginPosition(); +// +// // Calculate spring force back to origin +// const dx = this.originX - this.x; +// const dy = this.originY - this.y; +// const springForceX = dx * this.springStrength; +// const springForceY = dy * this.springStrength; +// +// // Apply spring force (scaled by deltaTime) +// this.vx += springForceX * deltaTime; +// this.vy += springForceY * deltaTime; +// +// // Apply damping +// this.vx *= Math.pow(this.damping, deltaTime * 60); +// this.vy *= Math.pow(this.damping, deltaTime * 60); +// +// // Clamp velocity to prevent extreme speeds +// const currentSpeed = Math.sqrt(this.vx * this.vx + this.vy * this.vy); +// if (currentSpeed > 1000) { +// this.vx = (this.vx / currentSpeed) * 1000; +// this.vy = (this.vy / currentSpeed) * 1000; +// } +// +// // Update position +// this.x += this.vx; +// this.y += this.vy; +// } +// +// applyForce(fx, fy) { +// this.vx += fx; +// this.vy += fy; +// } +//} +// +//// Create the particle system and initialize everything needed +//function createParticleSystem() { +// console.log("[Particles] Creating particle system"); +// +// if (GLOBAL_STATE.active) { +// console.log("[Particles] System already active, skipping creation"); +// return; // Already active +// } +// +// // Initialize particle canvases if needed +// if (Object.keys(GLOBAL_STATE.particleCanvases).length === 0) { +// Particle.initParticleCanvases(); +// } +// +// // Create particles if needed +// if (GLOBAL_STATE.particles.length === 0) { +// console.log("[Particles] Creating particles"); +// GLOBAL_STATE.particles = Array.from({ length: PARTICLE_CONFIG.PARTICLE_COUNT }, () => new Particle()); +// } +// +// // Create wrapper element +// wrapper = document.createElement('div'); +// wrapper.setAttribute('data-particle-wrapper', 'true'); +// Object.assign(wrapper.style, { +// position: 'absolute', +// top: '0', +// left: '0', +// width: '100%', +// height: `${Particle.CONTAINER_HEIGHT}px`, +// overflow: 'hidden', +// pointerEvents: 'none', +// zIndex: '-1' +// }); +// +// // Create canvas +// canvas = document.createElement('canvas'); +// canvas.setAttribute('data-particles', 'true'); +// Object.assign(canvas.style, { +// position: 'absolute', +// top: '0', +// left: '0', +// width: '100%', +// height: `${Particle.CONTAINER_HEIGHT}px`, +// pointerEvents: 'none', +// zIndex: '-1' +// }); +// +// // Set canvas size +// canvas.width = window.innerWidth; +// canvas.height = Particle.CONTAINER_HEIGHT; +// +// // Get context +// ctx = canvas.getContext('2d'); +// +// // Add canvas to wrapper +// wrapper.appendChild(canvas); +// +// // Add wrapper to body +// if (document.body.firstChild) { +// document.body.insertBefore(wrapper, document.body.firstChild); +// } else { +// document.body.appendChild(wrapper); +// } +// +// // Set up mouse tracking +// mouseMoveListener = (e) => { +// const currentTime = performance.now(); +// const deltaTime = (currentTime - GLOBAL_STATE.lastMouseTime) / 1000; +// +// // Use page coordinates for absolute positioning +// GLOBAL_STATE.mouseX = e.pageX; +// GLOBAL_STATE.mouseY = e.pageY; +// +// if (deltaTime > 0) { +// GLOBAL_STATE.mouseVX = (GLOBAL_STATE.mouseX - GLOBAL_STATE.lastMouseX) / deltaTime; +// GLOBAL_STATE.mouseVY = (GLOBAL_STATE.mouseY - GLOBAL_STATE.lastMouseY) / deltaTime; +// +// const maxVelocity = PARTICLE_CONFIG.MAX_MOUSE_VELOCITY; +// GLOBAL_STATE.mouseVX = Math.max(Math.min(GLOBAL_STATE.mouseVX, maxVelocity), -maxVelocity); +// GLOBAL_STATE.mouseVY = Math.max(Math.min(GLOBAL_STATE.mouseVY, maxVelocity), -maxVelocity); +// } +// +// GLOBAL_STATE.lastMouseX = GLOBAL_STATE.mouseX; +// GLOBAL_STATE.lastMouseY = GLOBAL_STATE.mouseY; +// GLOBAL_STATE.lastMouseTime = currentTime; +// }; +// +// document.addEventListener('mousemove', mouseMoveListener); +// +// // Handle resize +// resizeListener = () => { +// if (canvas) { +// canvas.width = window.innerWidth; +// canvas.height = Particle.CONTAINER_HEIGHT; +// GLOBAL_STATE.particles.forEach(particle => particle.reset()); +// } +// }; +// +// window.addEventListener('resize', resizeListener); +// +// // Initialize timing +// GLOBAL_STATE.lastFrameTime = performance.now(); +// GLOBAL_STATE.lastMouseTime = performance.now(); +// +// // Start animation +// startAnimation(); +// +// // Mark as active +// GLOBAL_STATE.active = true; +//} +// +//// Start the animation loop +//function startAnimation() { +// if (GLOBAL_STATE.animationFrameId !== null) { +// // Animation already running +// return; +// } +// +// console.log("[Particles] Starting animation"); +// +// const TARGET_FPS = PARTICLE_CONFIG.TARGET_FPS; +// const FRAME_TIME = 1000 / TARGET_FPS; +// const FORCE_RADIUS = PARTICLE_CONFIG.FORCE_RADIUS; +// +// function animate() { +// const currentTime = performance.now(); +// const timeSinceLastFrame = currentTime - GLOBAL_STATE.lastFrameTime; +// +// // Only render if enough time has passed for next frame +// if (timeSinceLastFrame >= FRAME_TIME) { +// const deltaTime = Math.min(timeSinceLastFrame / 1000, PARTICLE_CONFIG.MAX_FRAME_TIME); +// GLOBAL_STATE.lastFrameTime = currentTime; +// +// // Update all particles +// GLOBAL_STATE.particles.forEach(particle => { +// const dx = particle.x - GLOBAL_STATE.mouseX; +// const dy = particle.y - GLOBAL_STATE.mouseY; +// const distance = Math.sqrt(dx * dx + dy * dy); +// +// if (distance < FORCE_RADIUS) { +// // Calculate mouse velocity magnitude +// const mouseSpeed = Math.sqrt(GLOBAL_STATE.mouseVX * GLOBAL_STATE.mouseVX + GLOBAL_STATE.mouseVY * GLOBAL_STATE.mouseVY); +// // Normalize mouse velocity to 0-1 range +// const normalizedSpeed = Math.min(mouseSpeed / PARTICLE_CONFIG.MAX_VELOCITY, 1); +// +// // Calculate base force factor with smoother distance falloff +// const forceFactor = Math.pow(1 - distance / FORCE_RADIUS, 1.5) * +// normalizedSpeed * +// PARTICLE_CONFIG.BASE_PUSH_FORCE * +// PARTICLE_CONFIG.MOVEMENT_FORCE_MULTIPLIER * +// deltaTime * 60; +// +// // Calculate repulsion direction (away from mouse) +// const repelDirX = dx / distance; +// const repelDirY = dy / distance; +// +// // Get normalized mouse movement direction +// const mvx = GLOBAL_STATE.mouseVX / (mouseSpeed || 1); +// const mvy = GLOBAL_STATE.mouseVY / (mouseSpeed || 1); +// +// // Calculate movement influence based on distance +// const movementInfluence = Math.pow(1 - distance / FORCE_RADIUS, 1.2); +// +// // Combine forces with reduced repulsion and increased movement +// const repelForce = PARTICLE_CONFIG.REPEL_FORCE_RATIO * mouseSpeed; +// const moveForce = mouseSpeed * PARTICLE_CONFIG.MOVEMENT_FORCE_RATIO; +// +// // More emphasis on movement direction, less on repulsion +// const fx = (repelDirX * repelForce * 0.5 + mvx * moveForce) * forceFactor * movementInfluence; +// const fy = (repelDirY * repelForce * 0.5 + mvy * moveForce) * forceFactor * movementInfluence; +// +// particle.applyForce(fx, fy); +// } +// +// particle.update(deltaTime); +// }); +// +// // Make sure canvas and context exist before drawing +// if (canvas && ctx) { +// // Clear canvas +// ctx.clearRect(0, 0, canvas.width, canvas.height); +// +// // Draw particles +// GLOBAL_STATE.particles.forEach(particle => { +// particle.draw(ctx); +// }); +// } +// } +// +// // Continue animation +// GLOBAL_STATE.animationFrameId = requestAnimationFrame(animate); +// } +// +// GLOBAL_STATE.animationFrameId = requestAnimationFrame(animate); +//} +// +//// Completely destroy the particle system +//function destroyParticleSystem() { +// console.log("[Particles] Destroying particle system"); +// +// // Cancel animation frame +// if (GLOBAL_STATE.animationFrameId !== null) { +// cancelAnimationFrame(GLOBAL_STATE.animationFrameId); +// GLOBAL_STATE.animationFrameId = null; +// } +// +// // Remove event listeners +// if (mouseMoveListener) { +// document.removeEventListener('mousemove', mouseMoveListener); +// mouseMoveListener = null; +// } +// +// if (resizeListener) { +// window.removeEventListener('resize', resizeListener); +// resizeListener = null; +// } +// +// // Remove DOM elements +// if (wrapper && document.body.contains(wrapper)) { +// wrapper.remove(); +// } +// +// // Clear references +// canvas = null; +// ctx = null; +// wrapper = null; +// +// // Mark as inactive +// GLOBAL_STATE.active = false; +//} +// +//// Check if the page should show particles (.landing-root exists) +//function shouldShowParticles() { +// return document.querySelector('.landing-root') !== null; +//} +// +//// Handle mutation observer updates +//function observerInitialize() { +// // Check if we should show or hide particles +// if (shouldShowParticles()) { +// if (!GLOBAL_STATE.active) { +// console.log("[Particles] Landing page detected - creating particles"); +// createParticleSystem(); +// } +// } else { +// if (GLOBAL_STATE.active) { +// console.log("[Particles] Not a landing page - destroying particles"); +// destroyParticleSystem(); +// } +// } +// +// // Mark all containers as initialized +// document.querySelectorAll('.particle-container:not([data-particle-initialized])').forEach(container => { +// container.setAttribute('data-particle-initialized', 'true'); +// }); +//} +// +//// Compatibility function for observer-manager.js +//function initializeAllParticles() { +// observerInitialize(); +//} +// +//// Initialize after a small delay +////setTimeout(observerInitialize, 50); diff --git a/docs/styles/style.css b/docs/styles/style.css index 6b91a1e39..43170b5a0 100644 --- a/docs/styles/style.css +++ b/docs/styles/style.css @@ -1,4 +1,4 @@ -/* ActorCore custom styles */ +/* RivetKit custom styles */ .landing-root { text-align: center; @@ -18,32 +18,6 @@ margin: 0 auto; } -/* At 1025px and below, the nav bar starts overlapping with content - Add margin-top to offset the nav bar height */ -@media (max-width: 1025px) { - .landing-root { - margin-top: 120px; - } -} - -@media (max-width: 1024px) { - .landing-root { - padding: 0 40px; - } -} - -@media (max-width: 768px) { - .landing-root { - padding: 0 32px; - } -} - -@media (max-width: 480px) { - .landing-root { - padding: 0 20px; - } -} - .landing-section-heading { font-size: clamp(32px, 4vw, 48px); line-height: 1.2; @@ -73,12 +47,11 @@ } .landing-hero { - padding-top: 120px; - padding-bottom: 80px; display: flex; flex-direction: column; align-items: center; - justify-content: flex-start; + justify-content: center; + min-height: calc(100vh - 200px); } @media (max-width: 768px) { @@ -314,6 +287,165 @@ margin-right: 12px; } +/* Libraries grid */ +.libraries-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 24px; + margin-top: 48px; +} + +.library-box { + position: relative; + background: rgb(10, 10, 10); + border-radius: 1rem; + overflow: hidden; + border: 1px solid rgba(255, 255, 255, 0.1); + margin: 0.5rem 0; + width: 100%; + aspect-ratio: 0.75 / 1; /* Taller than wide */ + padding: 1.75rem; + display: flex; + flex-direction: column; + justify-content: space-between; + text-decoration: none; + transition: all 0.2s ease; +} + +.library-box-purple { + border-color: rgba(255, 255, 255, 0.1); +} + +.library-box-blue { + border-color: rgba(255, 255, 255, 0.1); +} + +.library-box-orange { + border-color: rgba(255, 255, 255, 0.1); +} + +.library-box-green { + border-color: rgba(255, 255, 255, 0.1); +} + +.library-box-purple:hover, .library-box-purple:focus { + background: rgba(168, 85, 247, 0.07); + border-color: rgba(168, 85, 247, 0.5); +} + +.library-box-blue:hover, .library-box-blue:focus { + background: rgba(59, 130, 246, 0.07); + border-color: rgba(59, 130, 246, 0.5); +} + +.library-box-orange:hover, .library-box-orange:focus { + background: rgba(255, 79, 0, 0.07); + border-color: rgba(255, 79, 0, 0.5); +} + +.library-box-green:hover, .library-box-green:focus { + background: rgba(16, 185, 129, 0.07); + border-color: rgba(16, 185, 129, 0.5); +} + +.library-content { + display: flex; + flex-direction: column; + gap: 20px; + flex: 1; +} + +.library-title { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 16px; +} + +.library-icon { + flex-shrink: 0; +} + +.library-text { + text-align: left; + flex: 1; +} + +.library-box h3 { + font-size: 1.5rem; + font-weight: 600; + margin: 0; + color: white; + text-align: left; +} + +.library-description { + margin-top: 0; + flex: 1; +} + +.library-description p { + font-size: 0.9rem; + line-height: 1.5rem; + color: rgba(255, 255, 255, 0.85); + margin: 0; + text-align: left; +} + +.library-description p + p { + margin-top: 0.75rem; +} + +.library-replaces { + font-size: 0.8rem !important; + color: rgba(255, 255, 255, 0.6) !important; +} + +.library-description .highlight { + color: rgba(255, 255, 255, 0.9); + font-weight: 500; + transition: color 0.2s ease; +} + +/* Remove hover color change for highlights */ + +.library-arrow { + align-self: flex-end; + margin-top: 16px; + opacity: 0; + transform: translateX(-10px); + transition: opacity 0.3s ease, transform 0.3s ease; +} + +.library-box:hover .library-arrow { + opacity: 1; + transform: translateX(0); +} + +@media (max-width: 1200px) { + .libraries-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 767px) { + .libraries-grid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 600px) { + .libraries-grid { + grid-template-columns: 1fr; + } + + .library-box { + aspect-ratio: auto; + min-height: 180px; + padding: 1.5rem; + } +} + /* Feature icons */ .feature-icon { display: inline-flex; @@ -647,6 +779,69 @@ text-align: center; } +/* Platform icons row */ +.platform-icons-container { + margin: 24px 0; + text-align: center; +} + +.platform-icons-label { + font-size: 14px; + color: #888; + margin-bottom: 16px; +} + +.platform-icons-row { + display: flex; + flex-wrap: wrap; + justify-content: center; +} + +.platform-icon-wrapper { + position: relative; + cursor: pointer; + padding: 10px; + text-decoration: none; + color: inherit; +} + +.platform-icon { + width: 36px; + height: 36px; + filter: grayscale(100%) brightness(70%); + opacity: 0.7; + transition: all 0.25s ease; +} + +.platform-icon-wrapper:hover .platform-icon { + filter: grayscale(0%) brightness(100%); + opacity: 1; + transform: scale(1.15); +} + +.platform-icon-tooltip { + position: absolute; + bottom: -28px; + left: 50%; + transform: translateX(-50%); + background: rgba(0, 0, 0, 0.8); + color: white; + padding: 4px 8px; + border-radius: 4px; + font-size: 12px; + white-space: nowrap; + opacity: 0; + visibility: hidden; + transition: all 0.2s ease; + pointer-events: none; + z-index: 10; +} + +.platform-icon-wrapper:hover .platform-icon-tooltip { + opacity: 1; + visibility: visible; +} + .comparison-table-footnotes { margin-top: 16px; text-align: center; diff --git a/docs/support/enterprise.mdx b/docs/support/enterprise.mdx index 0a106e248..1291bee15 100644 --- a/docs/support/enterprise.mdx +++ b/docs/support/enterprise.mdx @@ -2,7 +2,7 @@ title: "Enterprise" --- -ActorCore is developed and maintained by [Rivet](https://rivet.gg), offering comprehensive enterprise support and deployment solutions tailored to your organization's needs. +RivetKit is developed and maintained by [Rivet](https://rivet.gg), offering comprehensive enterprise support and deployment solutions tailored to your organization's needs. Our enterprise services include: diff --git a/examples/chat-room-python/actors/app.ts b/examples/chat-room-python/actors/app.ts index 21e9577b5..589544a7b 100644 --- a/examples/chat-room-python/actors/app.ts +++ b/examples/chat-room-python/actors/app.ts @@ -1,4 +1,4 @@ -import { actor, setup } from "actor-core"; +import { actor, setup } from "@rivetkit/actor"; // state managed by the actor export interface State { diff --git a/examples/chat-room-python/package.json b/examples/chat-room-python/package.json index 4ef6f91f3..af699b5d7 100644 --- a/examples/chat-room-python/package.json +++ b/examples/chat-room-python/package.json @@ -4,14 +4,13 @@ "private": true, "type": "module", "scripts": { - "dev": "npx @actor-core/cli@latest dev actors/app.ts", + "dev": "npx rivetkit/cli@latest dev actors/app.ts", "check-types": "tsc --noEmit", "pytest": "pytest tests/test_chat_room.py -v" }, "devDependencies": { - "@actor-core/cli": "workspace:*", + "@rivetkit/actor": "workspace:*", "@types/node": "^22.13.9", - "actor-core": "workspace:*", "tsx": "^3.12.7", "typescript": "^5.5.2" }, diff --git a/examples/chat-room/actors/app.ts b/examples/chat-room/actors/app.ts index 21e9577b5..589544a7b 100644 --- a/examples/chat-room/actors/app.ts +++ b/examples/chat-room/actors/app.ts @@ -1,4 +1,4 @@ -import { actor, setup } from "actor-core"; +import { actor, setup } from "@rivetkit/actor"; // state managed by the actor export interface State { diff --git a/examples/chat-room/package.json b/examples/chat-room/package.json index b5888becd..1cbcc9cb5 100644 --- a/examples/chat-room/package.json +++ b/examples/chat-room/package.json @@ -4,15 +4,14 @@ "private": true, "type": "module", "scripts": { - "dev": "npx @actor-core/cli@latest dev actors/app.ts", + "dev": "npx rivetkit/cli@latest dev actors/app.ts", "check-types": "tsc --noEmit", "test": "vitest run" }, "devDependencies": { - "@actor-core/cli": "workspace:*", + "@rivetkit/actor": "workspace:*", "@types/node": "^22.13.9", "@types/prompts": "^2", - "actor-core": "workspace:*", "prompts": "^2.4.2", "tsx": "^3.12.7", "typescript": "^5.5.2", diff --git a/examples/chat-room/scripts/cli.ts b/examples/chat-room/scripts/cli.ts index bf2479f6b..eb0a93858 100644 --- a/examples/chat-room/scripts/cli.ts +++ b/examples/chat-room/scripts/cli.ts @@ -1,4 +1,4 @@ -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "../actors/app"; import prompts from "prompts"; diff --git a/examples/chat-room/scripts/connect.ts b/examples/chat-room/scripts/connect.ts index c6808a219..5720e685c 100644 --- a/examples/chat-room/scripts/connect.ts +++ b/examples/chat-room/scripts/connect.ts @@ -1,5 +1,5 @@ /// -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "../actors/app"; async function main() { diff --git a/examples/chat-room/tests/chat-room.test.ts b/examples/chat-room/tests/chat-room.test.ts index 11788eb68..70b1d9674 100644 --- a/examples/chat-room/tests/chat-room.test.ts +++ b/examples/chat-room/tests/chat-room.test.ts @@ -1,5 +1,5 @@ import { test, expect } from "vitest"; -import { setupTest } from "actor-core/test"; +import { setupTest } from "@rivetkit/actor/test"; import { app } from "../actors/app"; test("chat room should handle messages", async (test) => { diff --git a/examples/counter/actors/app.ts b/examples/counter/actors/app.ts index 49dc5a07e..12b52fc44 100644 --- a/examples/counter/actors/app.ts +++ b/examples/counter/actors/app.ts @@ -1,4 +1,4 @@ -import { actor, setup } from "actor-core"; +import { actor, setup } from "@rivetkit/actor"; const counter = actor({ state: { count: 0 }, diff --git a/examples/counter/package.json b/examples/counter/package.json index 48f37e00e..9cb152723 100644 --- a/examples/counter/package.json +++ b/examples/counter/package.json @@ -4,14 +4,13 @@ "private": true, "type": "module", "scripts": { - "dev": "npx @actor-core/cli@latest dev actors/app.ts", + "dev": "npx rivetkit/cli@latest dev actors/app.ts", "check-types": "tsc --noEmit", "test": "vitest run" }, "devDependencies": { - "@actor-core/cli": "workspace:*", + "@rivetkit/actor": "workspace:*", "@types/node": "^22.13.9", - "actor-core": "workspace:*", "tsx": "^3.12.7", "typescript": "^5.7.3", "vitest": "^3.1.1" diff --git a/examples/counter/scripts/connect.ts b/examples/counter/scripts/connect.ts index b4f83b252..108527bec 100644 --- a/examples/counter/scripts/connect.ts +++ b/examples/counter/scripts/connect.ts @@ -1,5 +1,5 @@ /// -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import type { App } from "../actors/app"; async function main() { diff --git a/examples/counter/tests/counter.test.ts b/examples/counter/tests/counter.test.ts index 5a58d57b4..7a4924836 100644 --- a/examples/counter/tests/counter.test.ts +++ b/examples/counter/tests/counter.test.ts @@ -1,5 +1,5 @@ import { test, expect } from "vitest"; -import { setupTest } from "actor-core/test"; +import { setupTest } from "@rivetkit/actor/test"; import { app } from "../actors/app"; test("it should count", async (test) => { diff --git a/examples/linear-coding-agent/README.md b/examples/linear-coding-agent/README.md index 0d93b1b3d..682239971 100644 --- a/examples/linear-coding-agent/README.md +++ b/examples/linear-coding-agent/README.md @@ -70,7 +70,7 @@ This starts the ActorCore server that hosts the coding agent: ``` npm run dev # Or using the ActorCore CLI -npx @actor-core/cli dev src/actors/app.ts +npx rivetkit/cli dev src/actors/app.ts ``` ##### Running the Webhook Server (for Linear integration) diff --git a/examples/linear-coding-agent/package.json b/examples/linear-coding-agent/package.json index 40ee5f6ec..8ed46b304 100644 --- a/examples/linear-coding-agent/package.json +++ b/examples/linear-coding-agent/package.json @@ -5,18 +5,17 @@ "type": "module", "scripts": { "dev": "concurrently --raw \"yarn dev:actors\" \"yarn dev:server\" \"yarn dev:ngrok\"", - "dev:actors": "npx @actor-core/cli@latest dev src/actors/app.ts", + "dev:actors": "npx rivetkit/cli@latest dev src/actors/app.ts", "dev:server": "tsx --watch src/server/index.ts", "dev:ngrok": "ngrok http 3000", "check-types": "tsc --noEmit" }, "devDependencies": { - "@actor-core/cli": "workspace:*", + "@rivetkit/actor": "workspace:*", "@types/dotenv": "^8.2.3", "@types/express": "^5", "@types/node": "^22.13.9", "@types/prompts": "^2", - "actor-core": "workspace:*", "concurrently": "^9.1.2", "prompts": "^2.4.2", "tsx": "^3.12.7", diff --git a/examples/linear-coding-agent/src/actors/app.ts b/examples/linear-coding-agent/src/actors/app.ts index e4671be18..0120ac48a 100644 --- a/examples/linear-coding-agent/src/actors/app.ts +++ b/examples/linear-coding-agent/src/actors/app.ts @@ -1,4 +1,4 @@ -import { setup } from "actor-core"; +import { setup } from "@rivetkit/actor"; import dotenv from "dotenv"; import { codingAgent } from "./coding-agent/mod"; diff --git a/examples/linear-coding-agent/src/actors/coding-agent/mod.ts b/examples/linear-coding-agent/src/actors/coding-agent/mod.ts index 33970ba75..09fbfa7c4 100644 --- a/examples/linear-coding-agent/src/actors/coding-agent/mod.ts +++ b/examples/linear-coding-agent/src/actors/coding-agent/mod.ts @@ -1,4 +1,4 @@ -import { type ActionContextOf, type ActorContextOf, actor } from "actor-core"; +import { type ActionContextOf, type ActorContextOf, actor } from "@rivetkit/actor"; import type { CodingAgentState, CodingAgentVars, diff --git a/examples/linear-coding-agent/src/server/index.ts b/examples/linear-coding-agent/src/server/index.ts index 86b73ec4a..086915f6e 100644 --- a/examples/linear-coding-agent/src/server/index.ts +++ b/examples/linear-coding-agent/src/server/index.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import { serve } from "@hono/node-server"; import dotenv from "dotenv"; -import { createClient } from "actor-core/client"; +import { createClient } from "@rivetkit/actor/client"; import { app } from "../actors/app"; import type { App } from "../actors/app"; import type { LinearWebhookEvent } from "../types"; diff --git a/examples/resend-streaks/actors/app.ts b/examples/resend-streaks/actors/app.ts index 13feb34c2..1aea7ee3c 100644 --- a/examples/resend-streaks/actors/app.ts +++ b/examples/resend-streaks/actors/app.ts @@ -1,5 +1,5 @@ import { TZDate } from "@date-fns/tz"; -import { UserError, actor, setup } from "actor-core"; +import { UserError, actor, setup } from "@rivetkit/actor"; import { addDays, set } from "date-fns"; import { Resend } from "resend"; diff --git a/examples/resend-streaks/package.json b/examples/resend-streaks/package.json index 6f7b9489e..19af4f080 100644 --- a/examples/resend-streaks/package.json +++ b/examples/resend-streaks/package.json @@ -4,14 +4,13 @@ "private": true, "type": "module", "scripts": { - "dev": "npx @actor-core/cli@latest dev actors/app.ts", + "dev": "npx rivetkit/cli@latest dev actors/app.ts", "check-types": "tsc --noEmit", "test": "vitest run" }, "devDependencies": { - "@actor-core/cli": "workspace:*", + "@rivetkit/actor": "workspace:*", "@types/node": "^22.13.9", - "actor-core": "workspace:*", "tsx": "^3.12.7", "typescript": "^5.7.3", "vitest": "^3.1.1" diff --git a/examples/resend-streaks/tests/user.test.ts b/examples/resend-streaks/tests/user.test.ts index 2736133bb..3e1bd0c37 100644 --- a/examples/resend-streaks/tests/user.test.ts +++ b/examples/resend-streaks/tests/user.test.ts @@ -1,5 +1,5 @@ import { test, expect, vi, beforeEach } from "vitest"; -import { setupTest } from "actor-core/test"; +import { setupTest } from "@rivetkit/actor/test"; import { app } from "../actors/app"; // Create mock for send method diff --git a/examples/snippets/ai-agent/App.tsx b/examples/snippets/ai-agent/App.tsx index 93fc5106e..24b4a40cf 100644 --- a/examples/snippets/ai-agent/App.tsx +++ b/examples/snippets/ai-agent/App.tsx @@ -1,5 +1,5 @@ -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactActorCore } from "@rivetkit/react"; import { useState, useEffect } from "react"; import type { App } from "../actors/app"; import type { Message } from "./actor"; @@ -82,4 +82,4 @@ export function AIAssistant() {
      ); -} \ No newline at end of file +} diff --git a/examples/snippets/ai-agent/actor-json.ts b/examples/snippets/ai-agent/actor-json.ts index b7ce7d606..2588cb84e 100644 --- a/examples/snippets/ai-agent/actor-json.ts +++ b/examples/snippets/ai-agent/actor-json.ts @@ -1,4 +1,4 @@ -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; import { generateText, tool } from "ai"; import { openai } from "@ai-sdk/openai"; import { getWeather } from "./my-utils"; @@ -62,4 +62,4 @@ const aiAgent = actor({ } }); -export default aiAgent; \ No newline at end of file +export default aiAgent; diff --git a/examples/snippets/ai-agent/actor-sqlite.ts b/examples/snippets/ai-agent/actor-sqlite.ts index b61b19eb1..2877282cf 100644 --- a/examples/snippets/ai-agent/actor-sqlite.ts +++ b/examples/snippets/ai-agent/actor-sqlite.ts @@ -1,5 +1,5 @@ -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { generateText, tool } from "ai"; import { openai } from "@ai-sdk/openai"; import { getWeather } from "./my-utils"; @@ -83,4 +83,4 @@ const aiAgent = actor({ } }); -export default aiAgent; \ No newline at end of file +export default aiAgent; diff --git a/examples/snippets/chat-room/App.tsx b/examples/snippets/chat-room/App.tsx index 6b7e631f2..41be84e12 100644 --- a/examples/snippets/chat-room/App.tsx +++ b/examples/snippets/chat-room/App.tsx @@ -1,5 +1,5 @@ -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactActorCore } from "@rivetkit/react"; import { useState, useEffect } from "react"; import type { App } from "../actors/app"; import type { Message } from "./actor"; @@ -68,4 +68,4 @@ export function ChatRoom({ roomId = "general" }) {
      ); -} \ No newline at end of file +} diff --git a/examples/snippets/chat-room/actor-json.ts b/examples/snippets/chat-room/actor-json.ts index 9cb037c7b..8b279f4f4 100644 --- a/examples/snippets/chat-room/actor-json.ts +++ b/examples/snippets/chat-room/actor-json.ts @@ -1,4 +1,4 @@ -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; export type Message = { sender: string; text: string; timestamp: number; } diff --git a/examples/snippets/chat-room/actor-sqlite.ts b/examples/snippets/chat-room/actor-sqlite.ts index fe244d3cd..de9f9281a 100644 --- a/examples/snippets/chat-room/actor-sqlite.ts +++ b/examples/snippets/chat-room/actor-sqlite.ts @@ -1,5 +1,5 @@ -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { messages } from "./schema"; export type Message = { sender: string; text: string; timestamp: number; } @@ -37,4 +37,4 @@ const chatRoom = actor({ } }); -export default chatRoom; \ No newline at end of file +export default chatRoom; diff --git a/examples/snippets/crdt/App.tsx b/examples/snippets/crdt/App.tsx index 1692fb23f..e6c602ae3 100644 --- a/examples/snippets/crdt/App.tsx +++ b/examples/snippets/crdt/App.tsx @@ -1,5 +1,5 @@ -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactActorCore } from "@rivetkit/react"; import { useState, useEffect, useRef } from "react"; import * as Y from 'yjs'; import { applyUpdate, encodeStateAsUpdate } from 'yjs'; @@ -175,4 +175,4 @@ function bufferToBase64(buffer: Uint8Array): string { binary += String.fromCharCode(buffer[i]); } return btoa(binary); -} \ No newline at end of file +} diff --git a/examples/snippets/crdt/actor-json.ts b/examples/snippets/crdt/actor-json.ts index 049368208..0ec15a6aa 100644 --- a/examples/snippets/crdt/actor-json.ts +++ b/examples/snippets/crdt/actor-json.ts @@ -1,4 +1,4 @@ -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; import * as Y from 'yjs'; import { encodeStateAsUpdate, applyUpdate } from 'yjs'; @@ -69,4 +69,4 @@ function bufferToBase64(buffer: Uint8Array): string { return btoa(binary); } -export default yjsDocument; \ No newline at end of file +export default yjsDocument; diff --git a/examples/snippets/crdt/actor-sqlite.ts b/examples/snippets/crdt/actor-sqlite.ts index 366a70b07..0a6c20463 100644 --- a/examples/snippets/crdt/actor-sqlite.ts +++ b/examples/snippets/crdt/actor-sqlite.ts @@ -1,5 +1,5 @@ -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import * as Y from 'yjs'; import { encodeStateAsUpdate, applyUpdate } from 'yjs'; import { documents } from "./schema"; @@ -93,4 +93,4 @@ function bufferToBase64(buffer: Uint8Array): string { return btoa(binary); } -export default yjsDocument; \ No newline at end of file +export default yjsDocument; diff --git a/examples/snippets/database/App.tsx b/examples/snippets/database/App.tsx index 03bd364c2..a560ef7ac 100644 --- a/examples/snippets/database/App.tsx +++ b/examples/snippets/database/App.tsx @@ -1,5 +1,5 @@ -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactActorCore } from "@rivetkit/react"; import { useState, useEffect } from "react"; const client = createClient("http://localhost:6420"); @@ -74,4 +74,4 @@ export function NotesApp({ userId }: { userId: string }) {
      ); -} \ No newline at end of file +} diff --git a/examples/snippets/database/actor-json.ts b/examples/snippets/database/actor-json.ts index 25a6258c7..3fe717717 100644 --- a/examples/snippets/database/actor-json.ts +++ b/examples/snippets/database/actor-json.ts @@ -1,4 +1,4 @@ -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; import { authenticate } from "./my-utils"; export type Note = { id: string; content: string; updatedAt: number }; @@ -56,4 +56,4 @@ const notes = actor({ } }); -export default notes; \ No newline at end of file +export default notes; diff --git a/examples/snippets/database/actor-sqlite.ts b/examples/snippets/database/actor-sqlite.ts index e0074963c..d316f2bfa 100644 --- a/examples/snippets/database/actor-sqlite.ts +++ b/examples/snippets/database/actor-sqlite.ts @@ -1,5 +1,5 @@ -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { notes } from "./schema"; import { authenticate } from "./my-utils"; @@ -82,4 +82,4 @@ const userNotes = actor({ } }); -export default userNotes; \ No newline at end of file +export default userNotes; diff --git a/examples/snippets/document/App.tsx b/examples/snippets/document/App.tsx index ef66ffbde..839a701b4 100644 --- a/examples/snippets/document/App.tsx +++ b/examples/snippets/document/App.tsx @@ -1,5 +1,5 @@ -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactActorCore } from "@rivetkit/react"; import { useState, useEffect } from "react"; import type { App } from "../actors/app"; @@ -88,4 +88,4 @@ export function DocumentEditor() {
      ); -} \ No newline at end of file +} diff --git a/examples/snippets/document/actor-json.ts b/examples/snippets/document/actor-json.ts index 039d73a78..928835d46 100644 --- a/examples/snippets/document/actor-json.ts +++ b/examples/snippets/document/actor-json.ts @@ -1,4 +1,4 @@ -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; export type Cursor = { x: number, y: number, userId: string }; @@ -40,4 +40,4 @@ const document = actor({ } }); -export default document; \ No newline at end of file +export default document; diff --git a/examples/snippets/document/actor-sqlite.ts b/examples/snippets/document/actor-sqlite.ts index dc7c73a0e..2c34de513 100644 --- a/examples/snippets/document/actor-sqlite.ts +++ b/examples/snippets/document/actor-sqlite.ts @@ -1,5 +1,5 @@ -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { documents, cursors } from "./schema"; export type Cursor = { x: number, y: number, userId: string }; @@ -84,4 +84,4 @@ const document = actor({ } }); -export default document; \ No newline at end of file +export default document; diff --git a/examples/snippets/game/App.tsx b/examples/snippets/game/App.tsx index 4b6265542..28ef6e9c0 100644 --- a/examples/snippets/game/App.tsx +++ b/examples/snippets/game/App.tsx @@ -1,5 +1,5 @@ -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactActorCore } from "@rivetkit/react"; import { useState, useEffect, useRef } from "react"; import type { Player } from "./actor"; @@ -88,4 +88,4 @@ export function MultiplayerGame() {

      Move: WASD or Arrow Keys

      ); -} \ No newline at end of file +} diff --git a/examples/snippets/game/actor-json.ts b/examples/snippets/game/actor-json.ts index fd66fc744..088f20c42 100644 --- a/examples/snippets/game/actor-json.ts +++ b/examples/snippets/game/actor-json.ts @@ -1,4 +1,4 @@ -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; export type Position = { x: number; y: number }; export type Input = { x: number; y: number }; @@ -63,4 +63,4 @@ const gameRoom = actor({ } }); -export default gameRoom; \ No newline at end of file +export default gameRoom; diff --git a/examples/snippets/game/actor-sqlite.ts b/examples/snippets/game/actor-sqlite.ts index 5c178507f..b58a4f88d 100644 --- a/examples/snippets/game/actor-sqlite.ts +++ b/examples/snippets/game/actor-sqlite.ts @@ -1,5 +1,5 @@ -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { players, gameSettings } from "./schema"; export type Position = { x: number; y: number }; @@ -166,4 +166,4 @@ const gameRoom = actor({ } }); -export default gameRoom; \ No newline at end of file +export default gameRoom; diff --git a/examples/snippets/rate/App.tsx b/examples/snippets/rate/App.tsx index 2962ed5b0..19d49ef59 100644 --- a/examples/snippets/rate/App.tsx +++ b/examples/snippets/rate/App.tsx @@ -1,5 +1,5 @@ -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactActorCore } from "@rivetkit/react"; import { useState } from "react"; import type { App } from "../actors/app"; @@ -38,4 +38,4 @@ export function RateLimiter() { )}
      ); -} \ No newline at end of file +} diff --git a/examples/snippets/rate/actor-json.ts b/examples/snippets/rate/actor-json.ts index acdba6d74..41570cfb8 100644 --- a/examples/snippets/rate/actor-json.ts +++ b/examples/snippets/rate/actor-json.ts @@ -1,4 +1,4 @@ -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; // Simple rate limiter - allows 5 requests per minute const rateLimiter = actor({ @@ -35,4 +35,4 @@ const rateLimiter = actor({ } }); -export default rateLimiter; \ No newline at end of file +export default rateLimiter; diff --git a/examples/snippets/rate/actor-sqlite.ts b/examples/snippets/rate/actor-sqlite.ts index 03d29ec6c..374571216 100644 --- a/examples/snippets/rate/actor-sqlite.ts +++ b/examples/snippets/rate/actor-sqlite.ts @@ -1,5 +1,5 @@ -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { limiters } from "./schema"; // Simple rate limiter - allows 5 requests per minute @@ -64,4 +64,4 @@ const rateLimiter = actor({ } }); -export default rateLimiter; \ No newline at end of file +export default rateLimiter; diff --git a/examples/snippets/stream/App.tsx b/examples/snippets/stream/App.tsx index 962fed488..98a947dab 100644 --- a/examples/snippets/stream/App.tsx +++ b/examples/snippets/stream/App.tsx @@ -1,5 +1,5 @@ -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactActorCore } from "@rivetkit/react"; import { useState, useEffect } from "react"; import type { App } from "../actors/app"; import type { StreamState } from "./actor"; // Import shared types from actor @@ -50,4 +50,4 @@ export function StreamExample() {
      ); -} \ No newline at end of file +} diff --git a/examples/snippets/stream/actor-json.ts b/examples/snippets/stream/actor-json.ts index 60e1719e7..63bfb3526 100644 --- a/examples/snippets/stream/actor-json.ts +++ b/examples/snippets/stream/actor-json.ts @@ -1,4 +1,4 @@ -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; export type StreamState = { topValues: number[]; @@ -36,4 +36,4 @@ const streamProcessor = actor({ } }); -export default streamProcessor; \ No newline at end of file +export default streamProcessor; diff --git a/examples/snippets/stream/actor-sqlite.ts b/examples/snippets/stream/actor-sqlite.ts index 4a9874194..31356f685 100644 --- a/examples/snippets/stream/actor-sqlite.ts +++ b/examples/snippets/stream/actor-sqlite.ts @@ -1,5 +1,5 @@ -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { streams, streamValues } from "./schema"; export type StreamState = { topValues: number[]; }; @@ -53,4 +53,4 @@ const streamProcessor = actor({ } }); -export default streamProcessor; \ No newline at end of file +export default streamProcessor; diff --git a/examples/snippets/sync/App.tsx b/examples/snippets/sync/App.tsx index df2316e7d..4bac5c41b 100644 --- a/examples/snippets/sync/App.tsx +++ b/examples/snippets/sync/App.tsx @@ -1,5 +1,5 @@ -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactActorCore } from "@rivetkit/react"; import { useState, useEffect, useRef } from "react"; import type { Contact } from "./actor"; @@ -207,4 +207,4 @@ export function ContactsApp() {
      ); -} \ No newline at end of file +} diff --git a/examples/snippets/sync/actor-json.ts b/examples/snippets/sync/actor-json.ts index d78e09f49..a723d2c24 100644 --- a/examples/snippets/sync/actor-json.ts +++ b/examples/snippets/sync/actor-json.ts @@ -1,4 +1,4 @@ -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; export type Contact = { id: string; name: string; email: string; phone: string; updatedAt: number; } @@ -44,4 +44,4 @@ const contacts = actor({ } }); -export default contacts; \ No newline at end of file +export default contacts; diff --git a/examples/snippets/sync/actor-sqlite.ts b/examples/snippets/sync/actor-sqlite.ts index 234d1d85a..638ffeae0 100644 --- a/examples/snippets/sync/actor-sqlite.ts +++ b/examples/snippets/sync/actor-sqlite.ts @@ -1,5 +1,5 @@ -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { contacts } from "./schema"; export type Contact = { id: string; name: string; email: string; phone: string; updatedAt: number; } @@ -63,4 +63,4 @@ const contactSync = actor({ } }); -export default contactSync; \ No newline at end of file +export default contactSync; diff --git a/examples/snippets/tenant/App.tsx b/examples/snippets/tenant/App.tsx index c5f203a8b..b1d69bee3 100644 --- a/examples/snippets/tenant/App.tsx +++ b/examples/snippets/tenant/App.tsx @@ -1,5 +1,5 @@ -import { createClient } from "actor-core/client"; -import { createReactActorCore } from "@actor-core/react"; +import { createClient } from "@rivetkit/actor/client"; +import { createReactActorCore } from "@rivetkit/react"; import { useState, useEffect } from "react"; import type { App } from "../actors/app"; @@ -127,4 +127,4 @@ export function OrgDashboard({ orgId }: { orgId: string }) {
      ); -} \ No newline at end of file +} diff --git a/examples/snippets/tenant/actor-json.ts b/examples/snippets/tenant/actor-json.ts index 6411b4e34..1d6c54172 100644 --- a/examples/snippets/tenant/actor-json.ts +++ b/examples/snippets/tenant/actor-json.ts @@ -1,4 +1,4 @@ -import { actor } from "actor-core"; +import { actor } from "@rivetkit/actor"; import { authenticate } from "./my-utils"; // Simple tenant organization actor @@ -44,4 +44,4 @@ const tenant = actor({ } }); -export default tenant; \ No newline at end of file +export default tenant; diff --git a/examples/snippets/tenant/actor-sqlite.ts b/examples/snippets/tenant/actor-sqlite.ts index 9c354611e..954ebf8ae 100644 --- a/examples/snippets/tenant/actor-sqlite.ts +++ b/examples/snippets/tenant/actor-sqlite.ts @@ -1,5 +1,5 @@ -import { actor } from "actor-core"; -import { drizzle } from "@actor-core/drizzle"; +import { actor } from "@rivetkit/actor"; +import { drizzle } from "@rivetkit/drizzle"; import { members, invoices } from "./schema"; import { authenticate } from "./my-utils"; @@ -48,4 +48,4 @@ const tenant = actor({ } }); -export default tenant; \ No newline at end of file +export default tenant; diff --git a/package.json b/package.json index bfb98a075..66937c2c1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "packageManager": "yarn@4.2.2", - "name": "@actor-core/workspace", + "name": "@rivetkit/workspace", "workspaces": [ "packages/*", "packages/platforms/*", diff --git a/packages/actor-core-cli/package.json b/packages/actor-core-cli/package.json deleted file mode 100644 index 689fdb18e..000000000 --- a/packages/actor-core-cli/package.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "name": "@actor-core/cli", - "description": "All purpose CLI for ActorCore", - "version": "0.9.0-rc.1", - "license": "Apache-2.0", - "repository": { - "type": "git", - "url": "git+https://github.com/rivet-gg/actor-core.git", - "directory": "packages/actor-core-cli" - }, - "files": [ - "dist", - "package.json" - ], - "exports": { - ".": "./dist/mod.js" - }, - "type": "module", - "bin": { - "ac": "./dist/cli.js", - "actor-core": "./dist/cli.js" - }, - "sideEffects": true, - "preferGlobal": true, - "preferUnplugged": true, - "engines": { - "node": "^20 || ^22 || ^18.20" - }, - "scripts": { - "build": "tsup", - "dev": "yarn build --watch", - "test": "vitest run", - "check-types": "tsc --noEmit" - }, - "dependencies": { - "@actor-core/nodejs": "workspace:^", - "@sentry/profiling-node": "^9.3.0", - "bundle-require": "^5.1.0", - "chokidar": "^4.0.3", - "esbuild": "^0.25.1", - "invariant": "^2.2.4", - "open": "^10.1.0", - "yoga-wasm-web": "0.3.3" - }, - "devDependencies": { - "@inkjs/ui": "^2.0.0", - "@rivet-gg/api": "^24.6.2", - "@sentry/esbuild-plugin": "^3.2.0", - "@sentry/node": "^9.3.0", - "@types/invariant": "^2", - "@types/micromatch": "^4", - "@types/react": "^18.3", - "@types/semver": "^7.5.8", - "@types/which": "^3.0.4", - "actor-core": "workspace:*", - "commander": "^13.1.0", - "execa": "^9.5.2", - "ink": "^5.1.0", - "ink-gradient": "^3.0.0", - "ink-link": "^4.1.0", - "ink-spinner": "^5.0.0", - "micromatch": "^4.0.8", - "pkg-types": "^2.0.0", - "react": "^18.3", - "semver": "^7.7.1", - "strip-ansi": "^7.1.0", - "testcontainers": "^10.18.0", - "tsup": "^8.4.0", - "typescript": "^5.8.2", - "unplugin-macros": "^0.16.0", - "vitest": "^3.1.1", - "which": "^5.0.0", - "zod": "^3.24.2" - }, - "stableVersion": "0.8.0" -} diff --git a/packages/actor-core-cli/rdt-mock.js b/packages/actor-core-cli/rdt-mock.js deleted file mode 100644 index f2a85facf..000000000 --- a/packages/actor-core-cli/rdt-mock.js +++ /dev/null @@ -1,5 +0,0 @@ -// we don't want to bundle react-devtools-core -// but there's an import in ink we want to mock -// hence this file, included by scripts/build.ts - -export default {}; diff --git a/packages/actor-core-cli/src/cli.ts b/packages/actor-core-cli/src/cli.ts deleted file mode 100644 index 9284e4294..000000000 --- a/packages/actor-core-cli/src/cli.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { PACKAGE_JSON } from "./macros" with { type: "macro" }; -import { create, deploy, dev, endpoint, program } from "./mod"; - -export default program - .name(PACKAGE_JSON.name) - .version(PACKAGE_JSON.version) - .description(PACKAGE_JSON.description) - .addCommand(deploy) - .addCommand(create) - .addCommand(dev) - .addCommand(endpoint) - .parse(); diff --git a/packages/actor-core-cli/src/commands/create.tsx b/packages/actor-core-cli/src/commands/create.tsx deleted file mode 100644 index 7f35b698c..000000000 --- a/packages/actor-core-cli/src/commands/create.tsx +++ /dev/null @@ -1,242 +0,0 @@ -import * as fs from "node:fs"; -import * as path from "node:path"; -import { Argument, Command, Option } from "commander"; -import { workflow } from "../workflow"; - -import { $ } from "execa"; -import { Box, Text } from "ink"; -import micromatch from "micromatch"; -import { VERSION, getExamples } from "../macros" with { type: "macro" }; -import { - PLATFORM_NAMES, - type Platform, - cmd, - resolvePlatformSpecificOptions, -} from "../utils/platforms"; - -const EXAMPLES = await getExamples(); - -export const create = new Command() - .name("create") - .alias("init") - .description("Bootstrap your actor project.") - .addArgument(new Argument("[path]", "Location of the project")) - .addOption( - new Option("-t, --template [template]", "Specify which template to use"), - ) - .addOption( - new Option( - "-p, --platform [platform]", - "Specify which platform to use", - ).choices(Object.keys(PLATFORM_NAMES)), - ) - .addOption( - new Option( - "--actor-core-version [version]", - "Specify version of actor-core", - ), - ) - .addOption(new Option("--package-name [name]", "Name of the NPM package")) - .addOption(new Option("--skip-install", "Skip installing dependencies")) - .action(action); - -export async function action( - cmdPath: string, - opts: { - platform?: string; - template?: string; - actorCoreVersion?: string; - packageName?: string; - skipInstall?: boolean; - } = {}, -) { - await workflow("Bootstrap ActorCore in your project", async function* (ctx) { - const wd = - cmdPath || - (yield* ctx.prompt("Where would you like to create your project?", { - type: "text", - defaultValue: "./", - validate: (input) => { - const parsed = path.parse(input); - const isValidPathRegex = /^\.*?([a-zA-Z0-9_-]{0,}\/)*[a-zA-Z0-9_-]+$/; - const isValidPath = (path: string) => - path === "." || path === ".." || isValidPathRegex.test(path); - if (!isValidPath(parsed.base) || !isValidPath(parsed.name)) { - return "Invalid path. Please use a valid directory name like 'randomName'"; - } - return true; - }, - })); - - const stat = fs.statSync(wd, { throwIfNoEntry: false }); - - let detectedPlatform: Platform | undefined; - if (stat?.isDirectory()) { - detectedPlatform = yield* ctx.task("Check directory", async (ctx) => { - const files = await fs.promises.readdir(wd); - const nextJs = micromatch(files, ["next.config.*"]); - - if (nextJs.length > 0) { - return "vercel" as Platform; - } - - const deno = micromatch(files, ["deno.*", "jsr.*"]); - if (deno.length > 0) { - return "deno" as Platform; - } - - const bun = micromatch(files, ["bun.*"]); - if (bun.length > 0) { - return "bun" as Platform; - } - - const cloudflare = micromatch(files, ["wrangler.json"]); - if (cloudflare.length > 0) { - return "cloudflare-workers" as Platform; - } - - const supabase = micromatch(files, [ - "supabase.json", - "supabase", - "*.toml", - ]); - if (supabase.length > 0) { - return "supabase" as Platform; - } - }); - } - - const cwd = path.join(process.cwd(), wd); - - const platform = - (opts.platform as string) || - (yield* ctx.prompt( - `To which platform would you like to deploy? ${detectedPlatform ? `(detected ${PLATFORM_NAMES[detectedPlatform]})` : ""}`, - { - type: "select", - choices: Object.entries(PLATFORM_NAMES).map(([value, label]) => ({ - label, - value, - })), - }, - )); - - const template = - opts.template || - (yield* ctx.prompt("Which template would you like to use?", { - type: "select", - choices: Object.values(EXAMPLES) - .filter((example) => example.supports.includes(platform)) - .map((example) => ({ - label: example.name, - value: example.slug, - })), - })); - - const platformOptions = yield* ctx.task( - "Resolve platform specific files", - async () => { - return resolvePlatformSpecificOptions(platform as Platform, { - packageName: opts.packageName, - files: EXAMPLES[template].files, - version: opts.actorCoreVersion || VERSION, - }); - }, - ); - - const omittedPaths: string[] = []; - yield* ctx.task("Create files", async function* () { - for (const [name, contents] of Object.entries(platformOptions.files)) { - const filePath = path.join(cwd, name); - - const stat = fs.statSync(filePath, { - throwIfNoEntry: false, - }); - - if (!stat) { - yield fs.promises.mkdir(path.dirname(filePath), { - recursive: true, - }); - yield fs.promises.writeFile(filePath, contents, "utf8"); - } else { - omittedPaths.push(filePath); - } - } - }); - - if (!opts.skipInstall) { - yield* ctx.task("Install dependencies", async () => { - await $({ cwd: wd })(...platformOptions.cmds.install); - }); - } - - yield ctx.render( - <> - {omittedPaths.length > 0 && ( - - - Warning - - - We couldn't create the following files because they already exist. - - Remove them and re-run the command to create them. - {omittedPaths.map((omittedPath) => ( - - - {path.relative(process.cwd(), omittedPath)} - - ))} - - )} - - ▸ To get started, run - - - cd {wd} - {cmd(platformOptions.cmds.run)} dev - - - {platformOptions.deployable ? ( - <> - ▸ To deploy, run - - {cmd(platformOptions.cmds.run)} deploy - - - ) : null} - - ▸ Documentation - - - Overview - React - Node.js & Bun - Rust - - - https://actorcore.org/overview - https://actorcore.org/frameworks/react - https://actorcore.org/clients/javascript - https://actorcore.org/clients/rust - - - - ▸ Star ActorCore on GitHub - - - https://github.com/rivet-gg/actor-core - - - , - ); - }).render(); -} diff --git a/packages/actor-core-cli/src/commands/deploy.tsx b/packages/actor-core-cli/src/commands/deploy.tsx deleted file mode 100644 index cb7265bb8..000000000 --- a/packages/actor-core-cli/src/commands/deploy.tsx +++ /dev/null @@ -1,456 +0,0 @@ -import * as fs from "node:fs/promises"; -import path from "node:path"; -import { Argument, Command, Option } from "commander"; -import dedent from "dedent"; -import { $ } from "execa"; -import { Box, Text } from "ink"; -import semver from "semver"; -import which from "which"; -import { MIN_RIVET_CLI_VERSION } from "../constants"; -import { VERSION } from "../macros" with { type: "macro" }; -import { workflow } from "../workflow"; -import { z } from "zod"; -import { RivetClient } from "@rivet-gg/api"; -import { - createActorEndpoint, - createRivetApi, - getServiceToken, -} from "../utils/rivet-api"; -import { validateConfigTask } from "../workflows/validate-config"; - -export const deploy = new Command() - .name("deploy") - .description("Deploy the actor to selected platform.") - .addArgument( - new Argument("", "The platform to deploy to").choices(["rivet"]), - ) - .addArgument(new Argument("", "Location of the app.ts file")) - .addOption( - new Option("-r, --root [path]", "Location of the project").default("./"), - ) - .addOption(new Option("-p, --path [path]", "Location of the app.ts file")) - .addOption(new Option("--skip-manager", "Skip deploying ActorCore manager")) - .addOption(new Option("--env ", "Specify environment to deploy to")) - .addOption(new Option("-v [version]", "Specify version of actor-core")) - .addHelpText( - "afterAll", - "\nMissing your favorite platform?\nLet us know! https://github.com/rivet-gg/actor-core/issues/new", - ) - .action( - async ( - platform, - appPath: string, - opts: { - root: string; - port?: string; - skipManager: boolean; - env?: string; - version?: string; - }, - ) => { - const cwd = path.join(process.cwd(), opts.root); - - const exec = $({ - cwd, - env: { ...process.env, npm_config_yes: "true" }, - }); - - await workflow( - "Deploy actors to Rivet", - async function*(ctx) { - const { config, cli } = yield* ctx.task( - "Prepare", - async function*(ctx) { - const config = yield* validateConfigTask(ctx, cwd, appPath); - - const cli = yield* ctx.task("Locale rivet-cli", async (ctx) => { - let cliLocation = process.env.RIVET_CLI_PATH || null; - - if (!cliLocation) { - cliLocation = await which("rivet-cli", { nothrow: true }); - } - - if (!cliLocation) { - cliLocation = await which("rivet", { nothrow: true }); - } - - if (cliLocation) { - // check version - const { stdout } = await exec`${cliLocation} --version`; - const semVersion = semver.coerce( - stdout.split("\n")[2].split(" ")[1].trim(), - ); - - if (semVersion) { - if (semver.gte(semVersion, MIN_RIVET_CLI_VERSION)) { - return cliLocation; - } - } - } - - return ["npx", "@rivet-gg/cli@latest"]; - }); - - return { config, cli }; - }, - ); - - const { accessToken, projectName, envName, endpoint } = - yield* ctx.task("Auth with Rivet", async function*(ctx) { - const { stdout } = await exec`${cli} metadata auth-status`; - const isLogged = stdout === "true"; - - let endpoint: string | undefined; - if (!isLogged) { - const isUsingCloud = yield* ctx.prompt( - "Are you using Rivet Cloud?", - { - type: "confirm", - }, - ); - - endpoint = "https://api.rivet.gg"; - if (!isUsingCloud) { - endpoint = yield* ctx.prompt("What is the API endpoint?", { - type: "text", - defaultValue: "http://localhost:8080", - validate: (input) => { - if (z.string().url().safeParse(input).success === false) { - return "Please provide a valid URL"; - } - return true; - }, - }); - } - - await exec`${cli} login --api-endpoint=${endpoint}`; - } else { - const { stdout } = await exec`${cli} metadata api-endpoint`; - endpoint = stdout; - } - - const { stdout: accessToken } = - await exec`${cli} metadata access-token`; - - const { stdout: rawEnvs } = await exec`${cli} env ls --json`; - const envs = JSON.parse(rawEnvs); - - const envName = - opts.env ?? - (yield* ctx.prompt("Select environment", { - type: "select", - choices: envs.map( - (env: { display_name: string; name_id: string }) => ({ - label: env.display_name, - value: env.name_id, - }), - ), - })); - - const { stdout: projectName } = - await exec`${cli} metadata project-name-id`; - - return { accessToken, projectName, envName, endpoint }; - }); - - const Rivet = new RivetClient({ - token: accessToken, - environment: endpoint, - }); - - const RivetHttp = createRivetApi(endpoint, accessToken); - - let manager = undefined; - if (!opts.skipManager) { - manager = yield* ctx.task( - "Deploy ActorCore", - async function*(ctx) { - yield fs.mkdir(path.join(cwd, ".actorcore"), { - recursive: true, - }); - - const entrypoint = path.join(cwd, ".actorcore", "manager.js"); - // Calculate relative path from entrypoint to appPath - const relativePath = path.relative( - path.dirname(entrypoint), - path.join(cwd, appPath), - ); - yield fs.writeFile( - entrypoint, - dedent` - import { createManagerHandler } from "@actor-core/rivet"; - import { app } from "${relativePath}"; - export default createManagerHandler({ app }); - `, - ); - - const output = - await exec`${cli} publish manager --env ${envName} --tags role=manager,framework=actor-core,framework-version=${VERSION} --unstable-minify false ${entrypoint}`; - if (output.exitCode !== 0) { - throw ctx.error("Failed to deploy ActorCore.", { - hint: "Check the logs above for more information.", - }); - } - - const { actors: managers } = await Rivet.actor.list({ - tagsJson: JSON.stringify({ name: "manager" }), - environment: envName, - project: projectName, - includeDestroyed: false, - }); - - if (managers.length > 1) { - yield* ctx.warn( - "More than 1 manager actor is running. We recommend manually stopping one of them.", - ); - } - - if (managers.length > 0) { - for (const manager of managers) { - await Rivet.actor.upgrade(manager.id, { - project: projectName, - environment: envName, - body: { - buildTags: { - name: "manager", - current: "true", - }, - }, - }); - } - - const manager = managers.find( - (m) => !!createActorEndpoint(m.network), - ); - - if (!manager) { - throw ctx.error("Failed to find ActorCore Endpoint.", { - hint: "Any existing manager actor is not running or not accessible.", - }); - } - - return manager; - } else { - const serviceToken = await getServiceToken(RivetHttp, { - project: projectName, - env: envName, - }); - - const { regions } = await Rivet.actor.regions.list({ - project: projectName, - environment: envName, - }); - - // find closest region - const region = regions.find( - (r) => r.id === "atl" || r.id === "local", - ); - - if (!region) { - throw ctx.error( - "No closest region found. Please contact support.", - ); - } - - const { actor } = await Rivet.actor.create({ - project: projectName, - environment: envName, - body: { - region: region.id, - tags: { - name: "manager", - role: "manager", - framework: "actor-core", - }, - buildTags: { - name: "manager", - role: "manager", - framework: "actor-core", - current: "true", - }, - runtime: { - environment: { - RIVET_SERVICE_TOKEN: serviceToken, - ...(process.env._RIVET_MANAGER_LOG_LEVEL - ? { - _LOG_LEVEL: - process.env._RIVET_MANAGER_LOG_LEVEL, - } - : {}), - ...(process.env._RIVET_ACTOR_LOG_LEVEL - ? { - _ACTOR_LOG_LEVEL: - process.env._RIVET_ACTOR_LOG_LEVEL, - } - : {}), - }, - }, - network: { - mode: "bridge", - ports: { - http: { - protocol: "https", - routing: { - guard: {}, - }, - }, - }, - }, - lifecycle: { - durable: true, - }, - }, - }); - return actor; - } - }, - ); - } - - for (const [idx, actorName] of Object.keys( - config.app.config.actors, - ).entries()) { - yield* ctx.task( - `Deploy & upload "${actorName}" build (${idx + 1}/${Object.keys(config.app.config.actors).length - })`, - async function*(ctx) { - yield fs.mkdir(path.join(cwd, ".actorcore"), { - recursive: true, - }); - - const entrypoint = path.join( - cwd, - ".actorcore", - `entrypoint-${actorName}.js`, - ); - // Calculate relative path from entrypoint to appPath - const relativePath = path.relative( - path.dirname(entrypoint), - path.join(cwd, appPath), - ); - yield fs.writeFile( - entrypoint, - dedent` - import { createActorHandler } from "@actor-core/rivet"; - import { app } from "${relativePath}"; - export default createActorHandler({ app }); - `, - ); - - const buildTags = { - role: "actor", - framework: "actor-core", - "framework-version": VERSION, - }; - - const tagsArray = Object.entries(buildTags) - .map(([key, value]) => `${key}=${value}`) - .join(","); - - const output = - await exec`${cli} publish --env=${envName} --tags=${tagsArray} --unstable-minify false ${actorName} ${entrypoint}`; - - if (output.exitCode !== 0) { - throw ctx.error("Failed to deploy & upload actors.", { - hint: "Check the logs above for more information.", - }); - } - - await Rivet.actor.upgradeAll({ - project: projectName, - environment: envName, - body: { - tags: { name: actorName }, - buildTags: { - name: actorName, - current: "true", - }, - }, - }); - }, - ); - } - - const managerEndpoint = manager - ? createActorEndpoint(manager.network) - : undefined; - const actorName = Object.keys(config.app.config.actors)[0]; - const hub = endpoint?.includes("localhost") - ? `${endpoint}/ui` - : "https://hub.rivet.gg"; - - yield ctx.render( - - - Build uploaded successfully! - - - {opts.skipManager ? ( - <> - ▸ ActorCore Manager: - - - Manager deployment was skipped. - - - ) : managerEndpoint ? ( - <> - ▸ Connect to your Actor: - - - - {dedent`const client = createClient("${managerEndpoint}");`} - - - - {dedent`const ${actorName} = await client.${actorName}.get();`} - - - - ) : ( - <> - ▸ Connect to your Actor: - - - - Failed to deploy Actor Manager. Please check the logs in - Rivet Hub for more information. - - - - )} - - - ▸ Resources - - - {!opts.skipManager && managerEndpoint ? ( - Endpoint - ) : null} - Builds - Actors - Documentation - - - {!opts.skipManager && managerEndpoint ? ( - {managerEndpoint} - ) : null} - {`${hub}/projects/${projectName}/environments/${envName}/builds`} - {`${hub}/projects/${projectName}/environments/${envName}/actors`} - - https://actorcore.org/ - - - - - , - ); - }, - { showLabel: false }, - ).render(); - }, - ); diff --git a/packages/actor-core-cli/src/commands/dev.tsx b/packages/actor-core-cli/src/commands/dev.tsx deleted file mode 100644 index 5b8ad0626..000000000 --- a/packages/actor-core-cli/src/commands/dev.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import * as path from "node:path"; -import { Argument, Command, Option } from "commander"; -import { workflow } from "../workflow"; - -import { validateConfigTask } from "../workflows/validate-config"; -import chokidar from "chokidar"; -import { Text } from "ink"; -import open from "open"; -import { withResolvers } from "../utils/mod"; -import { spawn } from "node:child_process"; - -export const dev = new Command() - .name("dev") - .description("Run locally your ActorCore project.") - .addArgument( - new Argument("[path]", "Location of the app.ts file").default( - "actors/app.ts", - ), - ) - .addOption( - new Option("-r, --root [path]", "Location of the project").default("./"), - ) - .addOption( - new Option("--port [port]", "Specify which platform to use").default( - "6420", - ), - ) - .addOption( - new Option("--open", "Open the browser with ActorCore Studio").default( - true, - ), - ) - .option("--no-open", "Do not open the browser with ActorCore Studio") - .action(action); - -export async function action( - appPath: string, - opts: { - root: string; - port?: string; - open: boolean; - }, -) { - const cwd = path.join(process.cwd(), opts.root); - - await workflow( - `Run locally your ActorCore project (${appPath})`, - async function* (ctx) { - if (opts.open) { - open( - process.env._ACTOR_CORE_CLI_DEV - ? "http://localhost:43708" - : "http://studio.rivet.gg", - ); - } - - const watcher = chokidar.watch(cwd, { - awaitWriteFinish: true, - ignoreInitial: true, - ignored: (path) => path.includes("node_modules"), - }); - - function createServer() { - return spawn( - process.execPath, - [ - path.join( - path.dirname(require.resolve("@actor-core/cli")), - "server-entry.js", - ), - ], - { - env: { ...process.env, PORT: opts.port, APP_PATH: appPath }, - cwd, - stdio: "overlapped", - }, - ); - } - - let server: ReturnType | undefined = undefined; - let lock: ReturnType = withResolvers(); - - function createLock() { - if (lock) { - lock.resolve(undefined); - } - lock = withResolvers(); - } - - watcher.on("all", async (_, path) => { - if (path.includes("node_modules") || path.includes("/.")) return; - - if (server?.exitCode === 1) { - // Server exited with error - console.log("Server exited with error"); - lock.resolve(undefined); - return; - } else { - server?.kill("SIGQUIT"); - } - }); - - createLock(); - - while (true) { - yield* ctx.task( - "Server started. Watching for changes", - async function* (ctx) { - server = createServer(); - if (server?.stdout) { - yield ctx.attach(server.stdout, server.stderr); - } - createLock(); - - server?.addListener("exit", (code) => { - if (code === 1) { - ctx.changeLabel( - "Server exited with error. It will be restarted after next file change...", - ); - // Server exited with error - return; - } - lock.resolve(undefined); - }); - - await lock.promise; - }, - { success: (Changes detected, restarting!) }, - ); - } - }, - ).render(); -} diff --git a/packages/actor-core-cli/src/commands/endpoint.tsx b/packages/actor-core-cli/src/commands/endpoint.tsx deleted file mode 100644 index 970e77ca5..000000000 --- a/packages/actor-core-cli/src/commands/endpoint.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import * as fs from "node:fs/promises"; -import path from "node:path"; -import { Argument, Command, Option } from "commander"; -import { $ } from "execa"; -import semver from "semver"; -import which from "which"; -import { MIN_RIVET_CLI_VERSION } from "../constants"; -import { workflow } from "../workflow"; -import { RivetClient } from "@rivet-gg/api"; -import { - createActorEndpoint, - createRivetApi, - getServiceToken, -} from "../utils/rivet-api"; -import { validateConfigTask } from "../workflows/validate-config"; -import invariant from "invariant"; - -export const endpoint = new Command() - .name("endpoint") - .description( - "Get the application endpoint URL for your deployed application in Rivet.", - ) - .addArgument( - new Argument("", "The platform to get the endpoint for").choices([ - "rivet", - ]), - ) - .addOption( - new Option( - "-e, --env [env]", - "Specify environment to get the endpoint for", - ), - ) - .addOption( - new Option("--plain", "Output only the URL without any additional text"), - ) - // No actor option needed - returns the first available endpoint - .action( - async ( - platform, - opts: { - env?: string; - plain?: boolean; - }, - ) => { - const cwd = process.cwd(); - - const exec = $({ - cwd, - env: { ...process.env, npm_config_yes: "true" }, - }); - - await workflow( - "Get actor endpoint", - async function*(ctx) { - const cli = yield* ctx.task("Locate rivet-cli", async (ctx) => { - let cliLocation = process.env.RIVET_CLI_PATH || null; - - if (!cliLocation) { - cliLocation = await which("rivet-cli", { nothrow: true }); - } - - if (!cliLocation) { - cliLocation = await which("rivet", { nothrow: true }); - } - - if (cliLocation) { - // check version - const { stdout } = await exec`${cliLocation} --version`; - const semVersion = semver.coerce( - stdout.split("\n")[2].split(" ")[1].trim(), - ); - - if (semVersion) { - if (semver.gte(semVersion, MIN_RIVET_CLI_VERSION)) { - return cliLocation; - } - } - } - - return ["npx", "@rivet-gg/cli@latest"]; - }); - - const { accessToken, projectName, envName, endpoint } = - yield* ctx.task("Auth with Rivet", async function*(ctx) { - const { stdout } = await exec`${cli} metadata auth-status`; - const isLogged = stdout === "true"; - - let endpoint: string | undefined; - if (!isLogged) { - const isUsingCloud = yield* ctx.prompt( - "Are you using Rivet Cloud?", - { - type: "confirm", - }, - ); - - endpoint = "https://api.rivet.gg"; - if (!isUsingCloud) { - endpoint = yield* ctx.prompt("What is the API endpoint?", { - type: "text", - defaultValue: "http://localhost:8080", - }); - } - - await exec`${cli} login --api-endpoint=${endpoint}`; - } else { - const { stdout } = await exec`${cli} metadata api-endpoint`; - endpoint = stdout; - } - - const { stdout: accessToken } = - await exec`${cli} metadata access-token`; - - const { stdout: rawEnvs } = await exec`${cli} env ls --json`; - const envs = JSON.parse(rawEnvs); - - const envName = - opts.env ?? - (yield* ctx.prompt("Select environment", { - type: "select", - choices: envs.map( - (env: { display_name: string; name_id: string }) => ({ - label: env.display_name, - value: env.name_id, - }), - ), - })); - - const { stdout: projectName } = - await exec`${cli} metadata project-name-id`; - - return { accessToken, projectName, envName, endpoint }; - }); - - const rivet = new RivetClient({ - token: accessToken, - environment: endpoint, - }); - - yield* ctx.task("Get actor endpoint", async function*(ctx) { - const { actors } = await rivet.actor.list({ - environment: envName, - project: projectName, - includeDestroyed: false, - tagsJson: JSON.stringify({ - name: "manager", - role: "manager", - framework: "actor-core", - }), - }); - - if (actors.length === 0) { - throw ctx.error("No managers found for this project.", { - hint: "Make sure you have deployed first.", - }); - } - - const managerActor = actors[0]; - const port = managerActor.network.ports.http; - invariant(port, "http port does not exist on manager"); - invariant(port.url, "port has no url"); - - if (opts.plain) { - console.log(port.url); - } else { - yield ctx.log(`Application endpoint: ${port.url}`); - } - }); - }, - { - showLabel: !opts.plain, - quiet: opts.plain, - }, - ).render(); - }, - ); diff --git a/packages/actor-core-cli/src/constants.ts b/packages/actor-core-cli/src/constants.ts deleted file mode 100644 index 78da511b8..000000000 --- a/packages/actor-core-cli/src/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const MIN_RIVET_CLI_VERSION = "25.2.2"; diff --git a/packages/actor-core-cli/src/instrument.ts b/packages/actor-core-cli/src/instrument.ts deleted file mode 100644 index 95022e4a4..000000000 --- a/packages/actor-core-cli/src/instrument.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as Sentry from "@sentry/node"; -import { nodeProfilingIntegration } from "@sentry/profiling-node"; - -// Ensure to call this before requiring any other modules! -Sentry.init({ - dsn: process.env.SENTRY_DSN, - integrations: [ - // Add our Profiling integration - nodeProfilingIntegration(), - ], -}); diff --git a/packages/actor-core-cli/src/macros.ts b/packages/actor-core-cli/src/macros.ts deleted file mode 100644 index 5c6c1a083..000000000 --- a/packages/actor-core-cli/src/macros.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { execSync } from "node:child_process"; -import { readFile, readdir, stat } from "node:fs/promises"; -import path from "node:path"; -import micromatch from "micromatch"; -import pkgJson from "../package.json"; -import { PLATFORM_SLUGS } from "./utils/platforms"; - -const EXAMPLES_PATH = path.join(__dirname, "../../../examples"); - -const IGNORED_PATHS = /platforms|benches|(^tsconfig.json$)/; - -interface ExamplesRegistry { - [key: string]: { - name: string; - slug: string; - supports: string[]; - files: Record; - }; -} - -export async function getExamples(): Promise { - const registry: ExamplesRegistry = {}; - - const dirs = await readdir(EXAMPLES_PATH, { encoding: "utf-8" }); - - for (const dir of dirs) { - if (dir === "snippets") continue; - - const output = execSync(`git ls-files ${dir}`, { - cwd: EXAMPLES_PATH, - encoding: "utf-8", - }); - - const files = output - .split("\n") - .filter(Boolean) - .map((file) => path.relative(dir, file)); - - const packageJson = await readFile( - path.join(EXAMPLES_PATH, dir, "package.json"), - { encoding: "utf-8" }, - ); - - registry[dir] = { - slug: dir, - name: JSON.parse(packageJson).name, - supports: micromatch( - PLATFORM_SLUGS, - JSON.parse(packageJson).example.platforms, - {}, - ), - files: {}, - }; - - for (const file of files) { - if (IGNORED_PATHS.test(file)) { - continue; - } - - const info = await stat(path.join(EXAMPLES_PATH, dir, file)); - if (info.isDirectory()) { - continue; - } - - registry[dir].files[file] = await readFile( - path.join(EXAMPLES_PATH, dir, file), - { encoding: "utf-8" }, - ); - } - } - - return registry; -} - -export const VERSION = pkgJson.version; -export const PACKAGE_JSON = pkgJson; diff --git a/packages/actor-core-cli/src/mod.ts b/packages/actor-core-cli/src/mod.ts deleted file mode 100644 index 9e3b0f8ad..000000000 --- a/packages/actor-core-cli/src/mod.ts +++ /dev/null @@ -1,7 +0,0 @@ -import "./instrument"; -export { deploy } from "./commands/deploy"; -export { create, action as createAction } from "./commands/create"; -export { dev } from "./commands/dev"; -export { endpoint } from "./commands/endpoint"; -export { program } from "commander"; -export default {}; diff --git a/packages/actor-core-cli/src/server-entry.ts b/packages/actor-core-cli/src/server-entry.ts deleted file mode 100644 index e75a28f91..000000000 --- a/packages/actor-core-cli/src/server-entry.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { validateConfig } from "./utils/config"; -import { serve } from "@actor-core/nodejs"; - -async function run() { - const config = await validateConfig(process.cwd(), process.env.APP_PATH!); - config.app.config.inspector = { - enabled: true, - }; - config.app.config.cors = { - origin: (origin) => origin, - }; - serve(config.app, { - port: Number.parseInt(process.env.PORT || "6420", 10) || 6420, - }); -} - -run().catch((err) => { - console.error(err); - process.exit(1); -}); diff --git a/packages/actor-core-cli/src/ui/Intro.tsx b/packages/actor-core-cli/src/ui/Intro.tsx deleted file mode 100644 index 592bb54c7..000000000 --- a/packages/actor-core-cli/src/ui/Intro.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Box, Text } from "ink"; - -export function Intro() { - return ( - - - {"▄▀█ █▀▀ ▀█▀ █▀█ █▀█ █▀▀ █▀█ █▀█ █▀▀"} - {"█▀█ █▄▄ █ █▄█ █▀▄ █▄▄ █▄█ █▀▄ ██▄"} - - - ); -} diff --git a/packages/actor-core-cli/src/ui/Workflow.tsx b/packages/actor-core-cli/src/ui/Workflow.tsx deleted file mode 100644 index 4aa5de58b..000000000 --- a/packages/actor-core-cli/src/ui/Workflow.tsx +++ /dev/null @@ -1,364 +0,0 @@ -import { - ConfirmInput, - Select, - TextInput, - ThemeProvider, - defaultTheme, - extendTheme, -} from "@inkjs/ui"; -import { ExecaError } from "execa"; -import { Box, Text, type TextProps } from "ink"; -import Spinner from "ink-spinner"; -import { type ReactNode, useEffect, useState } from "react"; -import stripAnsi from "strip-ansi"; -import { type WorkflowAction, WorkflowError } from "../workflow"; -import type Stream from "node:stream"; - -const customTheme = extendTheme(defaultTheme, { - components: { - Select: { - styles: { - focusIndicator: () => ({ - color: "#ff4f00", - }), - label({ isFocused, isSelected }) { - let color: string | undefined; - - if (isSelected) { - color = "#ff4f00"; - } - - if (isFocused) { - color = "#ff4f00"; - } - - return { color }; - }, - }, - }, - }, -}); - -export const WorkflowDetails = ({ - tasks, - interactive, -}: { tasks: WorkflowAction.Interface[]; interactive?: boolean }) => { - return ( - - - - - - ); -}; - -function Tasks({ - tasks, - parent, - interactive, - parentOpts, -}: { - tasks: WorkflowAction.Interface[]; - parent: string | null; - parentOpts?: WorkflowAction.Progress["meta"]["opts"]; - interactive?: boolean; -}) { - const currentTasks = tasks.filter((task) => task.meta.parent === parent); - - if (currentTasks.length === 0) { - return null; - } - return ( - - {currentTasks.map((task) => ( - - - {"status" in task && task.status === "done" && interactive ? null : ( - - )} - - ))} - - ); -} - -export function Task({ - task, - parent, - interactive, -}: { - task: WorkflowAction.Interface; - parent: string | null; - interactive?: boolean; -}) { - if ("__taskPrompt" in task) { - if (task.opts.type === "select") { - if (task.opts.answer) { - return ( - - - - {task.question}{" "} - { - task.opts.choices.find((c) => c.value === task.opts.answer) - ?.label - } - - - - ); - } - return ( - } /> - ); - } - if (task.opts.type === "confirm") { - if (task.opts.answer !== null) { - return ( - - - - {task.question} {task.opts.answer ? "Yes" : "No"} - - - - ); - } - return ( - - - - {task.question}{" "} - - { - if (task.opts.type !== "confirm") return; - task.opts.onSubmit(true); - }} - onCancel={() => { - if (task.opts.type !== "confirm") return; - task.opts.onSubmit(false); - }} - /> - - ); - } - if (task.opts.type === "text") { - if (task.opts.answer) { - return ( - - - - {task.question} {task.opts.answer} - - - - ); - } - return } />; - } - } - - if ("__taskProgress" in task) { - return ( - <> - {task.meta.opts?.showLabel === false ? null : ( - - {task.meta.name} - - )} - {task.status === "error" ? ( - - {task.error instanceof WorkflowError ? ( - - {task.error.description} - {task.error.opts.hint ? ( - - Hint {task.error.opts.hint || ""} - - ) : null} - - ) : task.error instanceof ExecaError ? ( - - {task.error.shortMessage} - {typeof task.error.stderr === "string" ? ( - {stripAnsi(task.error.stderr)} - ) : null} - - ) : ( - <> - {task.error?.toString()} - - )} - - ) : null} - {task.streams ? : null} - - ); - } -} - -export function StreamBox({ streams }: { streams: Stream.Readable[] }) { - const [chunks, setChunks] = useState([]); - - useEffect(() => { - const handleChunk = (chunk: Buffer) => { - setChunks((old) => { - const lines = chunk - .toString() - .split(/\n/gm) - .flatMap((line) => line.split(/\\n/g)) - .map((line) => stripAnsi(line)); - return [...old, ...lines]; - }); - }; - for (const stream of streams) { - stream.on("data", handleChunk); - } - - return () => { - for (const stream of streams) { - stream.off("data", handleChunk); - } - }; - }, [streams]); - - return ( - - {chunks.map((chunk, chunkIdx) => { - return ( - - {chunk.replaceAll("\n", "")} - - ); - })} - - ); -} - -export function Status({ - value, - children, - interactive, - done = (Done), - ...rest -}: TextProps & { - value: WorkflowAction.Progress["status"]; - interactive?: boolean; - done?: ReactNode; -}) { - return ( - - - {value === "done" ? "✔" : null} - {value === "error" ? : null} - {value === "running" ? ( - interactive ? ( - - ) : ( - - ) - ) : null} - {" "} - {children} - {value === "running" && !interactive ? : null} - {value === "done" ? done : null} - - ); -} - -export function TextQuestion({ - task, -}: { task: WorkflowAction.Prompt.One<"text"> }) { - const [error, setError] = useState(null); - - const handleSubmit = (value: string) => { - if (task.opts.validate) { - const validation = task.opts.validate(value); - if (validation !== true) { - setError(validation); - return; - } - } - task.opts.onSubmit(value); - }; - - return ( - - - - {task.question} - - - {error ? ✖ {error} : null} - - ); -} - -function SelectQuestion({ - task, -}: { task: WorkflowAction.Prompt.One<"select"> }) { - return ( - - - - {task.question} - -