@xmachines/docs 1.0.0-beta.22 → 1.0.0-beta.24
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/api/@xmachines/play/README.md +1 -1
- package/api/@xmachines/play/classes/PlayError.md +4 -4
- package/api/@xmachines/play/type-aliases/PlayEvent.md +3 -3
- package/api/@xmachines/play-actor/README.md +2 -2
- package/api/@xmachines/play-actor/classes/AbstractActor.md +4 -4
- package/api/@xmachines/play-actor/interfaces/PlaySpec.md +2 -2
- package/api/@xmachines/play-actor/interfaces/Routable.md +3 -3
- package/api/@xmachines/play-actor/interfaces/ViewMetadata.md +3 -3
- package/api/@xmachines/play-actor/interfaces/Viewable.md +2 -2
- package/api/@xmachines/play-dom/classes/PlayRenderer.md +4 -4
- package/api/@xmachines/play-dom/functions/connectRenderer.md +1 -1
- package/api/@xmachines/play-dom/functions/renderSpec.md +1 -1
- package/api/@xmachines/play-dom/interfaces/ConnectRendererOptions.md +7 -7
- package/api/@xmachines/play-dom/interfaces/DomRenderContext.md +7 -7
- package/api/@xmachines/play-dom/interfaces/PlayDomOptions.md +3 -3
- package/api/@xmachines/play-dom/type-aliases/DomComponentRenderer.md +1 -1
- package/api/@xmachines/play-dom/type-aliases/DomRegistry.md +1 -1
- package/api/@xmachines/play-dom-router/README.md +152 -0
- package/api/@xmachines/{play-router → play-dom-router}/functions/connectRouter.md +8 -4
- package/api/@xmachines/{play-router → play-dom-router}/functions/createBrowserHistory.md +2 -2
- package/api/@xmachines/{play-router → play-dom-router}/functions/createRouter.md +12 -34
- package/api/@xmachines/{play-router → play-dom-router}/interfaces/BrowserHistory.md +17 -17
- package/api/@xmachines/{play-router → play-dom-router}/interfaces/BrowserWindow.md +17 -17
- package/api/@xmachines/play-dom-router/interfaces/ConnectRouterOptions.md +15 -0
- package/api/@xmachines/play-dom-router/interfaces/VanillaRouter.md +28 -0
- package/api/@xmachines/{play-router-demo → play-dom-router-demo}/README.md +20 -16
- package/api/@xmachines/play-react/README.md +1 -1
- package/api/@xmachines/play-react/classes/PlayErrorBoundary.md +5 -5
- package/api/@xmachines/play-react/functions/useActor.md +1 -1
- package/api/@xmachines/play-react/functions/useSignalEffect.md +1 -1
- package/api/@xmachines/play-react/interfaces/PlayErrorBoundaryProps.md +4 -4
- package/api/@xmachines/play-react/interfaces/PlayErrorBoundaryState.md +3 -3
- package/api/@xmachines/play-react/interfaces/PlayRendererProps.md +7 -7
- package/api/@xmachines/play-react/type-aliases/PlayActor.md +1 -1
- package/api/@xmachines/play-react/variables/PlayRenderer.md +1 -1
- package/api/@xmachines/play-react-router/classes/ReactRouterBridge.md +21 -21
- package/api/@xmachines/play-react-router/classes/RouteMap.md +4 -4
- package/api/@xmachines/play-react-router/functions/PlayRouterProvider.md +1 -1
- package/api/@xmachines/play-react-router/functions/createRouteMapFromTree.md +1 -1
- package/api/@xmachines/play-react-router/interfaces/PlayRouteEvent.md +7 -7
- package/api/@xmachines/play-react-router/interfaces/PlayRouterProviderProps.md +5 -5
- package/api/@xmachines/play-react-router/interfaces/RouteMapping.md +3 -3
- package/api/@xmachines/play-react-router/interfaces/RouterBridge.md +4 -4
- package/api/@xmachines/play-router/README.md +8 -13
- package/api/@xmachines/play-router/classes/BaseRouteMap.md +4 -4
- package/api/@xmachines/play-router/classes/RouterBridgeBase.md +21 -21
- package/api/@xmachines/play-router/functions/buildPlayRouteEvent.md +32 -0
- package/api/@xmachines/play-router/functions/buildRouteTree.md +1 -1
- package/api/@xmachines/play-router/functions/createRouteMap.md +1 -1
- package/api/@xmachines/play-router/functions/detectDuplicateRoutes.md +1 -1
- package/api/@xmachines/play-router/functions/extractMachineRoutes.md +1 -1
- package/api/@xmachines/play-router/functions/extractQuery.md +22 -0
- package/api/@xmachines/play-router/functions/findRouteById.md +1 -1
- package/api/@xmachines/play-router/functions/findRouteByPath.md +1 -1
- package/api/@xmachines/play-router/functions/getNavigableRoutes.md +1 -1
- package/api/@xmachines/play-router/functions/getRoutableRoutes.md +1 -1
- package/api/@xmachines/play-router/functions/getTransitionReachableRoutes.md +1 -1
- package/api/@xmachines/play-router/functions/isRouteReachable.md +1 -1
- package/api/@xmachines/play-router/functions/machineToGraph.md +1 -1
- package/api/@xmachines/play-router/functions/routeExists.md +1 -1
- package/api/@xmachines/play-router/functions/sanitizePathname.md +1 -1
- package/api/@xmachines/play-router/functions/validateRouteFormat.md +1 -1
- package/api/@xmachines/play-router/functions/validateStateExists.md +1 -1
- package/api/@xmachines/play-router/interfaces/BaseRouteMapping.md +3 -3
- package/api/@xmachines/play-router/interfaces/BuildPlayRouteEventOptions.md +13 -0
- package/api/@xmachines/play-router/interfaces/MachineEdgeData.md +3 -3
- package/api/@xmachines/play-router/interfaces/MachineNodeData.md +5 -5
- package/api/@xmachines/play-router/interfaces/PlayRouteEvent.md +7 -7
- package/api/@xmachines/play-router/interfaces/RouteInfo.md +8 -8
- package/api/@xmachines/play-router/interfaces/RouteMap.md +4 -4
- package/api/@xmachines/play-router/interfaces/RouteMatch.md +12 -0
- package/api/@xmachines/play-router/interfaces/RouteNode.md +10 -10
- package/api/@xmachines/play-router/interfaces/RouteObject.md +2 -2
- package/api/@xmachines/play-router/interfaces/RouteTree.md +5 -5
- package/api/@xmachines/play-router/interfaces/RouteWatcherHandle.md +3 -3
- package/api/@xmachines/play-router/interfaces/RouterBridge.md +4 -4
- package/api/@xmachines/play-router/type-aliases/MachineGraph.md +1 -1
- package/api/@xmachines/play-router/type-aliases/RouteMetadata.md +1 -1
- package/api/@xmachines/play-signals/README.md +2 -2
- package/api/@xmachines/play-signals/functions/watchSignal.md +1 -1
- package/api/@xmachines/play-signals/interfaces/ComputedOptions.md +2 -2
- package/api/@xmachines/play-signals/interfaces/SignalComputed.md +2 -2
- package/api/@xmachines/play-signals/interfaces/SignalOptions.md +2 -2
- package/api/@xmachines/play-signals/interfaces/SignalState.md +3 -3
- package/api/@xmachines/play-signals/interfaces/SignalWatcher.md +4 -4
- package/api/@xmachines/play-signals/type-aliases/WatcherNotify.md +1 -1
- package/api/@xmachines/play-solid/README.md +1 -1
- package/api/@xmachines/play-solid/functions/useActor.md +1 -1
- package/api/@xmachines/play-solid/interfaces/PlayRendererProps.md +7 -7
- package/api/@xmachines/play-solid/type-aliases/PlayActor.md +1 -1
- package/api/@xmachines/play-solid/variables/PlayRenderer.md +1 -1
- package/api/@xmachines/play-solid-router/README.md +1 -1
- package/api/@xmachines/play-solid-router/classes/RouteMap.md +4 -4
- package/api/@xmachines/play-solid-router/classes/SolidRouterBridge.md +22 -22
- package/api/@xmachines/play-solid-router/functions/PlayRouterProvider.md +1 -1
- package/api/@xmachines/play-solid-router/functions/createRouteMap.md +1 -1
- package/api/@xmachines/play-solid-router/interfaces/AbstractActor.md +4 -4
- package/api/@xmachines/play-solid-router/interfaces/PlayRouteEvent.md +7 -7
- package/api/@xmachines/play-solid-router/interfaces/PlayRouterProviderProps.md +5 -5
- package/api/@xmachines/play-solid-router/interfaces/RouteMapping.md +3 -3
- package/api/@xmachines/play-solid-router/interfaces/RouterBridge.md +4 -4
- package/api/@xmachines/play-solid-router/type-aliases/RoutableActor.md +1 -1
- package/api/@xmachines/play-solid-router/type-aliases/SolidRouterHooks.md +4 -4
- package/api/@xmachines/play-tanstack-react-router/classes/RouteMap.md +4 -4
- package/api/@xmachines/play-tanstack-react-router/classes/TanStackReactRouterBridge.md +21 -21
- package/api/@xmachines/play-tanstack-react-router/functions/PlayRouterProvider.md +1 -1
- package/api/@xmachines/play-tanstack-react-router/functions/createRouteMap.md +1 -1
- package/api/@xmachines/play-tanstack-react-router/functions/createRouteMapFromTree.md +1 -1
- package/api/@xmachines/play-tanstack-react-router/functions/extractMachineRoutes.md +1 -1
- package/api/@xmachines/play-tanstack-react-router/functions/extractParams.md +1 -1
- package/api/@xmachines/play-tanstack-react-router/functions/extractQueryParams.md +1 -1
- package/api/@xmachines/play-tanstack-react-router/interfaces/PlayRouteEvent.md +7 -7
- package/api/@xmachines/play-tanstack-react-router/interfaces/PlayRouterProviderProps.md +5 -5
- package/api/@xmachines/play-tanstack-react-router/interfaces/RouteMapping.md +3 -3
- package/api/@xmachines/play-tanstack-react-router/interfaces/RouteNavigateEvent.md +3 -3
- package/api/@xmachines/play-tanstack-react-router/interfaces/RouterBridge.md +4 -4
- package/api/@xmachines/play-tanstack-react-router/type-aliases/TanStackRouterInstance.md +1 -1
- package/api/@xmachines/play-tanstack-react-router/type-aliases/TanStackRouterLike.md +4 -4
- package/api/@xmachines/play-tanstack-solid-router/README.md +1 -1
- package/api/@xmachines/play-tanstack-solid-router/classes/RouteMap.md +4 -4
- package/api/@xmachines/play-tanstack-solid-router/classes/SolidRouterBridge.md +22 -22
- package/api/@xmachines/play-tanstack-solid-router/functions/PlayRouterProvider.md +1 -1
- package/api/@xmachines/play-tanstack-solid-router/functions/createRouteMap.md +1 -1
- package/api/@xmachines/play-tanstack-solid-router/interfaces/PlayRouteEvent.md +7 -7
- package/api/@xmachines/play-tanstack-solid-router/interfaces/PlayRouterProviderProps.md +5 -5
- package/api/@xmachines/play-tanstack-solid-router/interfaces/RouteMapping.md +3 -3
- package/api/@xmachines/play-tanstack-solid-router/interfaces/RouterBridge.md +4 -4
- package/api/@xmachines/play-tanstack-solid-router/type-aliases/RoutableActor.md +1 -1
- package/api/@xmachines/play-tanstack-solid-router/type-aliases/TanStackRouterInstance.md +1 -1
- package/api/@xmachines/play-tanstack-solid-router/type-aliases/TanStackRouterLike.md +4 -4
- package/api/@xmachines/play-vue/README.md +1 -1
- package/api/@xmachines/play-vue/functions/defineRegistry.md +1 -1
- package/api/@xmachines/play-vue/functions/useActor.md +1 -1
- package/api/@xmachines/play-vue/interfaces/PlayRendererProps.md +5 -5
- package/api/@xmachines/play-vue/type-aliases/ComponentEntry.md +1 -1
- package/api/@xmachines/play-vue/type-aliases/ComponentsMap.md +1 -1
- package/api/@xmachines/play-vue/type-aliases/DefineRegistryOptions.md +2 -2
- package/api/@xmachines/play-vue/type-aliases/PlayActor.md +1 -1
- package/api/@xmachines/play-vue/variables/PlayRenderer.md +1 -1
- package/api/@xmachines/play-vue-router/README.md +1 -1
- package/api/@xmachines/play-vue-router/classes/RouteMap.md +7 -7
- package/api/@xmachines/play-vue-router/classes/VueBaseRouteMap.md +7 -7
- package/api/@xmachines/play-vue-router/classes/VueRouterBridge.md +22 -22
- package/api/@xmachines/play-vue-router/functions/createRouteMap.md +1 -1
- package/api/@xmachines/play-vue-router/interfaces/PlayRouteEvent.md +7 -7
- package/api/@xmachines/play-vue-router/interfaces/RouteMapping.md +4 -4
- package/api/@xmachines/play-vue-router/interfaces/RouterBridge.md +4 -4
- package/api/@xmachines/play-vue-router/type-aliases/RoutableActor.md +1 -1
- package/api/@xmachines/play-vue-router/variables/PlayRouterProvider.md +1 -1
- package/api/@xmachines/play-xstate/README.md +1 -1
- package/api/@xmachines/play-xstate/classes/PlayerActor.md +12 -12
- package/api/@xmachines/play-xstate/functions/buildRouteUrl.md +1 -1
- package/api/@xmachines/play-xstate/functions/composeGuards.md +1 -1
- package/api/@xmachines/play-xstate/functions/composeGuardsOr.md +1 -1
- package/api/@xmachines/play-xstate/functions/contextFieldMatches.md +1 -1
- package/api/@xmachines/play-xstate/functions/definePlayer.md +2 -2
- package/api/@xmachines/play-xstate/functions/deriveRoute.md +2 -2
- package/api/@xmachines/play-xstate/functions/eventMatches.md +1 -1
- package/api/@xmachines/play-xstate/functions/formatPlayRouteTransitions.md +1 -1
- package/api/@xmachines/play-xstate/functions/hasContext.md +1 -1
- package/api/@xmachines/play-xstate/functions/isAbsoluteRoute.md +1 -1
- package/api/@xmachines/play-xstate/functions/negateGuard.md +1 -1
- package/api/@xmachines/play-xstate/interfaces/PlayerConfig.md +3 -3
- package/api/@xmachines/play-xstate/interfaces/PlayerFactoryResumeOptions.md +2 -2
- package/api/@xmachines/play-xstate/interfaces/PlayerOptions.md +6 -6
- package/api/@xmachines/play-xstate/interfaces/RouteContext.md +5 -5
- package/api/@xmachines/play-xstate/type-aliases/ComposedGuard.md +1 -1
- package/api/@xmachines/play-xstate/type-aliases/Guard.md +1 -1
- package/api/@xmachines/play-xstate/type-aliases/GuardArray.md +1 -1
- package/api/@xmachines/play-xstate/type-aliases/PlayerFactory.md +1 -1
- package/api/@xmachines/play-xstate/type-aliases/RouteMachineConfig.md +4 -4
- package/api/@xmachines/play-xstate/type-aliases/RouteStateNode.md +4 -4
- package/api/@xmachines/shared/functions/defineXmVitestConfig.md +2 -2
- package/api/@xmachines/shared/functions/xmAliases.md +1 -1
- package/api/README.md +2 -1
- package/api/llms.txt +1 -0
- package/examples/README.md +1 -1
- package/examples/multi-router-integration.md +36 -26
- package/package.json +4 -2
- package/api/@xmachines/play-router/interfaces/ConnectRouterOptions.md +0 -13
- package/api/@xmachines/play-router/interfaces/VanillaRouter.md +0 -28
- package/api/rfc/play.md +0 -447
package/api/rfc/play.md
DELETED
|
@@ -1,447 +0,0 @@
|
|
|
1
|
-
# RFC: Play
|
|
2
|
-
|
|
3
|
-
**Status:** Living
|
|
4
|
-
**Version:** 1.1
|
|
5
|
-
**Scope:** Universal runtime interoperability and reference implementation
|
|
6
|
-
**Non-goals:** Persistence, transport protocols, alternative state engines, non-signal reactivity
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## 1. Purpose
|
|
11
|
-
|
|
12
|
-
This RFC defines the **Universal Player Architecture** and its reference implementation. The architecture establishes a design pattern that strictly separates **Business Logic (The Actor)** from **Infrastructure (The Runtime Adapter and View)**.
|
|
13
|
-
|
|
14
|
-
The reference implementation provides a modular monorepo that satisfies the architectural constraints of **Runtime Agnosticism** and **Logic-Driven Guarding**. It leverages **Standardized Signals (TC39)** to glue a specific **State Engine (XState v5)** to multiple **Runtime Adapters** (TanStack Router, React Router, Vue Router, SolidJS Router) and **View Layers** (React, Vue, SolidJS, Vanilla DOM — all via JSON-Render), while ensuring that **business logic remains the single source of truth** for navigation, state, and UI structure.
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## 2. Architecture Model
|
|
19
|
-
|
|
20
|
-
### 2.1 Roles
|
|
21
|
-
|
|
22
|
-
- **The Actor (Logic Engine)**
|
|
23
|
-
- Pure, environment-agnostic logic runtime
|
|
24
|
-
- Owns state, guards, errors, and route validity
|
|
25
|
-
- Emits _Virtual Routes_ as derived intent
|
|
26
|
-
|
|
27
|
-
- **The Runtime Adapter (Infrastructure Layer)**
|
|
28
|
-
- Environment-specific adapter (Browser, Native, Server, Test Runner)
|
|
29
|
-
- Reflects Actor output into the environment
|
|
30
|
-
- Forwards environment events to the Actor without interpretation
|
|
31
|
-
|
|
32
|
-
- **The View**
|
|
33
|
-
- Passive consumer of Actor state
|
|
34
|
-
- No business rules or routing authority
|
|
35
|
-
|
|
36
|
-
### 2.2 Communication Medium
|
|
37
|
-
|
|
38
|
-
- **Signals (TC39 Proposal)**
|
|
39
|
-
- Used exclusively for Actor ↔ Adapter communication
|
|
40
|
-
- Enables synchronous, glitch-free propagation
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## 3. Invariants
|
|
45
|
-
|
|
46
|
-
1. **Actor Authority**
|
|
47
|
-
The Actor is the final authority on state and route validity.
|
|
48
|
-
|
|
49
|
-
2. **Strict Separation**
|
|
50
|
-
Business logic never depends on runtime APIs or routing libraries.
|
|
51
|
-
|
|
52
|
-
3. **Signal-Only Reactivity**
|
|
53
|
-
All cross-boundary communication uses standardized Signals.
|
|
54
|
-
|
|
55
|
-
4. **Passive Infrastructure**
|
|
56
|
-
Runtime Adapters do not enforce guards, validation, or business rules.
|
|
57
|
-
|
|
58
|
-
5. **State-Driven Reset**
|
|
59
|
-
Invalid external navigation is always overwritten by Actor-derived state.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## 4. Core Mechanisms
|
|
64
|
-
|
|
65
|
-
### 4.1 Reactive Substrate (Signals)
|
|
66
|
-
|
|
67
|
-
- **Push–Pull Model**
|
|
68
|
-
The Actor pushes state updates; Adapters and Views pull computed values lazily.
|
|
69
|
-
|
|
70
|
-
- **Glitch-Free Execution**
|
|
71
|
-
Updates are synchronous and atomic, preventing intermediate invalid states from leaking into the environment.
|
|
72
|
-
|
|
73
|
-
### 4.2 Logic Engine (The Actor)
|
|
74
|
-
|
|
75
|
-
- Defines **Virtual Routes** as metadata on state nodes
|
|
76
|
-
- Validates all incoming navigation intents
|
|
77
|
-
- On invalid intent:
|
|
78
|
-
1. Transitions to an error or fallback state
|
|
79
|
-
2. Emits a corresponding Virtual Route (e.g. `/error`, `/login`)
|
|
80
|
-
3. Forces the environment to realign with Actor state
|
|
81
|
-
|
|
82
|
-
The Actor has zero knowledge of:
|
|
83
|
-
|
|
84
|
-
- Browser APIs
|
|
85
|
-
- Routing libraries
|
|
86
|
-
- History mechanisms
|
|
87
|
-
- View frameworks
|
|
88
|
-
|
|
89
|
-
### 4.3 Infrastructure Layer (Runtime Adapter)
|
|
90
|
-
|
|
91
|
-
- **Watcher (Output)**
|
|
92
|
-
Observes the Actor's _Intended Route_ signal and updates the environment accordingly.
|
|
93
|
-
|
|
94
|
-
- **Reflector (Input)**
|
|
95
|
-
Listens to environment events (e.g. back/forward navigation) and forwards them as intents to the Actor.
|
|
96
|
-
|
|
97
|
-
If an intent is rejected:
|
|
98
|
-
|
|
99
|
-
- The Actor emits a new valid state
|
|
100
|
-
- The Adapter overwrites the external environment to match Actor reality
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
## 5. Package Model
|
|
105
|
-
|
|
106
|
-
### 5.1 Core Layer
|
|
107
|
-
|
|
108
|
-
#### 5.1.1 `@xmachines/play-signals`
|
|
109
|
-
|
|
110
|
-
**Role:** Reactive Substrate
|
|
111
|
-
|
|
112
|
-
Wraps the TC39 Signals (Stage 1) polyfill. By isolating the reactive primitive within this package, the ecosystem is protected from specification churn while providing the ergonomic API surface required by the rest of the architecture.
|
|
113
|
-
|
|
114
|
-
**Exports:**
|
|
115
|
-
|
|
116
|
-
- `Signal` — Re-exported namespace from `signal-polyfill`:
|
|
117
|
-
- `Signal.State` — Actor output snapshot
|
|
118
|
-
- `Signal.Computed` — Lazy, pull-based derivation of routes and views
|
|
119
|
-
- `Signal.subtle.Watcher` — Synchronous observation for Runtime Adapters
|
|
120
|
-
- `watchSignal` — Convenience function: subscribes to a single signal with a one-shot watcher lifecycle and microtask batching; returns a cleanup function
|
|
121
|
-
|
|
122
|
-
#### 5.1.2 `@xmachines/play`
|
|
123
|
-
|
|
124
|
-
**Role:** Core Protocols
|
|
125
|
-
|
|
126
|
-
Exports the minimal shared contracts that allow the Logic Engine and Infrastructure to communicate without direct dependencies. This package is intentionally narrow — it defines only the base event contract and structured error type.
|
|
127
|
-
|
|
128
|
-
**Exports:**
|
|
129
|
-
|
|
130
|
-
- `PlayEvent<TPayload>` — Generic intent event dispatched from Infrastructure to Actor. Any object with `{ readonly type: string }` plus optional typed payload fields.
|
|
131
|
-
- `PlayError` — Structured error base with `scope` and `code` fields for consistent error handling across all `@xmachines/*` packages.
|
|
132
|
-
|
|
133
|
-
> **Note:** Routing contracts (`RouterBridge`, `PlayRouteEvent`) live in `@xmachines/play-router`, not here. This keeps the core protocol package dependency-free.
|
|
134
|
-
|
|
135
|
-
#### 5.1.3 `@xmachines/play-actor`
|
|
136
|
-
|
|
137
|
-
**Role:** Abstract Logic Engine
|
|
138
|
-
|
|
139
|
-
Defines the minimal base class that all logic adapters must implement. By extending the XState `Actor` class, the `AbstractActor` remains fully compatible with the XState ecosystem (inspection, system registration, message passing) while enforcing the Play Architecture's **push–pull signal contract**.
|
|
140
|
-
|
|
141
|
-
The base class requires only two abstract members: **reactive state** and **typed event dispatch**. Routing and view rendering are opt-in via separate interfaces, allowing actors to compose only the capabilities they need.
|
|
142
|
-
|
|
143
|
-
```ts
|
|
144
|
-
import { Actor, type AnyActorLogic, type EventObject } from "xstate";
|
|
145
|
-
import type { Signal } from "@xmachines/play-signals";
|
|
146
|
-
import type { Spec } from "@json-render/core";
|
|
147
|
-
|
|
148
|
-
// Optional capability: Routing
|
|
149
|
-
export interface Routable {
|
|
150
|
-
readonly currentRoute: Signal.Computed<string | null>;
|
|
151
|
-
readonly initialRoute: string | null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Optional capability: View rendering
|
|
155
|
-
export interface PlaySpec extends Spec {
|
|
156
|
-
contextProps?: string[];
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export interface ViewMetadata {
|
|
160
|
-
component: string;
|
|
161
|
-
spec: PlaySpec;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export interface Viewable {
|
|
165
|
-
readonly currentView: Signal.State<ViewMetadata | null>;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// The Base Protocol — minimal contract
|
|
169
|
-
export abstract class AbstractActor<
|
|
170
|
-
TLogic extends AnyActorLogic,
|
|
171
|
-
TEvent extends EventObject = EventObject,
|
|
172
|
-
> extends Actor<TLogic> {
|
|
173
|
-
// Reactive Output (Snapshot)
|
|
174
|
-
public abstract state: Signal.State<unknown>;
|
|
175
|
-
|
|
176
|
-
// Input Channel (typed for specific event shapes)
|
|
177
|
-
public abstract override send(event: TEvent): void;
|
|
178
|
-
}
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
**Design Rationale:** The original design placed `currentRoute`, `currentView`, and `catalog` directly on `AbstractActor`. The implementation extracted these into `Routable` and `Viewable` interfaces because:
|
|
182
|
-
|
|
183
|
-
1. Not all actors need routing (e.g. embedded sub-actors)
|
|
184
|
-
2. Not all actors need view rendering (e.g. background logic actors)
|
|
185
|
-
3. Catalog/schema concerns are handled by the renderer layer (`@json-render/core`), not the actor
|
|
186
|
-
|
|
187
|
-
### 5.2 Logic Layer
|
|
188
|
-
|
|
189
|
-
#### 5.2.1 `@xmachines/play-xstate`
|
|
190
|
-
|
|
191
|
-
**Role:** Concrete Logic Adapter (XState v5)
|
|
192
|
-
|
|
193
|
-
Wraps XState v5 to satisfy the `AbstractActor` contract. Provides the primary API for creating actors from state machine definitions.
|
|
194
|
-
|
|
195
|
-
**Primary Exports:**
|
|
196
|
-
|
|
197
|
-
- `definePlayer` — Factory builder: accepts a machine config, returns a factory function that creates `PlayerActor` instances
|
|
198
|
-
- `PlayerActor` — Concrete class extending `AbstractActor` and implementing both `Routable` and `Viewable`
|
|
199
|
-
|
|
200
|
-
**Utility Exports:**
|
|
201
|
-
|
|
202
|
-
- Guard combinators: `composeGuards`, `composeGuardsOr`, `negateGuard`, `hasContext`, `eventMatches`, `contextFieldMatches`
|
|
203
|
-
- Route derivation: `deriveRoute`, `isAbsoluteRoute`, `buildRouteUrl`, `formatPlayRouteTransitions`
|
|
204
|
-
|
|
205
|
-
**Responsibilities:**
|
|
206
|
-
|
|
207
|
-
- Derive `currentRoute` and `currentView` from active state node metadata
|
|
208
|
-
- Manage signal lifecycle (create on start, dispose on stop)
|
|
209
|
-
- Support snapshot restore for resumable sessions
|
|
210
|
-
- Provide XState DevTools compatibility
|
|
211
|
-
|
|
212
|
-
```ts
|
|
213
|
-
import type { AnyStateMachine, InputFrom } from "xstate";
|
|
214
|
-
|
|
215
|
-
interface PlayerConfig<TMachine extends AnyStateMachine> {
|
|
216
|
-
machine: TMachine;
|
|
217
|
-
options?: PlayerOptions<TMachine>;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// definePlayer returns a factory function, not an object
|
|
221
|
-
const definePlayer = <TMachine extends AnyStateMachine>(
|
|
222
|
-
config: PlayerConfig<TMachine>,
|
|
223
|
-
): PlayerFactory<TMachine> => {
|
|
224
|
-
return (input?, restore?) => new PlayerActor(machine, options, input, restore?.snapshot);
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
// PlayerActor composes all capabilities
|
|
228
|
-
class PlayerActor<TMachine extends AnyStateMachine>
|
|
229
|
-
extends AbstractActor<AnyActorLogic, EventFromLogic<TMachine>>
|
|
230
|
-
implements Routable, Viewable
|
|
231
|
-
{
|
|
232
|
-
public state: Signal.State<AnyMachineSnapshot>;
|
|
233
|
-
public currentRoute: Signal.Computed<string | null>;
|
|
234
|
-
public currentView: Signal.State<ViewMetadata | null>;
|
|
235
|
-
public readonly initialRoute: string | null;
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
**Design Rationale:** The original design passed a `catalog` to `definePlayer` and returned `{ create, catalog }`. The implementation decoupled catalogs entirely — component schemas are defined via `@json-render/core`'s `defineCatalog` and bound to renderers via `defineRegistry`, not to actors. This means a single actor can be rendered by different component libraries without change.
|
|
240
|
-
|
|
241
|
-
### 5.3 Router Layer
|
|
242
|
-
|
|
243
|
-
#### 5.3.1 `@xmachines/play-router`
|
|
244
|
-
|
|
245
|
-
**Role:** Route Infrastructure
|
|
246
|
-
|
|
247
|
-
Provides the complete routing infrastructure: route extraction from state machines, bidirectional route mapping, and the abstract `RouterBridgeBase` that all framework-specific router adapters extend.
|
|
248
|
-
|
|
249
|
-
**Protocol Exports:**
|
|
250
|
-
|
|
251
|
-
- `RouterBridge` — Interface: `{ connect(): void; disconnect(): void }`
|
|
252
|
-
- `PlayRouteEvent` — Routing event: `{ type: 'play.route'; to: string; params?; query?; match? }`
|
|
253
|
-
- `RouterBridgeBase` — Abstract base class that encapsulates common bridge logic (signal watching, route synchronization, param/query extraction). Subclasses implement only three methods:
|
|
254
|
-
- `navigateRouter(path)` — Push a path into the framework router
|
|
255
|
-
- `watchRouterChanges()` — Subscribe to framework router navigation events
|
|
256
|
-
- `unwatchRouterChanges()` — Unsubscribe from framework router events
|
|
257
|
-
|
|
258
|
-
**Route Extraction Exports:**
|
|
259
|
-
|
|
260
|
-
- `RouteNode`, `RouteTree`, `RouteInfo` — Recursive route tree types
|
|
261
|
-
- `crawlMachine` — BFS traversal of state machine graph
|
|
262
|
-
- `extractMachineRoutes` — Build complete route tree from a machine definition
|
|
263
|
-
- `buildRouteTree` — Construct hierarchical tree from flat route info
|
|
264
|
-
- `createRouteMap` / `RouteMap` — Bidirectional path ↔ state ID resolution
|
|
265
|
-
- `BaseRouteMap` / `BaseRouteMapping` — Shared base for adapter-specific route maps
|
|
266
|
-
|
|
267
|
-
**Validation & Query Exports:**
|
|
268
|
-
|
|
269
|
-
- `validateRouteFormat`, `validateStateExists`, `detectDuplicateRoutes`
|
|
270
|
-
- `getNavigableRoutes`, `getRoutableRoutes`, `routeExists`
|
|
271
|
-
- `findRouteById`, `findRouteByPath`
|
|
272
|
-
|
|
273
|
-
**Browser Primitives:**
|
|
274
|
-
|
|
275
|
-
- `createBrowserHistory` / `BrowserHistory` — Browser history abstraction
|
|
276
|
-
- `createRouter` / `VanillaRouter` — Framework-agnostic vanilla router
|
|
277
|
-
- `connectRouter` — Low-level pure browser integration
|
|
278
|
-
|
|
279
|
-
#### 5.3.2 Framework Router Adapters
|
|
280
|
-
|
|
281
|
-
Each adapter extends `RouterBridgeBase` and implements the three abstract methods for its target router. All share the same architectural pattern:
|
|
282
|
-
|
|
283
|
-
| Package | Router | Framework |
|
|
284
|
-
| --------------------------------------- | --------------- | --------- |
|
|
285
|
-
| `@xmachines/play-tanstack-react-router` | TanStack Router | React |
|
|
286
|
-
| `@xmachines/play-tanstack-solid-router` | TanStack Router | SolidJS |
|
|
287
|
-
| `@xmachines/play-react-router` | React Router v7 | React |
|
|
288
|
-
| `@xmachines/play-vue-router` | Vue Router 4/5 | Vue |
|
|
289
|
-
| `@xmachines/play-solid-router` | SolidJS Router | SolidJS |
|
|
290
|
-
|
|
291
|
-
**Runtime Adapter Responsibilities (all adapters):**
|
|
292
|
-
|
|
293
|
-
- **Input (Reflector):** Listen to router navigation events, extract params/query, send `PlayRouteEvent` to Actor
|
|
294
|
-
- **Output (Watcher):** Observe Actor's `currentRoute` signal, call `navigateRouter()` when it changes
|
|
295
|
-
- **Reset:** If Actor rejects a navigation (guard failure), the adapter overwrites the URL bar to match Actor state
|
|
296
|
-
|
|
297
|
-
### 5.4 View Layer
|
|
298
|
-
|
|
299
|
-
#### 5.4.1 View Renderers
|
|
300
|
-
|
|
301
|
-
Each view renderer provides a `PlayRenderer` component that passively observes the actor's `currentView` signal and projects the JSON spec into framework-native components via `@json-render`.
|
|
302
|
-
|
|
303
|
-
| Package | Framework | JSON-Render Backend |
|
|
304
|
-
| ----------------------- | ----------- | -------------------- |
|
|
305
|
-
| `@xmachines/play-react` | React | `@json-render/react` |
|
|
306
|
-
| `@xmachines/play-vue` | Vue 3 | `@json-render/vue` |
|
|
307
|
-
| `@xmachines/play-solid` | SolidJS | `@json-render/solid` |
|
|
308
|
-
| `@xmachines/play-dom` | Vanilla DOM | `@json-render/core` |
|
|
309
|
-
|
|
310
|
-
**Shared Architecture (all renderers):**
|
|
311
|
-
|
|
312
|
-
1. Subscribe to `actor.currentView` signal
|
|
313
|
-
2. Resolve a `StateStore` (external or auto-created per view transition via `@xstate/store`)
|
|
314
|
-
3. Wire `actions` prop to `actor.send()` via `ActionProvider`
|
|
315
|
-
4. Render `spec` through the framework's `Renderer` with a `registry` (defined via `defineRegistry`)
|
|
316
|
-
|
|
317
|
-
**`@xmachines/play-react` exports (representative):**
|
|
318
|
-
|
|
319
|
-
```ts
|
|
320
|
-
// Main component
|
|
321
|
-
export const PlayRenderer: React.FC<PlayRendererProps> = ({
|
|
322
|
-
actor, // AbstractActor & Viewable
|
|
323
|
-
registry, // ComponentRegistry from defineRegistry()
|
|
324
|
-
store?, // Optional external StateStore (controlled mode)
|
|
325
|
-
fallback?, // Shown when currentView is null
|
|
326
|
-
actions?, // Map of action names → XState event type strings
|
|
327
|
-
}) => { /* ... */ };
|
|
328
|
-
|
|
329
|
-
// Hooks
|
|
330
|
-
export { useSignalEffect } from './useSignalEffect';
|
|
331
|
-
export { useActor } from './useActor';
|
|
332
|
-
|
|
333
|
-
// Error handling
|
|
334
|
-
export { PlayErrorBoundary } from './PlayErrorBoundary';
|
|
335
|
-
|
|
336
|
-
// Re-exports from @json-render/react
|
|
337
|
-
export { defineRegistry, useStateBinding, useBoundProp } from '@json-render/react';
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
**Design Rationale:** The original design passed `components: ComponentMap<TCatalog>` and `catalog: Catalog` directly to `PlayRenderer`. The implementation replaced these with a single `registry` (from `defineRegistry`), which bakes catalog validation into the registry at definition time. This eliminates runtime catalog lookups and provides better TypeScript inference.
|
|
341
|
-
|
|
342
|
-
### 5.5 Schema Layer (External)
|
|
343
|
-
|
|
344
|
-
Component schemas and UI vocabularies are defined via `@json-render/core`, an external package not in the `@xmachines` scope.
|
|
345
|
-
|
|
346
|
-
- `defineCatalog` — Defines available components and prop schemas (via Zod)
|
|
347
|
-
- `defineRegistry` — Binds a catalog to framework-specific component implementations (available from each `@json-render/*` framework package)
|
|
348
|
-
|
|
349
|
-
The original RFC proposed an `@xmachines/play-ui` package for this purpose. The implementation delegates to `@json-render/core` instead, which provides the same functionality with broader ecosystem support.
|
|
350
|
-
|
|
351
|
-
---
|
|
352
|
-
|
|
353
|
-
## 6. Usage Model
|
|
354
|
-
|
|
355
|
-
### 6.1 Schema Definition (`catalog.ts`)
|
|
356
|
-
|
|
357
|
-
Defines the **UI vocabulary** — available components and their prop schemas. No framework code.
|
|
358
|
-
|
|
359
|
-
```ts
|
|
360
|
-
import { defineCatalog, z } from "@json-render/core";
|
|
361
|
-
|
|
362
|
-
export const catalog = defineCatalog({
|
|
363
|
-
components: {
|
|
364
|
-
Dashboard: { props: z.object({ title: z.string() }) },
|
|
365
|
-
Metric: { props: z.object({ value: z.number(), label: z.string() }) },
|
|
366
|
-
},
|
|
367
|
-
});
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
### 6.2 Feature Definition (`dashboard.player.ts`)
|
|
371
|
-
|
|
372
|
-
Defines **Logic**. No framework code. No routing library imports.
|
|
373
|
-
|
|
374
|
-
```ts
|
|
375
|
-
import { setup } from "xstate";
|
|
376
|
-
import { definePlayer } from "@xmachines/play-xstate";
|
|
377
|
-
|
|
378
|
-
export const machine = setup({
|
|
379
|
-
/* types, guards, actions */
|
|
380
|
-
}).createMachine({
|
|
381
|
-
initial: "overview",
|
|
382
|
-
states: {
|
|
383
|
-
overview: {
|
|
384
|
-
meta: {
|
|
385
|
-
route: "/dashboard",
|
|
386
|
-
view: {
|
|
387
|
-
type: "Dashboard",
|
|
388
|
-
props: { title: "Q4 Performance" },
|
|
389
|
-
children: [{ type: "Metric", props: { value: 100, label: "Sales" } }],
|
|
390
|
-
},
|
|
391
|
-
},
|
|
392
|
-
},
|
|
393
|
-
},
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
// definePlayer returns a factory function
|
|
397
|
-
export const createDashboard = definePlayer({ machine });
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
### 6.3 Application (`App.tsx`)
|
|
401
|
-
|
|
402
|
-
Binds Logic to a View Renderer and Router Adapter.
|
|
403
|
-
|
|
404
|
-
```tsx
|
|
405
|
-
import { createDashboard, machine } from "./features/dashboard/player";
|
|
406
|
-
import { catalog } from "./catalog";
|
|
407
|
-
import { PlayRenderer, defineRegistry } from "@xmachines/play-react";
|
|
408
|
-
import { TanStackReactRouterBridge } from "@xmachines/play-tanstack-react-router";
|
|
409
|
-
import { extractMachineRoutes, createRouteMap } from "@xmachines/play-router";
|
|
410
|
-
|
|
411
|
-
// 1. Create the actor
|
|
412
|
-
const actor = createDashboard();
|
|
413
|
-
|
|
414
|
-
// 2. Define component implementations (type-checked against catalog)
|
|
415
|
-
const registry = defineRegistry(catalog, {
|
|
416
|
-
Dashboard: ({ props, children }) => <div className="p-4">{children}</div>,
|
|
417
|
-
Metric: ({ props }) => (
|
|
418
|
-
<span>
|
|
419
|
-
{props.label}: {props.value}
|
|
420
|
-
</span>
|
|
421
|
-
),
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
// 3. Build route map from machine definition
|
|
425
|
-
const routeTree = extractMachineRoutes(machine);
|
|
426
|
-
const routeMap = createRouteMap(routeTree);
|
|
427
|
-
|
|
428
|
-
// 4. Connect router adapter (router, actor, routeMap)
|
|
429
|
-
const bridge = new TanStackReactRouterBridge(tanstackRouter, actor, routeMap);
|
|
430
|
-
bridge.connect();
|
|
431
|
-
|
|
432
|
-
// 5. Render
|
|
433
|
-
function App() {
|
|
434
|
-
return <PlayRenderer actor={actor} registry={registry} actions={{ submit: "form.submit" }} />;
|
|
435
|
-
}
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
---
|
|
439
|
-
|
|
440
|
-
## 7. Lock Statement
|
|
441
|
-
|
|
442
|
-
> Logic is sovereign.
|
|
443
|
-
> Infrastructure reflects, never decides.
|
|
444
|
-
> Capabilities compose, never prescribe.
|
|
445
|
-
> Logic owns structure and flow.
|
|
446
|
-
> Adapters project, never decide.
|
|
447
|
-
> This is Universal Player.
|