Skip to content

@xmachines/play-actor

API / @xmachines/play-actor

Abstract Actor base class for XMachines Play Architecture.

Part of the xmachines-js monorepo.

Installation

Terminal window
npm install @xmachines/play-actor

Peer dependencies — install alongside the package:

Terminal window
npm install xstate @xmachines/play @xmachines/play-signals

Overview

@xmachines/play-actor provides AbstractActor, a minimal base class that extends the XState Actor class while enforcing the Play Architecture’s signal protocol (RFC section 5.3). It exposes reactive TC39 Signals for infrastructure-layer communication while preserving full XState ecosystem compatibility (devtools, inspection).

The core protocol is deliberately minimal:

PropertyTypeDescription
stateSignal.State<unknown>Reactive snapshot of current actor state
send(event: TEvent) => voidEvent dispatch method

Optional capabilities are declared as separate interfaces — a concrete actor opts in only to what it needs:

InterfacePropertyDescription
RoutablecurrentRoute: Signal.Computed<string | null>Current route path derived from state
RoutableinitialRoute: string | nullRoute the actor starts on
ViewablecurrentView: Signal.State<PlaySpec | null>Current JSON-render view spec

Concrete implementations are created by adapters such as @xmachines/play-xstate.

API Summary

AbstractActor<TLogic, TEvent>

Abstract base class extending XState Actor<TLogic>.

import { AbstractActor } from "@xmachines/play-actor";
import { Signal } from "@xmachines/play-signals";
import type { AnyActorLogic } from "xstate";
class MyActor extends AbstractActor<AnyActorLogic> {
// Required: reactive state signal
state = new Signal.State({});
// Required: typed event dispatch
send = (event: { type: string }) => {
/* dispatch to XState */
};
}

With a typed event union:

type AuthEvent = { type: "auth.login"; username: string } | { type: "auth.logout" };
class AuthActor extends AbstractActor<AnyActorLogic, AuthEvent> {
state = new Signal.State({ isAuthenticated: false, username: null });
send = (event: AuthEvent) => {
/* dispatch */
};
}

typedSpec<TContext>(spec)

Identity helper that constrains a PlaySpec object’s contextProps to keys of a specific machine context type. This enables compile-time validation and IDE autocomplete without any runtime cost.

import { typedSpec } from "@xmachines/play-actor";
interface DashboardCtx {
username: string;
params: Record<string, string>;
query: Record<string, string>;
}
// In an XState machine meta block:
meta: {
view: typedSpec<DashboardCtx>({
root: "root",
contextProps: ["username"], // ✓ key of DashboardCtx
// contextProps: ["usernaem"], // ✗ compile error
elements: {
root: { type: "Dashboard", props: {}, children: [] },
},
}),
}

PlaySpec

Extends @json-render/core’s Spec with an optional contextProps field — an explicit allowlist of machine context fields that are merged into element props at view derivation time.

import type { PlaySpec } from "@xmachines/play-actor";
const spec: PlaySpec = {
root: "root",
contextProps: ["username"], // only these keys are exposed to components
elements: {
root: { type: "Profile", props: { username: undefined }, children: [] },
},
};

Routable

Interface for actors that support routing.

import type { Routable } from "@xmachines/play-actor";
import { Signal } from "@xmachines/play-signals";
// Implement in a concrete actor (note: RoutableActor interface is exported from @xmachines/play-router):
class MyRoutableActor extends AbstractActor<AnyActorLogic> implements Routable {
state = new Signal.State({});
currentRoute = new Signal.Computed(() => this.state.get().path ?? null);
initialRoute = "/";
send = (event) => {
/* dispatch */
};
}

Viewable

Interface for actors that expose a renderable view signal.

import type { Viewable } from "@xmachines/play-actor";
import type { PlaySpec } from "@xmachines/play-actor";
import { Signal } from "@xmachines/play-signals";
// currentView carries PlaySpec | null
const signal = new Signal.State<PlaySpec | null>(null);
const viewable: Viewable = { currentView: signal };

BaseActorProviderProps<TRegistry>

Framework-agnostic base props shared by every ActorProvider implementation (React, Vue, Solid, Svelte). Framework renderer packages extend this interface.

import type { BaseActorProviderProps } from "@xmachines/play-actor";
import type { DefineRegistryResult } from "@json-render/react";
interface ActorProviderProps extends BaseActorProviderProps<DefineRegistryResult> {
fallback?: React.ReactNode;
children: React.ReactNode;
}

BaseViewContextValue<TRegistry>

Framework-agnostic base for every framework’s ViewContextValue. Holds spec, handlers, registry, and store fields that are identical across React, Vue, Solid, and Svelte.

Testing

Run the test suite for this package in isolation:

Terminal window
# From the package directory
npm test
# From the monorepo root (workspace-scoped)
npm test -w packages/play-actor
# Watch mode
npm run test:watch -w packages/play-actor

Requirements

  • Node.js >=22.0.0
  • TypeScript >=5.7 (strict mode)
  • ESM only"type": "module"

@xmachines/play-actor - Abstract Actor base class for Play Architecture

This package provides AbstractActor, a minimal base class that extends XState Actor while enforcing the Play Architecture’s signal protocol (RFC section 5.3).

The core protocol is minimal (state + send). Optional capabilities are provided via interfaces:

  • Routable: For actors that support routing
  • Viewable: For actors that support view rendering

Maintains XState ecosystem compatibility (inspection, devtools) while exposing reactive signals for Infrastructure layer communication.

See

Play RFC

Classes

Interfaces

Functions