@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 +21 -0
- package/README.md +42 -43
- package/dist/abstract-actor.d.ts +86 -145
- package/dist/abstract-actor.d.ts.map +1 -1
- package/dist/abstract-actor.js +33 -45
- package/dist/abstract-actor.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +17 -9
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 [
|
|
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
|
|
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
|
|
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<
|
|
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
|
-
|
|
67
|
-
|
|
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
|
|
79
|
+
- `send(event): void` - Send event to actor
|
|
72
80
|
- `start(): void` - Start the actor
|
|
73
81
|
- `stop(): void` - Stop the actor
|
|
74
|
-
- `getSnapshot()
|
|
82
|
+
- `getSnapshot()` - Get current XState snapshot (typed as `SnapshotFrom<TLogic>`)
|
|
75
83
|
|
|
76
84
|
**Example implementation pattern:**
|
|
77
85
|
|
|
78
86
|
```typescript
|
|
79
|
-
import {
|
|
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>
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
111
|
+
constructor(logic: TLogic) {
|
|
102
112
|
super(logic);
|
|
103
|
-
this.internalActor = createActor(logic);
|
|
104
113
|
|
|
105
|
-
// Subscribe to XState transitions
|
|
106
|
-
this.
|
|
107
|
-
this.state.set(snapshot);
|
|
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
|
-
|
|
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>.
|
package/dist/abstract-actor.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
25
|
-
*
|
|
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
|
-
*
|
|
28
|
-
*
|
|
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
|
|
40
|
+
export interface PlaySpec extends Spec {
|
|
40
41
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
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
|
-
*
|
|
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
|
|
48
|
+
readonly contextProps?: readonly string[];
|
|
57
49
|
}
|
|
58
50
|
/**
|
|
59
|
-
*
|
|
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
|
-
*
|
|
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
|
-
* ```
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
62
|
+
* ```ts
|
|
63
|
+
* interface DashboardCtx {
|
|
64
|
+
* username: string;
|
|
65
|
+
* params: Record<string, string>;
|
|
66
|
+
* query: Record<string, string>;
|
|
69
67
|
* }
|
|
70
68
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
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
|
|
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
|
-
*
|
|
95
|
+
* XMachines view spec — extends `@json-render/core` Spec with `contextProps`
|
|
96
|
+
* for explicit context field exposure.
|
|
81
97
|
*
|
|
82
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
113
|
+
* Current view signal.
|
|
98
114
|
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
115
|
+
* State signal containing view.component and view.spec from meta.view.
|
|
116
|
+
* Infrastructure renders view — Logic-Driven UI invariant.
|
|
101
117
|
*/
|
|
102
|
-
readonly
|
|
118
|
+
readonly currentView: Signal.State<ViewMetadata | null>;
|
|
103
119
|
}
|
|
104
120
|
/**
|
|
105
121
|
* Abstract base class for Play Architecture actors.
|
|
106
122
|
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
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
|
-
*
|
|
123
|
-
*
|
|
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
|
|
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<
|
|
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
|
-
*
|
|
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;
|
|
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"}
|
package/dist/abstract-actor.js
CHANGED
|
@@ -18,61 +18,49 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { Actor } from "xstate";
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* interface DashboardCtx {
|
|
34
|
+
* username: string;
|
|
35
|
+
* params: Record<string, string>;
|
|
36
|
+
* query: Record<string, string>;
|
|
48
37
|
* }
|
|
49
|
-
* ```
|
|
50
38
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
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
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
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
|
-
* @
|
|
74
|
-
* @
|
|
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,
|
|
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
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,
|
|
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
|
|
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,
|
|
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
|
+
"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
|
-
"
|
|
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
|
-
"
|
|
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.
|
|
47
|
-
"@xmachines/play-signals": "1.0.0-beta.
|
|
48
|
-
"xstate": "^5.
|
|
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
|
}
|