Skip to content

@xmachines/play-svelte-spa-router-demo

Examples / @xmachines/play-svelte-spa-router-demo

Svelte 5 + svelte-spa-router integration demo for the XMachines Play architecture using hash-based URL routing.

What This Demonstrates

  • Shared auth machine reused without framework-specific business logic
  • connectRouter hash-URL ↔ actor sync via @xmachines/play-svelte-spa-router
  • Shell-driven rendering via PlayRenderer with 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:

Terminal window
npm install
npm run dev -w @xmachines/play-svelte-spa-router-demo

Then open http://localhost:3005.

Step-by-Step Code Flow

Use this order to understand the implementation:

  1. src/main.ts mounts App.svelte and registers HMR cleanup via cleanup.
  2. src/runtime.ts creates the actor, builds the registry, calls createRouteMap(authMachine), and wires connectRouter({ actor, routeMap }).
  3. connectRouter synchronizes hash URL changes with the actor via play.route events and reflects actor-approved routes back to the hash URL.
  4. src/App.svelte imports the singleton actor and registryResult from src/runtime.ts and delegates to the shared Shell.svelte.
  5. Shell.svelte renders <PlayUIProvider> + <PlayRenderer /> with the actor and registryResult and provides the shared UI shell (nav, debug panel).
  6. 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 registration
  • src/runtime.ts - actor creation, registry construction, route map, and connectRouter wiring
  • src/App.svelte - minimal root component delegating to shared Shell.svelte
  • test/library-pattern.test.ts - architecture boundary and invariant assertions
  • test/browser/shared-demo.browser.test.ts - browser startup and auth route flow coverage

State Machine & Architecture Details

The demo utilizes XMachines architectural invariants:

  1. Actor Authority: When a user clicks a nav link, the hash URL changes. connectRouter intercepts this, translates it to a play.route event, and sends it to the actor. The actor evaluates guards (e.g. isAuthenticated) and transitions to the appropriate state.
  2. Passive Infrastructure: svelte-spa-router does not execute business logic. The actor dictates whether navigation is permitted. The Svelte application only renders the state.
  3. Signal-Only Reactivity: The bridge leverages Svelte 5 rune reactivity internally to react precisely when actor signals update, without ad-hoc $state stores for business logic.

Watcher Lifecycle and Cleanup Contract

This demo follows the canonical watcher lifecycle used across all @xmachines framework adapters:

  1. notify
  2. queueMicrotask
  3. getPending()
  4. Read actor signals and project Svelte-local render state
  5. 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-svelte-spa-router) is passive infrastructure. It translates hash URL changes into play.route events and reflects actor-approved route changes back to the URL. Business validity remains actor-owned. The shared Shell.svelte from @xmachines/play-svelte-demo is reused to avoid duplicating view components across router demos.

Available Scripts

These commands are defined in package.json:

CommandDescription
npm run dev -w @xmachines/play-svelte-spa-router-demoStart Vite dev server
npm run build -w @xmachines/play-svelte-spa-router-demoBuild production bundle
npm run preview -w @xmachines/play-svelte-spa-router-demoPreview built bundle
npm run test -w @xmachines/play-svelte-spa-router-demoRun Vitest test suite
npm run test:browser -w @xmachines/play-svelte-spa-router-demoRun browser-focused Vitest suite

Verification

Use these checks to validate README claims against the current demo implementation:

Terminal window
npm run test -w @xmachines/play-svelte-spa-router-demo
npm run test:browser -w @xmachines/play-svelte-spa-router-demo

Expected result: library-pattern invariant tests pass and the browser demo suite validates startup rendering and auth navigation.

Learn More