@blujosi/rivetkit-svelte 0.0.7 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,55 +1,61 @@
1
- # RivetKit Svelte
1
+ # @blujosi/rivetkit-svelte
2
2
 
3
- A Svelte 5 integration for [RivetKit](https://rivetkit.com) that provides reactive actor connections using Svelte's new runes system.
3
+ A Svelte 5 integration for [RivetKit](https://rivet.dev) that provides reactive actor connections using Svelte 5's runes system, plus a built-in SvelteKit handler for serverless deployment.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```sh
8
- pnpm add @rivetkit/svelte
8
+ pnpm add @blujosi/rivetkit-svelte rivetkit
9
9
 
10
10
  # or
11
11
 
12
- npm i @rivetkit/svelte
12
+ npm i @blujosi/rivetkit-svelte rivetkit
13
13
  ```
14
14
 
15
15
  ## Overview
16
16
 
17
- RivetKit Svelte provides seamless integration between RivetKit's actor system and Svelte 5's reactive runes. It allows you to connect to RivetKit actors from your Svelte components with automatic reactivity, real-time event handling, and type safety.
17
+ `@blujosi/rivetkit-svelte` provides two main pieces:
18
+
19
+ 1. **Svelte 5 client** (`@blujosi/rivetkit-svelte`) — `useActor` hook for reactive actor connections with real-time events
20
+ 2. **SvelteKit handler** (`@blujosi/rivetkit-svelte/sveltekit`) — `createRivetKitHandler` to serve RivetKit as a SvelteKit API route
18
21
 
19
22
  ## Features
20
23
 
21
- - **Svelte 5 Runes Integration** - Built specifically for Svelte 5's new reactivity system
22
- - **Real-time Actor Connections** - Connect to RivetKit actors with automatic state synchronization
23
- - **Event Handling** - Listen to actor events with automatic cleanup
24
- - **Type Safety** - Full TypeScript support with proper type inference
25
- - **SSR Compatible** - Works with SvelteKit's server-side rendering
26
- - **Automatic Reconnection** - Handles connection states and errors gracefully
24
+ - **Svelte 5 Runes** Built for `$state`, `$effect`, and `$derived`
25
+ - **Real-time Actor Connections** Connect to RivetKit actors with automatic state sync
26
+ - **Event Handling** `useEvent` with automatic cleanup
27
+ - **Type Safety** Full TypeScript support with registry type inference
28
+ - **SSR Compatible** Browser guard for SvelteKit SSR
29
+ - **SvelteKit Handler** Run RivetKit serverless inside your SvelteKit app
27
30
 
28
- ## Quick Start
31
+ ---
29
32
 
30
- ### Step 1: Set Up Your RivetKit Backend
33
+ ## Quick Start
31
34
 
32
- First, create your RivetKit actors and registry. Here's a simple counter example:
35
+ ### 1. Define Your Actors & Registry
33
36
 
34
37
  ```typescript
35
38
  // backend/registry.ts
36
- import { actor, setup } from "@rivetkit/actor";
39
+ import { actor, setup } from "rivetkit";
37
40
 
38
41
  export const counter = actor({
39
- onAuth: () => {
40
- // Configure auth here if needed
41
- },
42
- state: { count: 0 },
42
+ state: { count: 0, countDouble: 0 },
43
43
  actions: {
44
44
  increment: (c, x: number) => {
45
- console.log("incrementing by", x);
46
45
  c.state.count += x;
47
46
  c.broadcast("newCount", c.state.count);
48
47
  return c.state.count;
49
48
  },
49
+ doubleIncrement: (c, y: number) => {
50
+ c.state.countDouble += y;
51
+ c.broadcast("newDoubleCount", c.state.countDouble);
52
+ return c.state.countDouble;
53
+ },
50
54
  reset: (c) => {
51
55
  c.state.count = 0;
56
+ c.state.countDouble = 0;
52
57
  c.broadcast("newCount", c.state.count);
58
+ c.broadcast("newDoubleCount", c.state.countDouble);
53
59
  return c.state.count;
54
60
  },
55
61
  },
@@ -58,285 +64,252 @@ export const counter = actor({
58
64
  export const registry = setup({
59
65
  use: { counter },
60
66
  });
67
+
68
+ export type Registry = typeof registry;
61
69
  ```
62
70
 
63
- ### Step 2: Start Your RivetKit Server
71
+ ### 2. Set Up the SvelteKit Handler
72
+
73
+ Create a catch-all API route to proxy RivetKit requests through SvelteKit:
64
74
 
65
75
  ```typescript
66
- // backend/index.ts
67
- import { registry } from "./registry";
76
+ // src/routes/api/rivet/[...rest]/+server.ts
77
+ import { createRivetKitHandler } from "@blujosi/rivetkit-svelte/sveltekit";
78
+ import { dev } from "$app/environment";
79
+ import { registry } from "$backend/registry";
80
+
81
+ export const { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } =
82
+ createRivetKitHandler({
83
+ isDev: !!dev,
84
+ registry,
85
+ rivetSiteUrl: "http://localhost:5173",
86
+ });
87
+ ```
68
88
 
69
- export type Registry = typeof registry;
89
+ The handler automatically:
90
+ - Spawns the RivetKit engine in dev mode
91
+ - Configures the serverless runner pool
92
+ - Proxies requests to the registry's built-in handler
70
93
 
71
- registry.runServer({
72
- cors: {
73
- origin: "http://localhost:5173", // Your Svelte app URL
74
- },
75
- });
76
- ```
94
+ #### Handler Options
77
95
 
78
- ### Step 3: Create the Client Connection
96
+ | Option | Type | Description |
97
+ |---|---|---|
98
+ | `registry` | `Registry` | Your RivetKit registry instance |
99
+ | `isDev` | `boolean` | Enables auto-engine spawn and runner pool config |
100
+ | `rivetSiteUrl` | `string?` | Base URL for the site. Falls back to `PUBLIC_RIVET_ENDPOINT` env var |
79
101
 
80
- In your Svelte app, create a client connection to your RivetKit server:
102
+ ### 3. Create the Client
81
103
 
82
104
  ```typescript
83
- // src/lib/actor-client.ts
84
- import { createClient, createRivetKit } from "@rivetkit/svelte";
85
- import type { Registry } from "../../backend";
105
+ // src/lib/actor.client.ts
106
+ import { createClient, createRivetKitWithClient } from "@blujosi/rivetkit-svelte";
107
+ import type { Client } from "rivetkit/client";
108
+ import { browser } from "$app/environment";
109
+ import type { Registry } from "$backend/registry";
110
+
111
+ let rivetClient: Client<Registry> | undefined;
112
+
113
+ export const getRivetClient = () => {
114
+ if (!browser) return { useActor: () => {} };
86
115
 
87
- const client = createClient<Registry>(`http://localhost:8080`);
88
- export const { useActor } = createRivetKit(client);
116
+ const origin = `${location.origin}/api/rivet`;
117
+ rivetClient = createClient<Registry>(origin);
118
+ const { useActor } = createRivetKitWithClient(rivetClient);
119
+
120
+ return { rivetClient, useActor };
121
+ };
89
122
  ```
90
123
 
91
- ### Step 4: Use Actors in Your Svelte Components
124
+ > **Important:** The client must only be created in the browser. The `browser` guard ensures SSR doesn't attempt a connection.
92
125
 
93
- Now you can use the `useActor` function in your Svelte 5 components:
126
+ ### 4. Use Actors in Svelte Components
94
127
 
95
128
  ```svelte
96
129
  <!-- src/routes/+page.svelte -->
97
130
  <script lang="ts">
98
- import { useActor } from "../lib/actor-client";
131
+ import { getRivetClient } from "../lib/actor.client";
132
+ import { browser } from "$app/environment";
99
133
 
100
134
  let count = $state(0);
101
- const counter = useActor({ name: 'counter', key: ['test-counter'] });
102
-
103
- $effect(() => {
104
- console.log('status', counter?.isConnected);
105
- counter?.useEvent('newCount', (x: number) => {
106
- console.log('new count event', x);
107
- count = x;
108
- });
135
+ let countDouble = $state(0);
136
+
137
+ const { useActor } = getRivetClient();
138
+ const counterActor = browser
139
+ ? useActor({ name: "counter", key: ["test-counter"] })
140
+ : undefined;
141
+
142
+ // Listen for events (call at top-level, NOT inside $effect)
143
+ counterActor?.useEvent("newCount", (x: number) => {
144
+ count = x;
109
145
  });
110
146
 
111
- const increment = () => {
112
- counter?.connection?.increment(1);
113
- };
147
+ counterActor?.useEvent("newDoubleCount", (y: number) => {
148
+ countDouble = y;
149
+ });
114
150
 
115
- const reset = () => {
116
- counter?.connection?.reset();
151
+ // Call actions through the connection
152
+ const increment = async () => {
153
+ await counterActor?.current?.connection?.increment(1);
154
+ };
155
+ const reset = async () => {
156
+ await counterActor?.current?.connection?.reset();
157
+ };
158
+ const doubleCountClick = async () => {
159
+ await counterActor?.current?.connection?.doubleIncrement(2);
117
160
  };
118
-
119
- // Debug the connection status
120
- $inspect('useActor is connected', counter?.isConnected);
121
161
  </script>
122
162
 
123
163
  <div>
124
164
  <h1>Counter: {count}</h1>
125
165
  <button onclick={increment}>Increment</button>
126
166
  <button onclick={reset}>Reset</button>
167
+
168
+ <h1>Counter 2: {countDouble}</h1>
169
+ <button onclick={doubleCountClick}>Double Count</button>
127
170
  </div>
128
171
  ```
129
172
 
130
- ## Core Concepts
131
-
132
- ### useActor Hook
173
+ ---
133
174
 
134
- The `useActor` function is the main way to connect to RivetKit actors from your Svelte components. It returns a reactive object with the following properties:
175
+ ## API Reference
135
176
 
136
- - **`connection`** - The actor connection object for calling actions
137
- - **`handle`** - The actor handle for advanced operations
138
- - **`isConnected`** - Boolean indicating if the actor is connected
139
- - **`isConnecting`** - Boolean indicating if the actor is currently connecting
140
- - **`isError`** - Boolean indicating if there's an error
141
- - **`error`** - The error object if one exists
142
- - **`useEvent`** - Function to listen for actor events
177
+ ### Client Exports (`@blujosi/rivetkit-svelte`)
143
178
 
144
- ### Actor Options
179
+ #### `createClient<Registry>(url)`
145
180
 
146
- When calling `useActor`, you need to provide:
181
+ Creates a RivetKit client connection.
147
182
 
148
183
  ```typescript
149
- const actor = useActor({
150
- name: 'counter', // The actor name from your registry
151
- key: ['test-counter'], // Unique key for this actor instance
152
- params: { /* ... */ }, // Optional parameters
153
- enabled: true // Optional, defaults to true
154
- });
184
+ import { createClient } from "@blujosi/rivetkit-svelte";
185
+ const client = createClient<Registry>("http://localhost:5173/api/rivet");
155
186
  ```
156
187
 
157
- ## Event Handling
158
-
159
- ### Using useEvent
188
+ #### `createRivetKit<Registry>(url, opts?)`
160
189
 
161
- The `useEvent` function allows you to listen for events broadcast by actors:
190
+ Shorthand that creates both the client and the `useActor` hook.
162
191
 
163
- ```svelte
164
- <script lang="ts">
165
- import { useActor } from "../lib/actor-client";
166
-
167
- const chatActor = useActor({ name: 'chat', key: ['room-1'] });
168
-
169
- $effect(() => {
170
- // Listen for new messages
171
- chatActor?.useEvent('newMessage', (message) => {
172
- console.log('New message:', message);
173
- // Update your component state
174
- });
175
-
176
- // Listen for user joined events
177
- chatActor?.useEvent('userJoined', (user) => {
178
- console.log('User joined:', user);
179
- });
180
- });
181
- </script>
192
+ ```typescript
193
+ import { createRivetKit } from "@blujosi/rivetkit-svelte";
194
+ export const { useActor } = createRivetKit<Registry>("http://localhost:5173/api/rivet");
182
195
  ```
183
196
 
184
- ### Alternative Event Listening
185
-
186
- You can also listen to events directly on the connection:
187
-
188
- ```svelte
189
- <script lang="ts">
190
- const actor = useActor({ name: 'counter', key: ['test'] });
197
+ #### `createRivetKitWithClient<Registry>(client, opts?)`
191
198
 
192
- $effect(() => {
193
- const unsubscribe = actor.connection?.on('newCount', (count) => {
194
- console.log('Count updated:', count);
195
- });
199
+ Creates the `useActor` hook from an existing client instance. Use this when you need access to the client elsewhere.
196
200
 
197
- // Cleanup is handled automatically by $effect
198
- return unsubscribe;
199
- });
200
- </script>
201
+ ```typescript
202
+ import { createClient, createRivetKitWithClient } from "@blujosi/rivetkit-svelte";
203
+ const client = createClient<Registry>(url);
204
+ export const { useActor } = createRivetKitWithClient(client);
201
205
  ```
202
206
 
203
- ## Advanced Usage
207
+ ### `useActor(options)`
204
208
 
205
- ### Conditional Actor Connections
209
+ Connects to a RivetKit actor and returns reactive state. **Must be called at the top level of a component script** (not inside `onMount` or other callbacks) so runes attach correctly.
206
210
 
207
- You can conditionally enable/disable actor connections:
211
+ ```typescript
212
+ const actor = useActor({
213
+ name: "counter", // Actor name from your registry
214
+ key: ["test-counter"], // Unique key for this instance
215
+ params: { /* ... */ }, // Optional parameters
216
+ createInRegion: "us-east-1", // Optional region
217
+ createWithInput: { /* */ }, // Optional input data
218
+ enabled: true, // Optional, defaults to true
219
+ });
220
+ ```
208
221
 
209
- ```svelte
210
- <script lang="ts">
211
- let userId = $state<string | null>(null);
222
+ **Returns:**
212
223
 
213
- const userActor = useActor({
214
- name: 'user',
215
- key: [userId || 'anonymous'],
216
- enabled: userId !== null
217
- });
218
- </script>
219
- ```
224
+ | Property | Type | Description |
225
+ |---|---|---|
226
+ | `current.connection` | `ActorConn` | Call actions on the actor |
227
+ | `current.handle` | `ActorHandle` | Advanced actor operations |
228
+ | `current.isConnected` | `boolean` | Whether the actor is connected |
229
+ | `current.isConnecting` | `boolean` | Whether a connection is in progress |
230
+ | `current.isError` | `boolean` | Whether there's an error |
231
+ | `current.error` | `Error \| null` | The error object, if any |
232
+ | `useEvent(name, handler)` | `function` | Listen for actor events |
220
233
 
221
- ### Multiple Actor Instances
234
+ ### `useEvent(eventName, handler)`
222
235
 
223
- You can connect to multiple instances of the same actor:
236
+ Registers an event listener with automatic cleanup. **Call at the top level of the component script, not inside `$effect`.**
224
237
 
225
- ```svelte
226
- <script lang="ts">
227
- const chatRoom1 = useActor({ name: 'chat', key: ['room-1'] });
228
- const chatRoom2 = useActor({ name: 'chat', key: ['room-2'] });
229
- const privateChat = useActor({ name: 'chat', key: ['private', userId] });
230
- </script>
238
+ ```typescript
239
+ counterActor?.useEvent("newCount", (value: number) => {
240
+ count = value;
241
+ });
231
242
  ```
232
243
 
233
- ### Error Handling
244
+ ### SvelteKit Exports (`@blujosi/rivetkit-svelte/sveltekit`)
234
245
 
235
- Handle connection errors gracefully:
246
+ #### `createRivetKitHandler(opts)`
236
247
 
237
- ```svelte
238
- <script lang="ts">
239
- const actor = useActor({ name: 'counter', key: ['test'] });
248
+ Creates SvelteKit request handlers for all HTTP methods.
240
249
 
241
- $effect(() => {
242
- if (actor?.isError && actor?.error) {
243
- console.error('Actor connection error:', actor.error);
244
- // Show error message to user
245
- }
246
- });
247
- </script>
250
+ ```typescript
251
+ import { createRivetKitHandler } from "@blujosi/rivetkit-svelte/sveltekit";
248
252
 
249
- {#if actor?.isError}
250
- <div class="error">
251
- Connection failed: {actor.error?.message}
252
- </div>
253
- {:else if actor?.isConnecting}
254
- <div class="loading">Connecting...</div>
255
- {:else if actor?.isConnected}
256
- <div class="success">Connected!</div>
257
- {/if}
253
+ export const { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } =
254
+ createRivetKitHandler({ isDev: true, registry, rivetSiteUrl: "http://localhost:5173" });
258
255
  ```
259
256
 
260
- ## API Reference
257
+ ---
261
258
 
262
- ### createClient(url: string)
259
+ ## Common Pitfalls
263
260
 
264
- Creates a client connection to your RivetKit server.
261
+ ### Don't call `useActor` inside `onMount`
262
+
263
+ `useActor` uses `$effect` runes internally. Runes must be initialized during synchronous component setup, not in deferred callbacks.
265
264
 
266
265
  ```typescript
267
- import { createClient } from "@rivetkit/svelte";
266
+ // BAD
267
+ onMount(() => {
268
+ const actor = useActor({ name: "counter", key: ["test"] });
269
+ });
268
270
 
269
- const client = createClient<Registry>("http://localhost:8080");
271
+ // GOOD
272
+ const actor = browser
273
+ ? useActor({ name: "counter", key: ["test"] })
274
+ : undefined;
270
275
  ```
271
276
 
272
- ### createRivetKit(client: Client)
277
+ ### Don't call `useEvent` inside `$effect`
273
278
 
274
- Creates the RivetKit integration with your client.
279
+ `useEvent` manages its own internal effects. Wrapping it in another `$effect` causes duplicate listeners and broken state.
275
280
 
276
281
  ```typescript
277
- import { createRivetKit } from "@rivetkit/svelte";
282
+ // BAD
283
+ $effect(() => {
284
+ actor?.useEvent("newCount", (x) => { count = x; });
285
+ });
278
286
 
279
- const { useActor } = createRivetKit(client);
287
+ // GOOD
288
+ actor?.useEvent("newCount", (x) => { count = x; });
280
289
  ```
281
290
 
282
- ### useActor(options: ActorOptions)
283
-
284
- Connects to a RivetKit actor and returns reactive state.
285
-
286
- **Parameters:**
287
- - `name: string` - The actor name from your registry
288
- - `key: string | string[]` - Unique identifier for the actor instance
289
- - `params?: Record<string, string>` - Optional parameters to pass to the actor
290
- - `enabled?: boolean` - Whether the connection is enabled (default: true)
291
-
292
- **Returns:**
293
- - `connection` - Actor connection for calling actions
294
- - `handle` - Actor handle for advanced operations
295
- - `isConnected` - Connection status
296
- - `isConnecting` - Loading state
297
- - `isError` - Error state
298
- - `error` - Error object
299
- - `useEvent` - Function to listen for events
300
-
301
- ## TypeScript Support
291
+ ### Don't call `.connect()` on the connection
302
292
 
303
- RivetKit Svelte provides full TypeScript support. Make sure to type your registry:
293
+ The connection is automatically managed by `useActor`. Calling `.connect()` sends an RPC action named "connect" to your actor, which doesn't exist.
304
294
 
305
295
  ```typescript
306
- // backend/index.ts
307
- export type Registry = typeof registry;
296
+ // BAD
297
+ await actor?.current?.connection?.connect();
308
298
 
309
- // frontend/actor-client.ts
310
- import type { Registry } from "../../backend";
311
- const client = createClient<Registry>("http://localhost:8080");
299
+ // GOOD — just call actions directly
300
+ await actor?.current?.connection?.increment(1);
312
301
  ```
313
302
 
314
- ## SvelteKit Integration
315
-
316
- RivetKit Svelte works seamlessly with SvelteKit. The library automatically detects browser environment and handles SSR appropriately.
317
-
318
- ```svelte
319
- <!-- +layout.svelte -->
320
- <script lang="ts">
321
- import { useActor } from "$lib/actor-client";
322
-
323
- // This will only connect in the browser
324
- const globalActor = useActor({ name: 'global', key: ['app'] });
325
- </script>
326
- ```
303
+ ---
327
304
 
328
- ## Examples
305
+ ## TODO
329
306
 
330
- Check out the `examples` folder in this repository for a complete working example with:
331
- - Backend RivetKit server setup
332
- - Frontend Svelte integration
333
- - Real-time counter with events
334
- - TypeScript configuration
307
+ - [ ] Figure out using `useActor` and `rivetClient` in SSR mode, maybe using context?
335
308
 
336
- ## Contributing
309
+ ## License
337
310
 
338
- Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
311
+ MIT
339
312
 
340
- ## License
313
+ ---
341
314
 
342
- MIT License - see LICENSE file for details.
315
+ Inspired by the Rivet core implementation for React and Next.js.
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
- export * from "./rivet.svelte.js";
1
+ export * from './svelte';
2
+ export * from './sveltekit';
package/dist/index.js CHANGED
@@ -1 +1,2 @@
1
- export * from "./rivet.svelte.js";
1
+ export * from './svelte';
2
+ export * from './sveltekit';
@@ -0,0 +1 @@
1
+ export * from "./rivet.svelte.js";
@@ -0,0 +1 @@
1
+ export * from "./rivet.svelte.js";
@@ -1,5 +1,5 @@
1
- import type { ActorConn, ActorHandle, AnyActorDefinition, Client, ExtractActorsFromRegistry } from "@rivetkit/core/client";
2
1
  import { type ActorOptions, type AnyActorRegistry, type CreateRivetKitOptions } from "@rivetkit/framework-base";
2
+ import type { ActorConn, ActorHandle, AnyActorDefinition, Client, ExtractActorsFromRegistry } from "rivetkit/client";
3
3
  export interface ActorStateReference<AD extends AnyActorDefinition> {
4
4
  /**
5
5
  * The unique identifier for the actor.
@@ -61,12 +61,36 @@ export interface ActorStateReference<AD extends AnyActorDefinition> {
61
61
  enabled?: boolean;
62
62
  };
63
63
  }
64
- export { createClient } from "@rivetkit/core/client";
65
- export declare function createRivetKit<Registry extends AnyActorRegistry>(client: Client<Registry>, opts?: CreateRivetKitOptions<Registry>): {
64
+ import { createClient } from "rivetkit/client";
65
+ export { createClient } from "rivetkit/client";
66
+ export declare function createRivetKit<Registry extends AnyActorRegistry>(clientInput?: Parameters<typeof createClient>[0], opts?: CreateRivetKitOptions<Registry>): {
66
67
  useActor: <ActorName extends keyof ExtractActorsFromRegistry<Registry>>(opts: ActorOptions<Registry, ActorName>) => {
67
- current: Omit<ActorStateReference<ExtractActorsFromRegistry<Registry>>, "handle" | "connection"> & {
68
- handle: ActorHandle<ExtractActorsFromRegistry<Registry>[ActorName]> | null;
69
- connection: ActorConn<ExtractActorsFromRegistry<Registry>[ActorName]> | null;
68
+ current: {
69
+ connect(): void;
70
+ readonly connection: any;
71
+ readonly handle: any;
72
+ readonly isConnected: boolean;
73
+ readonly isConnecting: boolean;
74
+ readonly isError: boolean;
75
+ readonly error: any;
76
+ readonly opts: any;
77
+ readonly hash: any;
78
+ };
79
+ useEvent: (eventName: string, handler: (...args: any[]) => void) => void;
80
+ };
81
+ };
82
+ export declare function createRivetKitWithClient<Registry extends AnyActorRegistry>(client: Client<Registry>, opts?: CreateRivetKitOptions<Registry>): {
83
+ useActor: <ActorName extends keyof ExtractActorsFromRegistry<Registry>>(opts: ActorOptions<Registry, ActorName>) => {
84
+ current: {
85
+ connect(): void;
86
+ readonly connection: any;
87
+ readonly handle: any;
88
+ readonly isConnected: boolean;
89
+ readonly isConnecting: boolean;
90
+ readonly isError: boolean;
91
+ readonly error: any;
92
+ readonly opts: any;
93
+ readonly hash: any;
70
94
  };
71
95
  useEvent: (eventName: string, handler: (...args: any[]) => void) => void;
72
96
  };
@@ -1,7 +1,10 @@
1
1
  import { createRivetKit as createVanillaRivetKit, } from "@rivetkit/framework-base";
2
- import { useStore } from '@tanstack/svelte-store';
3
- export { createClient } from "@rivetkit/core/client";
4
- export function createRivetKit(client, opts = {}) {
2
+ import { createClient } from "rivetkit/client";
3
+ export { createClient } from "rivetkit/client";
4
+ export function createRivetKit(clientInput = undefined, opts = {}) {
5
+ return createRivetKitWithClient(createClient(clientInput), opts);
6
+ }
7
+ export function createRivetKitWithClient(client, opts = {}) {
5
8
  const { getOrCreateActor } = createVanillaRivetKit(client, opts);
6
9
  /**
7
10
  * Svelte 5 rune-based function to connect to an actor and retrieve its state.
@@ -12,63 +15,73 @@ export function createRivetKit(client, opts = {}) {
12
15
  * @returns An object containing reactive state and event listener function.
13
16
  */
14
17
  function useActor(opts) {
15
- const { mount, setState, state } = getOrCreateActor(opts);
18
+ const { mount, state } = getOrCreateActor(opts);
16
19
  // Update options reactively
17
- $effect.root(() => {
18
- setState((prev) => {
19
- prev.opts = {
20
- ...opts,
21
- enabled: opts.enabled ?? true,
22
- };
23
- return prev;
24
- });
25
- });
20
+ // $effect.root(() => {
21
+ // setState((prev) => {
22
+ // prev.opts = {
23
+ // ...opts,
24
+ // enabled: opts.enabled ?? true,
25
+ // } as any
26
+ // return prev
27
+ // })s
28
+ // })
26
29
  // Mount and subscribe to state changes
27
30
  $effect.root(() => {
28
31
  mount();
29
32
  });
30
- const actorState = useStore(state);
31
- function useEvent(eventName,
32
- // biome-ignore lint/suspicious/noExplicitAny: strong typing of handler is not supported yet
33
- handler) {
33
+ let actorState = $state(undefined);
34
+ const unsubscribe = state?.subscribe((res) => {
35
+ actorState = res.currentVal;
36
+ });
37
+ $effect(() => {
38
+ return () => unsubscribe?.();
39
+ });
40
+ function useEvent(eventName, handler) {
34
41
  let ref = $state(handler);
35
- const actorState = useStore(state) || {};
42
+ let actorState = $state(undefined);
43
+ state.subscribe((s) => {
44
+ actorState = s.currentVal;
45
+ });
36
46
  $effect(() => {
37
47
  ref = handler;
38
48
  });
39
49
  $effect(() => {
40
- if (!actorState?.current?.connection)
50
+ if (!actorState?.connection)
41
51
  return;
42
52
  function eventHandler(...args) {
43
53
  ref(...args);
44
54
  }
45
- return actorState.current.connection.on(eventName, eventHandler);
55
+ return actorState.connection?.on(eventName, eventHandler);
46
56
  });
47
57
  }
48
58
  const current = {
59
+ connect() {
60
+ actorState?.connection?.connect();
61
+ },
49
62
  get connection() {
50
- return actorState.current?.connection;
63
+ return actorState?.connection;
51
64
  },
52
65
  get handle() {
53
- return actorState.current?.handle;
66
+ return actorState?.handle;
54
67
  },
55
68
  get isConnected() {
56
- return actorState.current?.isConnected;
69
+ return actorState?.connStatus === "connected";
57
70
  },
58
71
  get isConnecting() {
59
- return actorState.current?.isConnecting;
72
+ return actorState?.connStatus === "connecting";
60
73
  },
61
74
  get isError() {
62
- return actorState.current?.isError;
75
+ return !!actorState?.error;
63
76
  },
64
77
  get error() {
65
- return actorState.current?.error;
78
+ return actorState?.error;
66
79
  },
67
80
  get opts() {
68
- return actorState.current?.opts;
81
+ return actorState?.opts;
69
82
  },
70
83
  get hash() {
71
- return actorState.current?.hash;
84
+ return actorState?.hash;
72
85
  },
73
86
  };
74
87
  return {
@@ -0,0 +1,15 @@
1
+ import type { RequestHandler } from "@sveltejs/kit";
2
+ import type { Registry } from "rivetkit";
3
+ export declare const createRivetKitHandler: (opts?: {
4
+ registry: Registry<any>;
5
+ isDev: boolean;
6
+ rivetSiteUrl?: string;
7
+ }) => {
8
+ GET: RequestHandler;
9
+ POST: RequestHandler;
10
+ PUT: RequestHandler;
11
+ DELETE: RequestHandler;
12
+ PATCH: RequestHandler;
13
+ HEAD: RequestHandler;
14
+ OPTIONS: RequestHandler;
15
+ };
@@ -0,0 +1,62 @@
1
+ import { getLogger } from "rivetkit/log";
2
+ import { PUBLIC_RIVET_ENDPOINT } from "$env/static/public";
3
+ const _devRunnerVersion = Math.floor(Date.now() / 1000);
4
+ const _logger = getLogger("driver-sveltekit");
5
+ const handler = async (request, opts) => {
6
+ const _requestUrl = new URL(request.url);
7
+ const rivetSiteUrl = opts?.rivetSiteUrl ?? PUBLIC_RIVET_ENDPOINT;
8
+ if (!rivetSiteUrl) {
9
+ throw new Error('PUBLIC_RIVET_ENDPOINT environment variable is not set');
10
+ }
11
+ const registry = opts?.registry;
12
+ if (!registry) {
13
+ throw new Error("registry is not set");
14
+ }
15
+ registry.config.serveManager = false;
16
+ registry.config.serverless = {
17
+ ...registry.config.serverless,
18
+ basePath: "/api/rivet",
19
+ };
20
+ if (opts?.isDev) {
21
+ _logger.debug("detected development environment, auto-starting engine and auto-configuring serverless");
22
+ // Set these on the registry's config directly since the legacy inputConfig
23
+ // isn't used by the serverless router
24
+ registry.config.serverless.spawnEngine = true;
25
+ registry.config.serverless.configureRunnerPool = {
26
+ url: `${rivetSiteUrl}/api/rivet`,
27
+ minRunners: 0,
28
+ maxRunners: 100_000,
29
+ requestLifespan: 300,
30
+ slotsPerRunner: 1,
31
+ metadata: { provider: "sveltekit" },
32
+ };
33
+ // Set runner version to enable hot-reloading on code changes
34
+ registry.config.runner = {
35
+ ...registry.config.runner,
36
+ version: _devRunnerVersion,
37
+ };
38
+ }
39
+ else {
40
+ _logger.debug("detected production environment, will not auto-start engine and auto-configure serverless");
41
+ }
42
+ const newUrl = `${rivetSiteUrl}${_requestUrl.pathname}`;
43
+ const newRequest = new Request(newUrl, request);
44
+ newRequest.headers.set("host", new URL(newUrl).host);
45
+ newRequest.headers.set("accept-encoding", "application/json");
46
+ return await registry.handler(newRequest);
47
+ // return fetch(newRequest, { method: request.method, redirect: "manual" })
48
+ };
49
+ export const createRivetKitHandler = (opts) => {
50
+ const requestHandler = async ({ request }) => {
51
+ return handler(request, opts);
52
+ };
53
+ return {
54
+ GET: requestHandler,
55
+ POST: requestHandler,
56
+ PUT: requestHandler,
57
+ DELETE: requestHandler,
58
+ PATCH: requestHandler,
59
+ HEAD: requestHandler,
60
+ OPTIONS: requestHandler,
61
+ };
62
+ };
@@ -0,0 +1 @@
1
+ export * from './handler.server';
@@ -0,0 +1 @@
1
+ export * from './handler.server';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blujosi/rivetkit-svelte",
3
- "version": "0.0.7",
3
+ "version": "2.1.0",
4
4
  "scripts": {
5
5
  "build": "vite build && npm run prepack",
6
6
  "preview": "vite preview",
@@ -17,37 +17,45 @@
17
17
  "sideEffects": [
18
18
  "**/*.css"
19
19
  ],
20
- "svelte": "./dist/index.js",
21
- "types": "./dist/index.d.ts",
20
+ "svelte": "./dist/svelte/index.js",
21
+ "types": "./dist/svelte/index.d.ts",
22
22
  "type": "module",
23
23
  "exports": {
24
24
  ".": {
25
- "types": "./dist/index.d.ts",
26
- "svelte": "./dist/index.js"
25
+ "types": "./dist/svelte/index.d.ts",
26
+ "svelte": "./dist/svelte/index.js",
27
+ "default": "./dist/svelte/index.js"
28
+ },
29
+ "./svelte": {
30
+ "types": "./dist/svelte/index.d.ts",
31
+ "svelte": "./dist/svelte/index.js",
32
+ "default": "./dist/svelte/index.js"
33
+ },
34
+ "./sveltekit": {
35
+ "types": "./dist/sveltekit/index.d.ts",
36
+ "svelte": "./dist/sveltekit/index.js"
27
37
  }
28
38
  },
29
39
  "peerDependencies": {
30
40
  "svelte": "^5.0.0"
31
41
  },
32
42
  "devDependencies": {
33
- "@sveltejs/adapter-auto": "^6.1.0",
34
- "@sveltejs/kit": "^2.37.0",
35
- "@sveltejs/package": "^2.5.0",
36
- "@sveltejs/vite-plugin-svelte": "^6.1.4",
37
- "publint": "^0.3.12",
38
- "svelte": "^5.38.7",
39
- "svelte-check": "^4.3.1",
40
- "typescript": "^5.9.2",
41
- "vite": "^7.1.4"
43
+ "@rivetkit/framework-base": "^2.1.3",
44
+ "@sveltejs/adapter-auto": "^7.0.1",
45
+ "@sveltejs/kit": "^2.53.4",
46
+ "@sveltejs/package": "^2.5.7",
47
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
48
+ "publint": "^0.3.18",
49
+ "svelte": "^5.53.6",
50
+ "svelte-check": "^4.4.4",
51
+ "typescript": "^5.9.3",
52
+ "vite": "^7.3.1"
42
53
  },
43
54
  "keywords": [
44
55
  "svelte"
45
56
  ],
46
57
  "dependencies": {
47
- "@rivetkit/actor": "^2.0.2",
48
- "@rivetkit/core": "^2.0.2",
49
- "@rivetkit/framework-base": "^2.0.2",
50
- "@tanstack/svelte-store": "^0.7.4",
51
- "esm-env": "^1.2.2"
58
+ "esm-env": "^1.2.2",
59
+ "rivetkit": "^2.1.3"
52
60
  }
53
- }
61
+ }