Skip to content

Class: PlayerActor<TMachine>

Documentation / @xmachines/play-xstate / PlayerActor

Defined in: packages/play-xstate/src/player-actor.ts:120

Concrete XState actor implementing Play Architecture signal protocol

Extends @xmachines/play-actor!AbstractActor to provide XState v5 integration while maintaining ecosystem compatibility (XState inspection, devtools). This actor wraps an internal XState actor and exposes TC39 Signal-based reactive state for Infrastructure observation.

Capabilities: Implements both @xmachines/play-actor!Routable and @xmachines/play-actor!Viewable interfaces, providing routing and view rendering support.

Architectural Context: Implements Actor Authority (INV-01) by ensuring the XState machine’s guards control all navigation decisions. Infrastructure observes the actor’s signals (state, currentRoute, currentView) but cannot directly manipulate state—all mutations flow through the state machine’s event handlers.

Examples

Basic actor creation and lifecycle

import { setup } from "xstate";
import { definePlayer } from "@xmachines/play-xstate";
const machine = setup({}).createMachine({
initial: "idle",
states: {
idle: {
meta: { route: "/", view: { component: "HomePage" } },
},
},
});
const createPlayer = definePlayer({ machine });
const actor = createPlayer();
actor.start();
// Observe signals
console.log(actor.currentRoute.get()); // '/'
console.log(actor.currentView.get()); // { component: 'HomePage' }

Signal lifecycle with watchers (microtask batching)

import { Signal } from "@xmachines/play-signals";
const watcher = new Signal.subtle.Watcher(() => {
queueMicrotask(() => {
const pending = watcher.getPending();
console.log("State changed:", actor.state.get());
});
});
watcher.watch(actor.state);
actor.send({ type: "play.route", to: "#about" });
// Watcher notification scheduled via microtask

See

Remarks

Routing: This actor supports both XState’s route: {} config pattern and play.route events with parameters. The deriveRoute() function checks meta.route (Stately pattern) for URL templates with parameter substitution support.

View Signal Pattern: The currentView signal is a direct Signal.State (not Signal.Computed) to ensure proper watcher propagation in PlayRenderer. Views are cached and updated at state entry, not computed on every read.

Extends

Type Parameters

Type ParameterDescription
TMachine extends AnyStateMachineXState v5 state machine type

Implements

Constructors

Constructor

new PlayerActor<TMachine>(
machine,
catalog,
options,
input?): PlayerActor<TMachine>;

Defined in: packages/play-xstate/src/player-actor.ts:142

Parameters

ParameterType
machineTMachine
catalogCatalog | null | undefined
optionsPlayerOptions<TMachine>
input?InputFrom<TMachine>

Returns

PlayerActor<TMachine>

Overrides

AbstractActor.constructor

Properties

PropertyModifierTypeDescriptionOverridesInherited fromDefined in
_parent?publicAnyActorRef--AbstractActor._parentnode_modules/xstate/dist/declarations/src/createActor.d.ts:33
catalogpublicCatalogComponent catalog for view resolution Maps component names to actual component implementations. Used by renderers to resolve view.component to actual UI components.--packages/play-xstate/src/player-actor.ts:140
clockpublicClockThe clock that is responsible for setting and clearing timeouts, such as delayed events and transitions.-AbstractActor.clocknode_modules/xstate/dist/declarations/src/createActor.d.ts:25
currentRoutepublicComputed<string | null>Current route signal Computed signal derived from state machine. Infrastructure observes to sync browser URL. Invariant: Passive Infrastructure - Infrastructure reflects route, never decides. Example const watcher = new Signal.subtle.Watcher(() => { const route = actor.currentRoute.get(); console.log('Route changed:', route); }); watcher.watch(actor.currentRoute);--packages/play-xstate/src/player-actor.ts:138
currentViewpublicState<ViewMetadata | null>Current view signal State signal containing UI structure schema from meta.view. Infrastructure renders view. Invariant: Logic-Driven UI - View structure is defined by business logic, not JSX. Example const watcher = new Signal.subtle.Watcher(() => { const view = actor.currentView.get(); console.log('View changed:', view); }); watcher.watch(actor.currentView);--packages/play-xstate/src/player-actor.ts:139
idpublicstringThe unique identifier for this actor relative to its parent.-AbstractActor.idnode_modules/xstate/dist/declarations/src/createActor.d.ts:28
logicpublicAnyActorLogic--AbstractActor.logicnode_modules/xstate/dist/declarations/src/createActor.d.ts:18
optionspublicReadonly<ActorOptions<TLogic>>--AbstractActor.optionsnode_modules/xstate/dist/declarations/src/createActor.d.ts:26
refpublicActorRef<any, any, any>--AbstractActor.refnode_modules/xstate/dist/declarations/src/createActor.d.ts:34
sessionIdpublicstringThe globally unique process ID for this invocation.-AbstractActor.sessionIdnode_modules/xstate/dist/declarations/src/createActor.d.ts:38
srcpublic| string | AnyActorLogic--AbstractActor.srcnode_modules/xstate/dist/declarations/src/createActor.d.ts:42
statepublicState<AnyMachineSnapshot>Reactive snapshot of current actor state. Typed as Signal.State<unknown> at the abstract level; concrete implementations narrow this to the actual snapshot type (e.g., Signal.State<AnyMachineSnapshot> in @xmachines/play-xstate’s PlayerActor). Infrastructure observes this signal to react to state changes without directly coupling to the Actor’s internal state machine implementation. Example // Infrastructure observes state signal const watcher = new Signal.subtle.Watcher(() => { console.log('Actor state changed:', actor.state.get()); }); watcher.watch(actor.state);AbstractActor.state-packages/play-xstate/src/player-actor.ts:137
systempublicAnyActorSystemThe system to which this actor belongs.-AbstractActor.systemnode_modules/xstate/dist/declarations/src/createActor.d.ts:40
systemIdpublicstring | undefined--AbstractActor.systemIdnode_modules/xstate/dist/declarations/src/createActor.d.ts:36

Methods

[observable]()

observable: InteropSubscribable<any>;

Defined in: node_modules/xstate/dist/declarations/src/createActor.d.ts:153

Returns

InteropSubscribable<any>

Inherited from

AbstractActor.[observable]


dispose()

dispose(): void;

Defined in: packages/play-xstate/src/player-actor.ts:434

Convenience dispose method for cleanup

Per CONTEXT.md: “Both .dispose() convenience method and manual machine.stop()“

Returns

void


getPersistedSnapshot()

getPersistedSnapshot(): Snapshot<unknown>;

Defined in: node_modules/xstate/dist/declarations/src/createActor.d.ts:152

Obtain the internal state of the actor, which can be persisted.

Returns

Snapshot<unknown>

Remarks

The internal state can be persisted from any actor, not only machines.

Note that the persisted state is not the same as the snapshot from Actor.getSnapshot. Persisted state represents the internal state of the actor, while snapshots represent the actor’s last emitted value.

Can be restored with ActorOptions.state

See

https://stately.ai/docs/persistence

Inherited from

AbstractActor.getPersistedSnapshot


getSnapshot()

getSnapshot(): SnapshotFrom<TMachine>;

Defined in: packages/play-xstate/src/player-actor.ts:307

Get current snapshot

Returns

SnapshotFrom<TMachine>

Overrides

AbstractActor.getSnapshot


on()

on<TType>(type, handler): Subscription;

Defined in: node_modules/xstate/dist/declarations/src/createActor.d.ts:115

Type Parameters

Type Parameter
TType extends any

Parameters

ParameterType
typeTType
handler(emitted) => void

Returns

Subscription

Inherited from

AbstractActor.on


send()

send(event): void;

Defined in: packages/play-xstate/src/player-actor.ts:282

Send event to actor

Forwards events to the underlying XState actor. The actor’s state machine guards determine whether each event is valid from the current state.

Parameters

ParameterTypeDescription
eventPlayEventAny event object with a type property

Returns

void

Example

// Domain event
actor.send({ type: "auth.login", userId: "123" });
// Routing event
actor.send({ type: "play.route", to: "#home" });

Overrides

AbstractActor.send


start()

start(): this;

Defined in: packages/play-xstate/src/player-actor.ts:239

Start the actor

Per RESEARCH.md Pitfall 1: Always call start() after creation

Returns

this

Overrides

AbstractActor.start


stop()

stop(): this;

Defined in: packages/play-xstate/src/player-actor.ts:253

Stop the actor and cleanup

Returns

this

Overrides

AbstractActor.stop


subscribe()

Call Signature

subscribe(observer): Subscription;

Defined in: node_modules/xstate/dist/declarations/src/createActor.d.ts:113

Subscribe an observer to an actor’s snapshot values.

Parameters
ParameterTypeDescription
observerObserver<any>Either a plain function that receives the latest snapshot, or an observer object whose .next(snapshot) method receives the latest snapshot
Returns

Subscription

Remarks

The observer will receive the actor’s snapshot value when it is emitted. The observer can be:

  • A plain function that receives the latest snapshot, or
  • An observer object whose .next(snapshot) method receives the latest snapshot
Examples
// Observer as a plain function
const subscription = actor.subscribe((snapshot) => {
console.log(snapshot);
});
// Observer as an object
const subscription = actor.subscribe({
next(snapshot) {
console.log(snapshot);
},
error(err) {
// ...
},
complete() {
// ...
},
});

The return value of actor.subscribe(observer) is a subscription object that has an .unsubscribe() method. You can call subscription.unsubscribe() to unsubscribe the observer:

const subscription = actor.subscribe((snapshot) => {
// ...
});
// Unsubscribe the observer
subscription.unsubscribe();

When the actor is stopped, all of its observers will automatically be unsubscribed.

Inherited from

AbstractActor.subscribe

Call Signature

subscribe(
nextListener?,
errorListener?,
completeListener?): Subscription;

Defined in: node_modules/xstate/dist/declarations/src/createActor.d.ts:114

Subscribe an observer to an actor’s snapshot values.

Parameters
ParameterType
nextListener?(snapshot) => void
errorListener?(error) => void
completeListener?() => void
Returns

Subscription

Remarks

The observer will receive the actor’s snapshot value when it is emitted. The observer can be:

  • A plain function that receives the latest snapshot, or
  • An observer object whose .next(snapshot) method receives the latest snapshot
Examples
// Observer as a plain function
const subscription = actor.subscribe((snapshot) => {
console.log(snapshot);
});
// Observer as an object
const subscription = actor.subscribe({
next(snapshot) {
console.log(snapshot);
},
error(err) {
// ...
},
complete() {
// ...
},
});

The return value of actor.subscribe(observer) is a subscription object that has an .unsubscribe() method. You can call subscription.unsubscribe() to unsubscribe the observer:

const subscription = actor.subscribe((snapshot) => {
// ...
});
// Unsubscribe the observer
subscription.unsubscribe();

When the actor is stopped, all of its observers will automatically be unsubscribed.

Inherited from

AbstractActor.subscribe


toJSON()

toJSON(): object;

Defined in: node_modules/xstate/dist/declarations/src/createActor.d.ts:135

Returns

object

NameTypeDefined in
idstringnode_modules/xstate/dist/declarations/src/createActor.d.ts:137
xstate$$typenumbernode_modules/xstate/dist/declarations/src/createActor.d.ts:136

Inherited from

AbstractActor.toJSON