@xmachines/play-sveltekit-router-demo
Examples / @xmachines/play-sveltekit-router-demo
Svelte 5 + SvelteKit router adapter integration demo for the XMachines Play architecture using browser pathname URL routing.
What This Demonstrates
- Shared auth machine reused without framework-specific business logic
connectRouterpathname URL ↔ actor sync via@xmachines/play-sveltekit-router- Shell-driven rendering via
PlayUIProvider+PlayRendererwith actor-authoritative navigation - Canonical TC39 Signals lifecycle integrated with Svelte 5 runes
- Non-browser invariant tests plus browser E2E coverage
Running the Demo
From the repository root:
npm installnpm run dev -w @xmachines/play-sveltekit-router-demoThen open http://localhost:5173.
Step-by-Step Code Flow
Use this order to understand the implementation:
src/main.tsmountsApp.svelteand registers HMR cleanup viacleanup.src/runtime.tscreates the actor, builds the registry, callscreateRouteMap(authMachine), and wiresconnectRouter({ actor, routeMap }).connectRoutersynchronizes browser pathname changes with the actor viaplay.routeevents and reflects actor-approved routes back to the pathname URL.src/App.svelteimports the singletonactorandregistryResultfromsrc/runtime.tsand delegates to the sharedShell.svelte.Shell.svelterenders<PlayUIProvider>+<PlayRenderer />with the actor and registryResult and provides the shared UI shell (nav, debug panel).- Browser tests in
test/browser/validate startup and auth route transitions.
// src/runtime.ts (shape)const createDemoPlayer = definePlayer({ machine: authMachine });export const actor = createDemoPlayer();actor.start();
export const routeMap = createRouteMap(authMachine);export const disconnectRouter = connectRouter({ actor, routeMap });
export function cleanup(): void { disconnectRouter(); actor.stop();}<!-- src/App.svelte (shape) --><script lang="ts"> import { Shell } from "@xmachines/play-svelte-demo"; import { actor, registryResult } from "./runtime.js";</script>
<Shell {actor} {registryResult} /><!-- Shell.svelte — rendering (shape) --><PlayUIProvider actor={props.actor} registryResult={props.registryResult}> <PlayRenderer /></PlayUIProvider>Key Files
src/main.ts- Svelte app mount and HMR cleanup registrationsrc/runtime.ts- actor creation, registry construction, route map, andconnectRouterwiringsrc/lib/navigation.ts- History API shim implementing SvelteKit’s$app/navigationcontract for Vite dev and production buildssrc/app-navigation.d.ts- TypeScript ambient declaration for the$app/navigationmodulesrc/App.svelte- minimal root component delegating to sharedShell.sveltetest/library-pattern.test.ts- architecture boundary and invariant assertionstest/browser/shared-demo.browser.test.ts- browser startup and auth route flow coverage
State Machine & Architecture Details
The demo utilizes XMachines architectural invariants:
- Actor Authority: When a user clicks a nav link, the browser pathname changes.
connectRouterintercepts this, translates it to aplay.routeevent, and sends it to the actor. The actor evaluates guards (e.g.isAuthenticated) and transitions to the appropriate state. - Passive Infrastructure: The SvelteKit router adapter does not execute business logic. The actor dictates whether navigation is permitted. The Svelte application only renders the state.
- Signal-Only Reactivity: The bridge leverages Svelte 5 rune reactivity internally to react precisely when actor signals update, without ad-hoc
$statestores for business logic.
Watcher Lifecycle and Cleanup Contract
This demo follows the canonical watcher lifecycle used across all @xmachines framework adapters:
notifyqueueMicrotaskgetPending()- Read actor signals and project Svelte-local render state
- Re-arm with
watch()/watch(...signals)
Watcher notifications are one-shot. Cleanup is explicit: cleanup() calls disconnectRouter() and actor.stop() during HMR disposal, preventing memory leaks and ghost subscriptions.
Adapter Boundaries
connectRouter (from @xmachines/play-sveltekit-router) is passive infrastructure. It translates pathname URL changes into play.route events and reflects actor-approved route changes back to the browser pathname. Business validity remains actor-owned. The shared Shell.svelte from @xmachines/play-svelte-demo is reused to avoid duplicating view components across router demos.
The $app/navigation module is shimmed by src/lib/navigation.ts using the History API, so the demo runs outside a full SvelteKit project (plain Vite dev server and production build).
Available Scripts
These commands are defined in package.json:
| Command | Description |
|---|---|
npm run dev -w @xmachines/play-sveltekit-router-demo | Start Vite dev server |
npm run build -w @xmachines/play-sveltekit-router-demo | Build production bundle |
npm run preview -w @xmachines/play-sveltekit-router-demo | Preview built bundle |
npm run test -w @xmachines/play-sveltekit-router-demo | Run Vitest test suite |
npm run test:browser -w @xmachines/play-sveltekit-router-demo | Run browser-focused Vitest suite |
Verification
Use these checks to validate README claims against the current demo implementation:
npm run test -w @xmachines/play-sveltekit-router-demonpm run test:browser -w @xmachines/play-sveltekit-router-demoExpected result: library-pattern invariant tests pass and the browser demo suite validates startup rendering and auth navigation.