@xmachines/play-actor 1.0.0-beta.3 → 1.0.0-beta.30

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,11 @@ npm install @xmachines/play-actor
28
28
  - `AbstractActor`
29
29
  - `Routable` (type)
30
30
  - `Viewable` (type)
31
+ - `ViewMetadata` (type)
31
32
 
32
33
  **Peer dependencies:**
33
34
 
34
- - `xstate` ^5.0.0 - State machine runtime (XState compatibility)
35
+ - `xstate` ^5.0.0 State machine runtime (XState compatibility)
35
36
  - `@xmachines/play-signals` - TC39 Signals primitives
36
37
  - `@xmachines/play` - Protocol types (PlayEvent, etc.)
37
38
 
@@ -43,7 +44,7 @@ npm install @xmachines/play-actor
43
44
  import { definePlayer } from "@xmachines/play-xstate";
44
45
 
45
46
  // definePlayer returns PlayerActor (extends AbstractActor)
46
- const createPlayer = definePlayer({ machine, catalog });
47
+ const createPlayer = definePlayer({ machine });
47
48
  const actor = createPlayer();
48
49
  actor.start();
49
50
 
@@ -61,69 +62,64 @@ Abstract base class defining signal protocol:
61
62
 
62
63
  **Abstract Properties (must implement):**
63
64
 
64
- - `state: Signal.State<any>` - Reactive snapshot of current state
65
+ - `state: Signal.State<unknown>` - Reactive snapshot of current state
66
+
67
+ **Optional capability interfaces:**
68
+
69
+ Implement `Routable` to add routing support:
70
+
65
71
  - `currentRoute: Signal.Computed<string | null>` - Derived navigation path
66
- - `currentView: Signal.Computed<Record<string, any> | null>` - Derived UI structure
67
- - `catalog: any` - Component catalog
72
+
73
+ Implement `Viewable` to add view rendering support:
74
+
75
+ - `currentView: Signal.State<ViewMetadata | null>` - Current view spec (updated on every state transition). `ViewMetadata` has the shape `{ component: string; spec: Spec }` where `spec` is a `@json-render/core` spec object driving the renderer.
68
76
 
69
77
  **Inherited from XState Actor:**
70
78
 
71
- - `send(event: PlayEvent): void` - Send event to actor
79
+ - `send(event): void` - Send event to actor
72
80
  - `start(): void` - Start the actor
73
81
  - `stop(): void` - Stop the actor
74
- - `getSnapshot(): Snapshot` - Get current XState snapshot
82
+ - `getSnapshot()` - Get current XState snapshot (typed as `SnapshotFrom<TLogic>`)
75
83
 
76
84
  **Example implementation pattern:**
77
85
 
78
86
  ```typescript
79
- import { AbstractActor } from "@xmachines/play-actor";
87
+ import {
88
+ AbstractActor,
89
+ type Routable,
90
+ type Viewable,
91
+ type ViewMetadata,
92
+ } from "@xmachines/play-actor";
80
93
  import { Signal } from "@xmachines/play-signals";
94
+ import type { AnyActorLogic, AnyMachineSnapshot } from "xstate";
81
95
 
82
- class PlayerActor<TLogic extends AnyActorLogic> extends AbstractActor<TLogic> {
83
- // Implement required signal properties
84
- state = new Signal.State(this.internalActor.getSnapshot());
96
+ class PlayerActor<TLogic extends AnyActorLogic>
97
+ extends AbstractActor<TLogic>
98
+ implements Routable, Viewable
99
+ {
100
+ // Required: reactive state snapshot
101
+ state = new Signal.State<AnyMachineSnapshot>(this.getSnapshot() as AnyMachineSnapshot);
85
102
 
103
+ // Routable: derived navigation path
86
104
  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;
105
+ return deriveRoute(this.state.get());
94
106
  });
95
107
 
96
- catalog = this.config.catalog;
97
-
98
- // Internal XState actor
99
- private internalActor: Actor<TLogic>;
108
+ // Viewable: current view spec — Signal.State, updated on every state transition
109
+ currentView = new Signal.State<ViewMetadata | null>(null);
100
110
 
101
- constructor(logic: TLogic, catalog: any) {
111
+ constructor(logic: TLogic) {
102
112
  super(logic);
103
- this.internalActor = createActor(logic);
104
113
 
105
- // Subscribe to XState transitions
106
- this.internalActor.subscribe((snapshot) => {
107
- this.state.set(snapshot); // Update signal
114
+ // Subscribe to XState transitions and update signals
115
+ this.subscribe((snapshot) => {
116
+ this.state.set(snapshot as AnyMachineSnapshot);
117
+ // Derive currentView from snapshot meta and update the signal...
108
118
  });
109
119
  }
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
120
  }
123
121
  ```
124
122
 
125
- **Complete API:** See [API Documentation](../../docs/api/@xmachines/play-actor)
126
-
127
123
  ## Examples
128
124
 
129
125
  ### Infrastructure Observing Signals
@@ -215,4 +211,7 @@ This base class enforces three architectural invariants:
215
211
 
216
212
  ## License
217
213
 
218
- MIT
214
+ Copyright (c) 2016 [Mikael Karon](mailto:mikael@karon.se). All rights reserved.
215
+
216
+ This work is licensed under the terms of the MIT license.
217
+ For a copy, see <https://opensource.org/licenses/MIT>.
@@ -16,189 +16,130 @@
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 } 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: {
71
+ * component: "Dashboard",
72
+ * spec: typedSpec<DashboardCtx>({
73
+ * root: "root",
74
+ * contextProps: ["username"], // ✓ key of DashboardCtx
75
+ * // contextProps: ["usernaem"], // ✗ compile error
76
+ * elements: { root: { type: "Dashboard", props: {}, children: [] } },
77
+ * }),
78
+ * },
75
79
  * }
76
80
  * ```
77
81
  */
78
- export interface Viewable {
82
+ export declare function typedSpec<TContext extends object>(spec: Omit<PlaySpec, "contextProps"> & {
83
+ readonly contextProps?: readonly (keyof TContext & string)[];
84
+ }): PlaySpec;
85
+ /**
86
+ * View metadata for rendering.
87
+ *
88
+ * Describes the component to be rendered and the json-render Spec to use.
89
+ * Used by PlayRenderer to dynamically render UI based on actor state.
90
+ */
91
+ export interface ViewMetadata {
92
+ /** Root element type name (for diagnostics and component resolution) */
93
+ component: string;
79
94
  /**
80
- * Current view signal
95
+ * XMachines view spec — extends `@json-render/core` Spec with `contextProps`
96
+ * for explicit context field exposure.
81
97
  *
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
- * ```
98
+ * Use `typedSpec<TContext>(...)` at the definition site to validate
99
+ * `contextProps` entries against the machine context type.
94
100
  */
95
- readonly currentView: Signal.State<any>;
101
+ spec: PlaySpec;
102
+ }
103
+ /**
104
+ * Actor capability for exposing renderable view state.
105
+ *
106
+ * `Viewable` marks actors that publish a `currentView` signal.
107
+ * Renderers such as `PlayRenderer` consume this contract to resolve the
108
+ * current view description into concrete UI without embedding view logic inside the
109
+ * framework adapter.
110
+ */
111
+ export interface Viewable {
96
112
  /**
97
- * Component catalog for view resolution
113
+ * Current view signal.
98
114
  *
99
- * Maps component names to actual component implementations.
100
- * Used by renderers to resolve view.component to actual UI components.
115
+ * State signal containing view.component and view.spec from meta.view.
116
+ * Infrastructure renders view Logic-Driven UI invariant.
101
117
  */
102
- readonly catalog: any;
118
+ readonly currentView: Signal.State<ViewMetadata | null>;
103
119
  }
104
120
  /**
105
121
  * Abstract base class for Play Architecture actors.
106
122
  *
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
113
- *
114
- * Optional capabilities (routing, view rendering) are provided via interfaces:
115
- * - Implement Routable for routing support
116
- * - Implement Viewable for view rendering support
117
- *
118
- * Concrete implementations created by @xmachines/play-xstate adapter.
119
- *
120
- * @typeParam TLogic - XState actor logic type (maintains type safety)
123
+ * Provides signal-driven state observation that integrates with XState ecosystem
124
+ * tooling (devtools, inspection) while exposing reactive signals for
125
+ * Infrastructure layer communication.
121
126
  *
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.
125
- *
126
- * @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
- * ```
134
- *
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) { ... }
142
- * }
143
- * ```
144
- *
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
- * ```
156
- *
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
127
+ * @typeParam TLogic - XState actor logic type
128
+ * @typeParam TEvent - Event type constraint (defaults to EventObject)
160
129
  */
161
- export declare abstract class AbstractActor<TLogic extends AnyActorLogic> extends Actor<TLogic> {
130
+ export declare abstract class AbstractActor<TLogic extends AnyActorLogic, TEvent extends EventObject = EventObject> extends Actor<TLogic> {
162
131
  /**
163
132
  * Reactive snapshot of current actor state.
164
133
  *
165
134
  * 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
- * ```
135
+ * directly coupling to the actor's internal state machine implementation.
176
136
  */
177
- abstract state: Signal.State<any>;
137
+ abstract state: Signal.State<unknown>;
178
138
  /**
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
- * ```
139
+ * Send event to Actor.
195
140
  *
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.
141
+ * Constrained to TEvent for type safety in concrete implementations.
199
142
  */
200
- abstract send(event: {
201
- readonly type: string;
202
- } & Record<string, any>): void;
143
+ abstract send(event: TEvent): void;
203
144
  }
204
145
  //# 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,MAAM,mBAAmB,CAAC;AAE9C;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;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;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC5B,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,IAAI,EAAE,QAAQ,CAAC;CACf;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;CACxD;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,49 @@
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: {
41
+ * component: "Dashboard",
42
+ * spec: typedSpec<DashboardCtx>({
43
+ * root: "root",
44
+ * contextProps: ["username"], // key of DashboardCtx
45
+ * // contextProps: ["usernaem"], // ✗ compile error
46
+ * elements: { root: { type: "Dashboard", props: {}, children: [] } },
47
+ * }),
48
+ * },
58
49
  * }
59
50
  * ```
51
+ */
52
+ export function typedSpec(spec) {
53
+ return spec;
54
+ }
55
+ /**
56
+ * Abstract base class for Play Architecture actors.
60
57
  *
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
- * ```
58
+ * Provides signal-driven state observation that integrates with XState ecosystem
59
+ * tooling (devtools, inspection) while exposing reactive signals for
60
+ * Infrastructure layer communication.
72
61
  *
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
62
+ * @typeParam TLogic - XState actor logic type
63
+ * @typeParam TEvent - Event type constraint (defaults to EventObject)
76
64
  */
77
65
  export class AbstractActor extends Actor {
78
66
  }
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,SAAS,CACxB,IAEC;IAED,OAAO,IAAgB,CAAC;AACzB,CAAC;AAuCD;;;;;;;;;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 ViewMetadata, type PlaySpec, } 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,YAAY,EACjB,KAAK,QAAQ,GACb,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,GAKT,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.3",
3
+ "version": "1.0.0-beta.30",
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,25 +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/.vite*",
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.16.0"
43
+ },
41
44
  "devDependencies": {
42
45
  "@types/node": "^25.5.0",
43
- "xstate": "^5.28.0"
46
+ "@xmachines/shared": "1.0.0-beta.30",
47
+ "oxfmt": "^0.43.0",
48
+ "oxlint": "^1.57.0",
49
+ "vitest": "^4.1.2",
50
+ "xstate": "^5.30.0"
44
51
  },
45
52
  "peerDependencies": {
46
- "@xmachines/play": "1.0.0-beta.3",
47
- "@xmachines/play-signals": "1.0.0-beta.3",
48
- "xstate": "^5.28.0"
53
+ "@xmachines/play": "1.0.0-beta.30",
54
+ "@xmachines/play-signals": "1.0.0-beta.30",
55
+ "xstate": "^5.30.0"
49
56
  },
50
57
  "engines": {
51
58
  "node": ">=22.0.0"
52
- }
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."
53
61
  }