Skip to content

@xmachines/play-solid-router-demo

Examples / @xmachines/play-solid-router-demo

SolidJS Router adapter demonstration with authentication flow using the XMachines Play architecture and provider-based router integration.

What This Demonstrates

  • Shared auth machine reused without framework-specific business logic
  • PlayRouterProvider renderer-based integration with Solid Router
  • Shell-driven rendering via PlayRenderer with actor-authoritative navigation
  • Canonical TC39 Signals lifecycle integration with Solid’s fine-grained reactivity
  • 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-solid-router-demo

Then open http://localhost:3002.

Step-by-Step Code Flow

Use this order to understand the implementation:

  1. src/main.tsx bootstraps the demo and mounts <App /> onto #app.
  2. src/runtime.ts sets up the actor, registry, routes, and route map — all shared module-level singletons.
  3. src/App.tsx wires Solid Router routes and the PlayRouterProvider using the runtime exports.
  4. A routeMap is created by calling createRouteMap(authMachine) in src/runtime.ts.
  5. PlayRouterProvider forwards navigation intent from @solidjs/router to the actor, and reflects actor-approved routes back to the URL.
  6. Shell (from @xmachines/play-solid-demo) renders actor-projected views (PlayRenderer) and provides the shared UI shell.
  7. test/library-pattern.test.ts and test/browser/ verify invariants and runtime routing behavior.
// src/main.tsx (shape)
render(() => <App />, document.getElementById("app")!);
// src/runtime.ts (shape)
const createPlayer = definePlayer({ machine: authMachine });
export const actor = createPlayer();
actor.start();
export const routeMap = createRouteMap(authMachine);
// src/App.tsx (shape)
const Layout: ParentComponent = () => {
const navigate = useNavigate();
const location = useLocation();
const params = useParams();
return (
<PlayRouterProvider
actor={actor}
routeMap={routeMap}
router={{ navigate, location, params }}
renderer={(currentActor, currentRouter) => (
<Shell
actor={currentActor}
router={currentRouter}
registryResult={registryResult}
/>
)}
/>
);
};

Key Files

  • src/runtime.ts - actor lifecycle, registry, route extraction, and route map creation
  • src/App.tsx - Solid Router wiring, PlayRouterProvider integration, and renderer composition
  • src/main.tsx - Vite bootstrap that mounts <App /> via Solid’s render
  • test/library-pattern.test.ts - architecture boundary and invariant assertions
  • test/browser/shared-demo.browser.test.ts - browser startup and full auth flow coverage

State Machine & Architecture Details

The demo utilizes XMachines architectural invariants:

  1. Actor Authority: When a user navigates to a protected route, the Solid Router updates the URL. The PlayRouterProvider intercepts this, translates it to a play.route event, and sends it to the actor. The actor evaluates guards (e.g., is the user logged in?) and transitions to the appropriate state.
  2. Passive Infrastructure: Solid components hold no business state. They observe actor.currentView to know what to render, and actor.currentRoute to reflect the active path.
  3. Signal-Only Reactivity: Instead of React-like re-renders, Solid uses createEffect internally within the RouterBridge to react precisely when signals update.

Watcher Lifecycle and Cleanup Contract

This demo follows the canonical watcher lifecycle:

  1. notify
  2. queueMicrotask
  3. getPending()
  4. Read actor signals and update Solid-local render triggers
  5. Re-arm with watch()/watch(...signals)

Watcher notifications are one-shot. Cleanup is explicit and lifecycle-bound: Solid teardown uses onCleanup, and provider/bridge teardown must call disconnect/unwatch paths rather than relying on GC-only cleanup. This is crucial to prevent memory leaks during component unmounting.

Adapter Boundaries

Solid Router integration remains passive infrastructure. RouterBridgeBase stays the shared policy point; the Solid adapter only implements framework port behavior (calling navigate() and tracking location.pathname).

Available Scripts

These commands are defined in package.json:

CommandDescription
npm run dev -w @xmachines/play-solid-router-demoStart Vite dev server
npm run build -w @xmachines/play-solid-router-demoBuild production bundle
npm run preview -w @xmachines/play-solid-router-demoPreview built bundle
npm run test -w @xmachines/play-solid-router-demoRun Vitest test suite
npm run test:browser -w @xmachines/play-solid-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-solid-router-demo
npm run test:browser -w @xmachines/play-solid-router-demo

Expected result: library-pattern invariant tests and the browser shared-demo suite both pass, confirming login/logout transitions update both view and URL correctly.

Learn More