@xmachines/play-actor 1.0.0-beta.4 → 1.0.0-beta.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mikael Karon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -8,7 +8,7 @@ Foundation for all actor implementations, enforcing XState compatibility and rea
8
8
 
9
9
  `@xmachines/play-actor` provides `AbstractActor`, a base class that extends XState's `Actor` while enforcing the Play Architecture's signal protocol. It maintains XState ecosystem compatibility (inspection tools, devtools) while exposing reactive signals for infrastructure layer communication.
10
10
 
11
- Per [RFC Play v1](https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md), this package implements:
11
+ Per [Play RFC](../docs/rfc/play.md), this package implements:
12
12
 
13
13
  - **Actor Authority (INV-01):** Actor is sole source of truth for state transitions
14
14
  - **Signal-Only Reactivity (INV-05):** Infrastructure observes via TC39 Signals, never directly queries
@@ -28,10 +28,14 @@ npm install @xmachines/play-actor
28
28
  - `AbstractActor`
29
29
  - `Routable` (type)
30
30
  - `Viewable` (type)
31
+ - `PlaySpec` (type)
32
+ - `typedSpec`
33
+ - `BaseActorProviderProps` (type)
34
+ - `BaseViewContextValue` (type)
31
35
 
32
36
  **Peer dependencies:**
33
37
 
34
- - `xstate` ^5.0.0 - State machine runtime (XState compatibility)
38
+ - `xstate` ^5.0.0 State machine runtime (XState compatibility)
35
39
  - `@xmachines/play-signals` - TC39 Signals primitives
36
40
  - `@xmachines/play` - Protocol types (PlayEvent, etc.)
37
41
 
@@ -43,7 +47,7 @@ npm install @xmachines/play-actor
43
47
  import { definePlayer } from "@xmachines/play-xstate";
44
48
 
45
49
  // definePlayer returns PlayerActor (extends AbstractActor)
46
- const createPlayer = definePlayer({ machine, catalog });
50
+ const createPlayer = definePlayer({ machine });
47
51
  const actor = createPlayer();
48
52
  actor.start();
49
53
 
@@ -61,69 +65,59 @@ Abstract base class defining signal protocol:
61
65
 
62
66
  **Abstract Properties (must implement):**
63
67
 
64
- - `state: Signal.State<any>` - Reactive snapshot of current state
68
+ - `state: Signal.State<unknown>` - Reactive snapshot of current state
69
+
70
+ **Optional capability interfaces:**
71
+
72
+ Implement `Routable` to add routing support:
73
+
65
74
  - `currentRoute: Signal.Computed<string | null>` - Derived navigation path
66
- - `currentView: Signal.Computed<Record<string, any> | null>` - Derived UI structure
67
- - `catalog: any` - Component catalog
75
+
76
+ Implement `Viewable` to add view rendering support:
77
+
78
+ - `currentView: Signal.State<PlaySpec | null>` - Current view spec (updated on every state transition). `PlaySpec` is a `@json-render/core` spec object (`{ root, elements }`) that drives the renderer directly.
68
79
 
69
80
  **Inherited from XState Actor:**
70
81
 
71
- - `send(event: PlayEvent): void` - Send event to actor
82
+ - `send(event): void` - Send event to actor
72
83
  - `start(): void` - Start the actor
73
84
  - `stop(): void` - Stop the actor
74
- - `getSnapshot(): Snapshot` - Get current XState snapshot
85
+ - `getSnapshot()` - Get current XState snapshot (typed as `SnapshotFrom<TLogic>`)
75
86
 
76
87
  **Example implementation pattern:**
77
88
 
78
89
  ```typescript
79
- import { AbstractActor } from "@xmachines/play-actor";
90
+ import { AbstractActor, type Routable, type Viewable, type PlaySpec } from "@xmachines/play-actor";
80
91
  import { Signal } from "@xmachines/play-signals";
92
+ import type { AnyActorLogic, AnyMachineSnapshot } from "xstate";
81
93
 
82
- class PlayerActor<TLogic extends AnyActorLogic> extends AbstractActor<TLogic> {
83
- // Implement required signal properties
84
- state = new Signal.State(this.internalActor.getSnapshot());
94
+ class PlayerActor<TLogic extends AnyActorLogic>
95
+ extends AbstractActor<TLogic>
96
+ implements Routable, Viewable
97
+ {
98
+ // Required: reactive state snapshot
99
+ state = new Signal.State<AnyMachineSnapshot>(this.getSnapshot() as AnyMachineSnapshot);
85
100
 
101
+ // Routable: derived navigation path
86
102
  currentRoute = new Signal.Computed(() => {
87
- const snapshot = this.state.get();
88
- return deriveRoute(snapshot);
89
- });
90
-
91
- currentView = new Signal.Computed(() => {
92
- const snapshot = this.state.get();
93
- return snapshot.meta?.view ?? null;
103
+ return deriveRoute(this.state.get());
94
104
  });
95
105
 
96
- catalog = this.config.catalog;
97
-
98
- // Internal XState actor
99
- private internalActor: Actor<TLogic>;
106
+ // Viewable: current view spec — Signal.State, updated on every state transition
107
+ currentView = new Signal.State<PlaySpec | null>(null);
100
108
 
101
- constructor(logic: TLogic, catalog: any) {
109
+ constructor(logic: TLogic) {
102
110
  super(logic);
103
- this.internalActor = createActor(logic);
104
111
 
105
- // Subscribe to XState transitions
106
- this.internalActor.subscribe((snapshot) => {
107
- this.state.set(snapshot); // Update signal
112
+ // Subscribe to XState transitions and update signals
113
+ this.subscribe((snapshot) => {
114
+ this.state.set(snapshot as AnyMachineSnapshot);
115
+ // Derive currentView from snapshot meta and update the signal...
108
116
  });
109
117
  }
110
-
111
- override start(): void {
112
- this.internalActor.start();
113
- }
114
-
115
- override stop(): void {
116
- this.internalActor.stop();
117
- }
118
-
119
- override send(event: PlayEvent): void {
120
- this.internalActor.send(event as any);
121
- }
122
118
  }
123
119
  ```
124
120
 
125
- **Complete API:** See [API Documentation](../../docs/api/@xmachines/play-actor)
126
-
127
121
  ## Examples
128
122
 
129
123
  ### Infrastructure Observing Signals
@@ -215,4 +209,7 @@ This base class enforces three architectural invariants:
215
209
 
216
210
  ## License
217
211
 
218
- MIT
212
+ Copyright (c) 2016 [Mikael Karon](mailto:mikael@karon.se). All rights reserved.
213
+
214
+ This work is licensed under the terms of the MIT license.
215
+ For a copy, see <https://opensource.org/licenses/MIT>.
@@ -16,189 +16,169 @@
16
16
  *
17
17
  * @packageDocumentation
18
18
  */
19
- import { Actor, type AnyActorLogic } from "xstate";
19
+ import { Actor, type AnyActorLogic, type EventObject } from "xstate";
20
20
  import type { Signal } from "@xmachines/play-signals";
21
+ import type { Spec, StateStore, RenderErrorHandler, ActionHandler } from "@json-render/core";
21
22
  /**
22
23
  * Optional capability: Routing support
24
+ */
25
+ export interface Routable {
26
+ readonly currentRoute: Signal.Computed<string | null>;
27
+ readonly initialRoute: string | null;
28
+ }
29
+ /**
30
+ * XMachines extension of `@json-render/core` `Spec`.
23
31
  *
24
- * Actors implementing this interface can derive a route from their state.
25
- * Router adapters observe the currentRoute signal to sync browser URLs.
32
+ * Adds `contextProps` an explicit allowlist of machine context fields that
33
+ * `deriveCurrentView` merges into element props as low-priority slots. Only
34
+ * fields named here are ever exposed to components; nothing leaks from context
35
+ * without an opt-in declaration.
26
36
  *
27
- * @example
28
- * ```typescript
29
- * class MyActor extends AbstractActor implements Routable {
30
- * currentRoute = new Signal.Computed(() => deriveRoute(this.state.get()));
31
- * }
32
- *
33
- * // Router requires Routable
34
- * function connectRouter<T extends AbstractActor & Routable>(actor: T) {
35
- * watcher.watch(actor.currentRoute);
36
- * }
37
- * ```
37
+ * Use `typedSpec<TContext>(...)` at the definition site to validate `contextProps`
38
+ * entries against your machine's context type at compile time.
38
39
  */
39
- export interface Routable {
40
+ export interface PlaySpec extends Spec {
40
41
  /**
41
- * Current route signal
42
- *
43
- * Computed signal derived from state machine. Infrastructure observes to sync browser URL.
44
- *
45
- * Invariant: Passive Infrastructure - Infrastructure reflects route, never decides.
42
+ * Explicit allowlist of machine context field names to expose as prop slots.
43
+ * Each named field is merged into every spec element's `props` at view derivation
44
+ * time, filling any slot whose current value is `undefined`.
46
45
  *
47
- * @example
48
- * ```typescript
49
- * const watcher = new Signal.subtle.Watcher(() => {
50
- * const route = actor.currentRoute.get();
51
- * console.log('Route changed:', route);
52
- * });
53
- * watcher.watch(actor.currentRoute);
54
- * ```
46
+ * Use `typedSpec<TContext>(...)` to constrain entries to `keyof TContext & string`.
55
47
  */
56
- readonly currentRoute: Signal.Computed<string | null>;
48
+ readonly contextProps?: readonly string[];
57
49
  }
58
50
  /**
59
- * Optional capability: View rendering support
51
+ * Identity helper that constrains a `PlaySpec` object's `contextProps` to keys
52
+ * of a specific machine context type, giving compile-time validation and IDE
53
+ * autocomplete at the definition site.
54
+ *
55
+ * XState's `meta` field is typed as `Record<string, unknown>`, so TypeScript
56
+ * cannot infer the constraint from context. `typedSpec<MyCtx>(...)` is the
57
+ * opt-in mechanism that activates enforcement where the spec is written.
60
58
  *
61
- * Actors implementing this interface can derive view structures from their state.
62
- * Renderers observe the currentView signal to update the UI.
59
+ * At runtime this is a no-op the spec object is returned unchanged.
63
60
  *
64
61
  * @example
65
- * ```typescript
66
- * class MyActor extends AbstractActor implements Viewable {
67
- * currentView = new Signal.State(null);
68
- * catalog = { HomePage: HomeComponent };
62
+ * ```ts
63
+ * interface DashboardCtx {
64
+ * username: string;
65
+ * params: Record<string, string>;
66
+ * query: Record<string, string>;
69
67
  * }
70
68
  *
71
- * // Renderer requires Viewable
72
- * function renderView<T extends AbstractActor & Viewable>(actor: T) {
73
- * const view = actor.currentView.get();
74
- * return catalog[view.component];
69
+ * meta: {
70
+ * view: typedSpec<DashboardCtx>({
71
+ * root: "root",
72
+ * contextProps: ["username"], // ✓ key of DashboardCtx
73
+ * // contextProps: ["usernaem"], // ✗ compile error
74
+ * elements: { root: { type: "Dashboard", props: {}, children: [] } },
75
+ * }),
75
76
  * }
76
77
  * ```
77
78
  */
79
+ export declare function typedSpec<TContext extends object>(spec: Omit<PlaySpec, "contextProps"> & {
80
+ readonly contextProps?: readonly (keyof TContext & string)[];
81
+ }): PlaySpec;
82
+ /**
83
+ * Actor capability for exposing renderable view state.
84
+ *
85
+ * `Viewable` marks actors that publish a `currentView` signal.
86
+ * Renderers such as `PlayRenderer` consume this contract to resolve the
87
+ * current view description into concrete UI without embedding view logic inside the
88
+ * framework adapter.
89
+ */
78
90
  export interface Viewable {
79
91
  /**
80
- * Current view signal
81
- *
82
- * State signal containing UI structure schema from meta.view. Infrastructure renders view.
83
- *
84
- * Invariant: Logic-Driven UI - View structure is defined by business logic, not JSX.
85
- *
86
- * @example
87
- * ```typescript
88
- * const watcher = new Signal.subtle.Watcher(() => {
89
- * const view = actor.currentView.get();
90
- * console.log('View changed:', view);
91
- * });
92
- * watcher.watch(actor.currentView);
93
- * ```
94
- */
95
- readonly currentView: Signal.State<any>;
96
- /**
97
- * Component catalog for view resolution
92
+ * Current view signal. Contains the json-render PlaySpec for the current machine
93
+ * state, or null when no view is active.
98
94
  *
99
- * Maps component names to actual component implementations.
100
- * Used by renderers to resolve view.component to actual UI components.
95
+ * Infrastructure renders view Logic-Driven UI invariant.
101
96
  */
102
- readonly catalog: any;
97
+ readonly currentView: Signal.State<PlaySpec | null>;
103
98
  }
104
99
  /**
105
- * Abstract base class for Play Architecture actors.
106
- *
107
- * Extends XState Actor to maintain ecosystem compatibility (inspection, devtools)
108
- * while enforcing minimal signal protocol for Actor ↔ Infrastructure communication.
109
- *
110
- * The core protocol contains only:
111
- * - state: Reactive state snapshot
112
- * - send: Event dispatch method
100
+ * Framework-agnostic base for every framework's `ViewContextValue`.
113
101
  *
114
- * Optional capabilities (routing, view rendering) are provided via interfaces:
115
- * - Implement Routable for routing support
116
- * - Implement Viewable for view rendering support
102
+ * Holds the three fields that are identical across React, Vue, Solid, and Svelte.
103
+ * `registry` is framework-specific (each framework has its own `ComponentRegistry`
104
+ * type) so it is typed via `TRegistry` — the same generic used in `BaseActorProviderProps`.
117
105
  *
118
- * Concrete implementations created by @xmachines/play-xstate adapter.
106
+ * @typeParam TRegistry - The framework's component registry type (e.g. `ComponentRegistry` from `@json-render/react`).
107
+ */
108
+ export interface BaseViewContextValue<TRegistry extends object> {
109
+ /** The current PlaySpec to render. */
110
+ spec: PlaySpec;
111
+ /** Action handlers resolved against the live StateStore. */
112
+ handlers: Record<string, ActionHandler>;
113
+ /** Component registry from registryResult.registry. */
114
+ registry: TRegistry;
115
+ /** The active StateStore — pass to JSONUIProvider/JsonUIProvider as `store` to share state across providers. */
116
+ store: StateStore;
117
+ }
118
+ /**
119
+ * Framework-agnostic base props shared by every `ActorProvider` implementation
120
+ * (React, Vue, Solid, Svelte). `TRegistry` captures the framework-specific
121
+ * `DefineRegistryResult` type; `RenderErrorHandler` is sourced from
122
+ * `@json-render/core` so no second generic is needed.
119
123
  *
120
- * @typeParam TLogic - XState actor logic type (maintains type safety)
124
+ * Framework packages extend this with their `fallback`, `onError`, and `children` fields.
121
125
  *
122
- * Invariant: Actor Authority - Actor is the sole source of truth for state transitions.
123
- * Invariant: Signal-Only Reactivity - Infrastructure observes via TC39 Signals.
124
- * Invariant: Passive Infrastructure - Infrastructure reflects, never decides.
126
+ * @typeParam TRegistry - The framework's `DefineRegistryResult` type.
125
127
  *
126
128
  * @example
127
- * Simple actor (no routing, no view)
128
- * ```typescript
129
- * class SimpleActor extends AbstractActor<any> {
130
- * state = new Signal.State({...});
131
- * send(event) { ... }
132
- * }
133
- * ```
129
+ * ```ts
130
+ * import type { BaseActorProviderProps } from "@xmachines/play-actor";
131
+ * import type { DefineRegistryResult } from "@json-render/react";
134
132
  *
135
- * @example
136
- * Routable actor
137
- * ```typescript
138
- * class RoutableActor extends AbstractActor<any> implements Routable {
139
- * state = new Signal.State({...});
140
- * currentRoute = new Signal.Computed(() => deriveRoute(this.state.get()));
141
- * send(event) { ... }
133
+ * interface ActorProviderProps extends BaseActorProviderProps<DefineRegistryResult> {
134
+ * fallback?: React.ReactNode;
135
+ * children: React.ReactNode;
142
136
  * }
143
137
  * ```
138
+ */
139
+ export interface BaseActorProviderProps<TRegistry extends {
140
+ registry: object;
141
+ handlers: (...args: never[]) => unknown;
142
+ }> {
143
+ /** Actor instance with currentView signal (requires Viewable capability). */
144
+ actor: AbstractActor<AnyActorLogic> & Viewable;
145
+ /** Full result from defineRegistry() — contains the component registry and action handlers factory. */
146
+ registryResult: TRegistry;
147
+ /**
148
+ * Optional external StateStore (controlled mode).
149
+ * When provided, spec.state is ignored and this store is the single source of truth.
150
+ * When omitted, a fresh @xstate/store atom is created per view transition from spec.state.
151
+ */
152
+ store?: StateStore;
153
+ /**
154
+ * Called when an individual catalog component throws during render.
155
+ * Takes precedence over any onRenderError set via defineRegistry.
156
+ */
157
+ onRenderError?: RenderErrorHandler;
158
+ }
159
+ /**
160
+ * Abstract base class for Play Architecture actors.
144
161
  *
145
- * @example
146
- * Full-featured actor (routing + view)
147
- * ```typescript
148
- * class PlayerActor extends AbstractActor<any> implements Routable, Viewable {
149
- * state = new Signal.State({...});
150
- * currentRoute = new Signal.Computed(() => deriveRoute(this.state.get()));
151
- * currentView = new Signal.State(null);
152
- * catalog = {};
153
- * send(event) { ... }
154
- * }
155
- * ```
162
+ * Provides signal-driven state observation that integrates with XState ecosystem
163
+ * tooling (devtools, inspection) while exposing reactive signals for
164
+ * Infrastructure layer communication.
156
165
  *
157
- * @see {@link https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md#53-actor-protocol | RFC Play v1 Section 5.3}
158
- * @see {@link Routable} for routing capability
159
- * @see {@link Viewable} for view rendering capability
166
+ * @typeParam TLogic - XState actor logic type
167
+ * @typeParam TEvent - Event type constraint (defaults to EventObject)
160
168
  */
161
- export declare abstract class AbstractActor<TLogic extends AnyActorLogic> extends Actor<TLogic> {
169
+ export declare abstract class AbstractActor<TLogic extends AnyActorLogic, TEvent extends EventObject = EventObject> extends Actor<TLogic> {
162
170
  /**
163
171
  * Reactive snapshot of current actor state.
164
172
  *
165
173
  * Infrastructure observes this signal to react to state changes without
166
- * directly coupling to the Actor's internal state machine implementation.
167
- *
168
- * @example
169
- * ```typescript
170
- * // Infrastructure observes state signal
171
- * const watcher = new Signal.subtle.Watcher(() => {
172
- * console.log('Actor state changed:', actor.state.get());
173
- * });
174
- * watcher.watch(actor.state);
175
- * ```
174
+ * directly coupling to the actor's internal state machine implementation.
176
175
  */
177
- abstract state: Signal.State<any>;
176
+ abstract state: Signal.State<unknown>;
178
177
  /**
179
- * Send event to Actor
180
- *
181
- * Infrastructure forwards user intents (navigation, domain events, custom events)
182
- * as events to the Actor. The Actor's state machine guards determine whether
183
- * each event is valid from the current state.
184
- *
185
- * @param event - Event object with type property (e.g., PlayEvent, PlayRouteEvent)
186
- *
187
- * Invariant: Actor Authority - Only Actor decides whether an event is valid.
188
- *
189
- * @example
190
- * ```typescript
191
- * // Infrastructure forwards user intent
192
- * actor.send({ type: 'auth.login', userId: '123' });
193
- * // Actor's guards determine if event is allowed
194
- * ```
178
+ * Send event to Actor.
195
179
  *
196
- * @remarks
197
- * Accepts any event object with a type property. Core events (PlayEvent) are in
198
- * @xmachines/play, routing events (PlayRouteEvent) are in @xmachines/play-router.
180
+ * Constrained to TEvent for type safety in concrete implementations.
199
181
  */
200
- abstract send(event: {
201
- readonly type: string;
202
- } & Record<string, any>): void;
182
+ abstract send(event: TEvent): void;
203
183
  }
204
184
  //# sourceMappingURL=abstract-actor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"abstract-actor.d.ts","sourceRoot":"","sources":["../src/abstract-actor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,KAAK,EAAE,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,QAAQ;IACxB;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACtD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,QAAQ;IACxB;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAExC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,8BAAsB,aAAa,CAAC,MAAM,SAAS,aAAa,CAAE,SAAQ,KAAK,CAAC,MAAM,CAAC;IACtF;;;;;;;;;;;;;;OAcG;IACH,SAAgB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAEzC;;;;;;;;;;;;;;;;;;;;;OAqBG;aACsB,IAAI,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;CAC3F"}
1
+ {"version":3,"file":"abstract-actor.d.ts","sourceRoot":"","sources":["../src/abstract-actor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,KAAK,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE7F;;GAEG;AACH,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACtD,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,QAAS,SAAQ,IAAI;IACrC;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,SAAS,CAAC,QAAQ,SAAS,MAAM,EAChD,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,GAAG;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC;CAC7D,GACC,QAAQ,CAEV;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;CACpD;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAoB,CAAC,SAAS,SAAS,MAAM;IAC7D,sCAAsC;IACtC,IAAI,EAAE,QAAQ,CAAC;IACf,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACxC,uDAAuD;IACvD,QAAQ,EAAE,SAAS,CAAC;IACpB,gHAAgH;IAChH,KAAK,EAAE,UAAU,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,sBAAsB,CACtC,SAAS,SAAS;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAA;CAAE;IAE/E,6EAA6E;IAC7E,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ,CAAC;IAC/C,uGAAuG;IACvG,cAAc,EAAE,SAAS,CAAC;IAC1B;;;;OAIG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB;;;OAGG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAED;;;;;;;;;GASG;AACH,8BAAsB,aAAa,CAClC,MAAM,SAAS,aAAa,EAC5B,MAAM,SAAS,WAAW,GAAG,WAAW,CACvC,SAAQ,KAAK,CAAC,MAAM,CAAC;IACtB;;;;;OAKG;IACH,SAAgB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAE7C;;;;OAIG;aACsB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAClD"}
@@ -18,61 +18,46 @@
18
18
  */
19
19
  import { Actor } from "xstate";
20
20
  /**
21
- * Abstract base class for Play Architecture actors.
22
- *
23
- * Extends XState Actor to maintain ecosystem compatibility (inspection, devtools)
24
- * while enforcing minimal signal protocol for Actor ↔ Infrastructure communication.
25
- *
26
- * The core protocol contains only:
27
- * - state: Reactive state snapshot
28
- * - send: Event dispatch method
29
- *
30
- * Optional capabilities (routing, view rendering) are provided via interfaces:
31
- * - Implement Routable for routing support
32
- * - Implement Viewable for view rendering support
21
+ * Identity helper that constrains a `PlaySpec` object's `contextProps` to keys
22
+ * of a specific machine context type, giving compile-time validation and IDE
23
+ * autocomplete at the definition site.
33
24
  *
34
- * Concrete implementations created by @xmachines/play-xstate adapter.
25
+ * XState's `meta` field is typed as `Record<string, unknown>`, so TypeScript
26
+ * cannot infer the constraint from context. `typedSpec<MyCtx>(...)` is the
27
+ * opt-in mechanism that activates enforcement where the spec is written.
35
28
  *
36
- * @typeParam TLogic - XState actor logic type (maintains type safety)
37
- *
38
- * Invariant: Actor Authority - Actor is the sole source of truth for state transitions.
39
- * Invariant: Signal-Only Reactivity - Infrastructure observes via TC39 Signals.
40
- * Invariant: Passive Infrastructure - Infrastructure reflects, never decides.
29
+ * At runtime this is a no-op the spec object is returned unchanged.
41
30
  *
42
31
  * @example
43
- * Simple actor (no routing, no view)
44
- * ```typescript
45
- * class SimpleActor extends AbstractActor<any> {
46
- * state = new Signal.State({...});
47
- * send(event) { ... }
32
+ * ```ts
33
+ * interface DashboardCtx {
34
+ * username: string;
35
+ * params: Record<string, string>;
36
+ * query: Record<string, string>;
48
37
  * }
49
- * ```
50
38
  *
51
- * @example
52
- * Routable actor
53
- * ```typescript
54
- * class RoutableActor extends AbstractActor<any> implements Routable {
55
- * state = new Signal.State({...});
56
- * currentRoute = new Signal.Computed(() => deriveRoute(this.state.get()));
57
- * send(event) { ... }
39
+ * meta: {
40
+ * view: typedSpec<DashboardCtx>({
41
+ * root: "root",
42
+ * contextProps: ["username"], // key of DashboardCtx
43
+ * // contextProps: ["usernaem"], // ✗ compile error
44
+ * elements: { root: { type: "Dashboard", props: {}, children: [] } },
45
+ * }),
58
46
  * }
59
47
  * ```
48
+ */
49
+ export function typedSpec(spec) {
50
+ return spec;
51
+ }
52
+ /**
53
+ * Abstract base class for Play Architecture actors.
60
54
  *
61
- * @example
62
- * Full-featured actor (routing + view)
63
- * ```typescript
64
- * class PlayerActor extends AbstractActor<any> implements Routable, Viewable {
65
- * state = new Signal.State({...});
66
- * currentRoute = new Signal.Computed(() => deriveRoute(this.state.get()));
67
- * currentView = new Signal.State(null);
68
- * catalog = {};
69
- * send(event) { ... }
70
- * }
71
- * ```
55
+ * Provides signal-driven state observation that integrates with XState ecosystem
56
+ * tooling (devtools, inspection) while exposing reactive signals for
57
+ * Infrastructure layer communication.
72
58
  *
73
- * @see {@link https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md#53-actor-protocol | RFC Play v1 Section 5.3}
74
- * @see {@link Routable} for routing capability
75
- * @see {@link Viewable} for view rendering capability
59
+ * @typeParam TLogic - XState actor logic type
60
+ * @typeParam TEvent - Event type constraint (defaults to EventObject)
76
61
  */
77
62
  export class AbstractActor extends Actor {
78
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"abstract-actor.js","sourceRoot":"","sources":["../src/abstract-actor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,KAAK,EAAsB,MAAM,QAAQ,CAAC;AAyFnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,MAAM,OAAgB,aAA4C,SAAQ,KAAa;CAyCtF"}
1
+ {"version":3,"file":"abstract-actor.js","sourceRoot":"","sources":["../src/abstract-actor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,KAAK,EAAwC,MAAM,QAAQ,CAAC;AAkCrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,SAAS,CACxB,IAEC;IAED,OAAO,IAAgB,CAAC;AACzB,CAAC;AAiFD;;;;;;;;;GASG;AACH,MAAM,OAAgB,aAGpB,SAAQ,KAAa;CAetB"}
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * reactive signals for Infrastructure layer communication.
14
14
  *
15
15
  * @packageDocumentation
16
- * @see {@link https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md#53-actor-protocol | RFC Play v1 Section 5.3}
16
+ * @see [Play RFC](../../docs/rfc/play.md)
17
17
  */
18
- export { AbstractActor, type Routable, type Viewable } from "./abstract-actor.js";
18
+ export { AbstractActor, typedSpec, type Routable, type Viewable, type PlaySpec, type BaseActorProviderProps, type BaseViewContextValue, } from "./abstract-actor.js";
19
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACN,aAAa,EACb,SAAS,EACT,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,GACzB,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@
13
13
  * reactive signals for Infrastructure layer communication.
14
14
  *
15
15
  * @packageDocumentation
16
- * @see {@link https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md#53-actor-protocol | RFC Play v1 Section 5.3}
16
+ * @see [Play RFC](../../docs/rfc/play.md)
17
17
  */
18
- export { AbstractActor } from "./abstract-actor.js";
18
+ export { AbstractActor, typedSpec, } from "./abstract-actor.js";
19
19
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,aAAa,EAAgC,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACN,aAAa,EACb,SAAS,GAMT,MAAM,qBAAqB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmachines/play-actor",
3
- "version": "1.0.0-beta.4",
3
+ "version": "1.0.0-beta.41",
4
4
  "private": false,
5
5
  "description": "Abstract Actor base class for XMachines Play Architecture",
6
6
  "keywords": [
@@ -20,6 +20,7 @@
20
20
  "type": "module",
21
21
  "exports": {
22
22
  ".": {
23
+ "source": "./src/index.ts",
23
24
  "types": "./dist/index.d.ts",
24
25
  "import": "./dist/index.js"
25
26
  }
@@ -29,26 +30,32 @@
29
30
  },
30
31
  "scripts": {
31
32
  "build": "tsc --build",
32
- "clean": "rm -rf dist *.tsbuildinfo",
33
- "typecheck": "tsc --noEmit",
34
- "test": "vitest run",
33
+ "clean": "rm -rf dist *.tsbuildinfo coverage node_modules/.svelte2tsx-*",
34
+ "test": "vitest",
35
35
  "lint": "oxlint .",
36
36
  "lint:fix": "oxlint --fix .",
37
37
  "format": "oxfmt .",
38
38
  "format:check": "oxfmt --check .",
39
39
  "prepublishOnly": "npm run build"
40
40
  },
41
+ "dependencies": {
42
+ "@json-render/core": "^0.18.0"
43
+ },
41
44
  "devDependencies": {
42
- "@types/node": "^25.5.0",
43
- "@xmachines/shared": "1.0.0-beta.4",
44
- "xstate": "^5.28.0"
45
+ "@types/node": "^25.6.0",
46
+ "@xmachines/shared": "1.0.0-beta.41",
47
+ "oxfmt": "^0.45.0",
48
+ "oxlint": "^1.60.0",
49
+ "vitest": "^4.1.4",
50
+ "xstate": "^5.30.0"
45
51
  },
46
52
  "peerDependencies": {
47
- "@xmachines/play": "1.0.0-beta.4",
48
- "@xmachines/play-signals": "1.0.0-beta.4",
49
- "xstate": "^5.28.0"
53
+ "@xmachines/play": "1.0.0-beta.41",
54
+ "@xmachines/play-signals": "1.0.0-beta.41",
55
+ "xstate": "^5.30.0"
50
56
  },
51
57
  "engines": {
52
58
  "node": ">=22.0.0"
53
- }
59
+ },
60
+ "_devDependencies_note": "xstate appears in both peerDependencies and devDependencies intentionally. devDependencies provides workspace resolution for local builds and tests. peerDependencies declares the consumer version constraint. Both are pinned to ^5.30.0 to prevent drift."
54
61
  }