Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/frameworks/svelte/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# RivetKit Svelte

_Lightweight Libraries for Backends_

Built for Svelte 5 using runes.

[Learn More →](https://github.com/rivet-gg/rivetkit)

[Discord](https://rivet.gg/discord) — [Documentation](https://rivetkit.org) — [Issues](https://github.com/rivet-gg/rivetkit/issues)

## License

Apache 2.0
44 changes: 44 additions & 0 deletions packages/frameworks/svelte/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "@rivetkit/svelte",
"version": "0.1.0",
"license": "Apache-2.0",
"type": "module",
"keywords": [
"rivetkit",
"svelte",
"runes",
"frontend",
"ui"
],
"sideEffects": false,
"files": [
"dist",
"src/lib"
],
"svelte": "./dist/index.js",
"exports": {
".": {
"types": "./dist/index.d.ts",
"svelte": "./dist/index.js"
}
},
"scripts": {
"build": "svelte-package",
"check-types": "tsc --noEmit"
},
"dependencies": {
"@rivetkit/framework-base": "workspace:*",
"@tanstack/store": "^0.7.1",
"@tanstack/svelte-store": "^0.7.1"
},
"peerDependencies": {
"svelte": "^5.0.0",
"@rivetkit/core": "*"
},
"devDependencies": {
"@rivetkit/core": "workspace:^",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/package": "^2.0.0",
"typescript": "^5.5.2"
}
}
114 changes: 114 additions & 0 deletions packages/frameworks/svelte/src/lib/mod.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Requires Svelte 5 and runes enabled
import { useStore } from "@tanstack/svelte-store";
import {
type AnyActorRegistry,
type CreateRivetKitOptions,
type ActorOptions,
createRivetKit as createVanillaRivetKit,
} from "@rivetkit/framework-base";
import type { Client, ExtractActorsFromRegistry } from "@rivetkit/core/client";

export { createClient } from "@rivetkit/core/client";

/**
* Creates a RivetKit instance for Svelte 5 using runes.
* @param client - The RivetKit client
* @param opts - Optional configuration
*/
export function createRivetKit<Registry extends AnyActorRegistry>(
client: Client<Registry>,
opts: CreateRivetKitOptions<Registry> = {}
) {
const { getOrCreateActor } = createVanillaRivetKit<
Registry,
ExtractActorsFromRegistry<Registry>,
keyof ExtractActorsFromRegistry<Registry>
>(client, opts);

/**
* Hook to connect to a actor and retrieve its state. Using this hook with the same options
* will return the same actor instance. This simplifies passing around the actor state in your components.
* It also provides a method to listen for events emitted by the actor.
* @param opts - Options for the actor, including its name, key, and parameters.
* @returns An object containing the actor's state and a method to listen for events.
*/
function useActor<ActorName extends keyof ExtractActorsFromRegistry<Registry>>(
opts: ActorOptions<Registry, ActorName>
) {
const { mount, setState, state } = getOrCreateActor<ActorName>(opts);
$effect(() => {
setState((prev) => {
prev.opts = {
...opts,
enabled: opts.enabled ?? true,
};

return prev;
});
});

// Mount the actor and handle cleanup
$effect.pre(() => {
return mount();
});

// Use TanStack Svelte store to get reactive state
const actorState = useStore(state) || {};

/**
* Hook to listen for events emitted by the actor.
* This hook allows you to subscribe to specific events emitted by the actor and execute a handler function
* when the event occurs.
* It uses the `$effect` rune to set up the event listener when the actor connection is established.
* It cleans up the listener when the component unmounts or when the actor connection changes.
* @param eventName The name of the event to listen for.
* @param handler The function to call when the event is emitted.
*/
const useEvent = (
eventName: string,
handler: (...args: any[]) => void
) => {
$effect(() => {
// track dependency so that the effect is re-run when the actor connection changes
actorState.current?.isConnected;

if (!actorState.current.connection) return;
return actorState.current.connection.on(eventName, handler);
});
};

return {
get hash() {
return actorState.current.hash;
},
get handle() {
return actorState.current.handle;
},
get connection() {
return actorState.current.connection;
},
get isConnected() {
return actorState.current.isConnected;
},
get isConnecting() {
return actorState.current.isConnecting;
},
get isError() {
return actorState.current.isError;
},
get error() {
return actorState.current.error;
},
get opts() {
return actorState.current.opts;
},
useEvent,
} satisfies typeof actorState.current & {
useEvent: typeof useEvent;
}
}

return {
useActor,
};
}
19 changes: 19 additions & 0 deletions packages/frameworks/svelte/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}
Loading