Client SDK
cloudbed/client is the browser half: Preact hooks wired to your server's queries and mutations over a WebSocket, plus built-in auth and a small router. The client entry (client/index.tsx) renders into #app:
import { render } from "preact";
import { useAuth, useMutation, useQuery } from "cloudbed/client";
function App() {
const auth = useAuth();
const todos = useQuery("todos") ?? [];
const addTodo = useMutation("addTodo");
return (
<div>
<p>Welcome, {auth.displayName}</p>
<button onClick={() => addTodo("New task")}>Add</button>
<ul>{todos.map((t) => <li key={t.id}>{t.text}</li>)}</ul>
</div>
);
}
render(<App />, document.getElementById("app")!);
Data hooks
useQuery(name)
Subscribe to a server query by name. Returns undefined until the first result arrives, then the query's value — and re-renders automatically whenever a mutation changes the underlying data. Live updates are pushed by the server; there is nothing to invalidate or refetch.
useMutation(name)
Returns a stable async function that runs the named server mutation with whatever arguments you pass. On failure it rejects with a TransportError carrying { code, message } (the message of any Error your mutation threw).
const addTodo = useMutation("addTodo");
try {
await addTodo(text);
} catch (err) {
setError(err.message);
}
Auth
Every capsule gets "Sign in with Google" with zero registration. Tokens are scoped to your app's origin and user ids are pairwise per app, so capsules can't correlate a user across apps. Before sign-in, users are guests (guest:local, or guest:<name> via a ?guest=<name> URL parameter) — ctx.auth.userId works either way on the server.
useAuth()— current auth state:userId,displayName,provider("guest"|"google"),isGuest,isAuthenticated,isLoading, plusemail/emailVerified/picturewhen signed in.<SignInWithGoogle />— a ready-made button; acceptsclassNameand customchildren, disables itself while auth is loading, and renders nothing if sign-in isn't configured for the deploy.signInWithGoogle()— start the sign-in flow imperatively (redirects to the auth broker and back).signOut()— drop the identity and reconnect as a guest.isSignInConfigured()— whether the deploy has sign-in available.
The redirect round-trip is handled for you: the SDK completes the flow on /auth/callback and returns the user to the page they started from.
Router
A minimal client-side router, included so a capsule needs no other dependencies:
import { Link, Route, Router, Routes, useParams } from "cloudbed/client";
function App() {
return (
<Router>
<nav><Link to="/">Home</Link></nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/todo/:id" element={<Todo />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Router>
);
}
function Todo() {
const { id } = useParams();
// ...
}
<Router>wraps the app;<Routes>renders the first matching non-wildcard<Route>, falling back topath="*"when nothing else matches.<Link to>navigates without a reload (and correctly defers to modifier-clicks and middle-clicks).useParams()— captured:paramvalues;useLocation()— current{ pathname, search, hash }.useNavigate()in components — or the module-levelnavigate(to, { replace? })outside them.