@xmachines/play-solid 1.0.0-beta.9 → 1.1.0
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 +135 -231
- package/dist/ActorProvider.d.ts +79 -0
- package/dist/ActorProvider.d.ts.map +1 -0
- package/dist/PlayRenderer.d.ts +16 -55
- package/dist/PlayRenderer.d.ts.map +1 -1
- package/dist/PlayUIProvider.d.ts +49 -0
- package/dist/PlayUIProvider.d.ts.map +1 -0
- package/dist/index.d.ts +23 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +7 -23
- package/dist/types.d.ts.map +1 -1
- package/dist/useActor.d.ts +32 -0
- package/dist/useActor.d.ts.map +1 -0
- package/package.json +40 -16
- package/dist/PlayRenderer.js +0 -31
- package/dist/PlayRenderer.js.map +0 -1
- package/dist/index.js +0 -2
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
|
@@ -1,299 +1,203 @@
|
|
|
1
1
|
# @xmachines/play-solid
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Solid renderer for XMachines Play architecture
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://www.npmjs.com/package/@xmachines/play-solid)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
SolidJS rendering layer that passively observes actor signals and renders UI components via `@xmachines/json-render-solid`. SolidJS reactivity is used solely to trigger re-renders — TC39 Signals are the source of truth.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Per [RFC Play v1](https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md), this package implements:
|
|
12
|
-
|
|
13
|
-
- **Signal-Only Reactivity (INV-05):** No createSignal/createStore for business logic, TC39 signals only
|
|
14
|
-
- **Passive Infrastructure (INV-04):** Components observe signals, send events to actor
|
|
15
|
-
|
|
16
|
-
**Key Principle:** SolidJS state is never used for business logic. Signals are the source of truth.
|
|
17
|
-
|
|
18
|
-
Renderer receives actor via props (provider pattern), not children.
|
|
10
|
+
Part of the [xmachines-js monorepo](../../README.md).
|
|
19
11
|
|
|
20
12
|
## Installation
|
|
21
13
|
|
|
22
14
|
```bash
|
|
23
|
-
|
|
24
|
-
npm install @xmachines/play-solid
|
|
15
|
+
pnpm add @xmachines/play-solid
|
|
25
16
|
```
|
|
26
17
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- `PlayRenderer`
|
|
30
|
-
- `PlayRendererProps` (type)
|
|
31
|
-
|
|
32
|
-
**Peer dependencies:**
|
|
18
|
+
**Peer dependencies** — install alongside the package:
|
|
33
19
|
|
|
34
|
-
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add solid-js xstate @xstate/store @xmachines/json-render-solid @xmachines/json-render-core @xmachines/json-render-xstate
|
|
22
|
+
```
|
|
35
23
|
|
|
36
24
|
## Quick Start
|
|
37
25
|
|
|
38
26
|
```tsx
|
|
39
|
-
import {
|
|
27
|
+
import { PlayUIProvider, PlayRenderer, defineRegistry } from "@xmachines/play-solid";
|
|
40
28
|
import { definePlayer } from "@xmachines/play-xstate";
|
|
41
|
-
import { defineCatalog } from "@xmachines/
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
29
|
+
import { defineCatalog } from "@xmachines/json-render-core";
|
|
30
|
+
import { schema } from "@xmachines/json-render-solid/schema";
|
|
31
|
+
|
|
32
|
+
// 1. Define a catalog (authCatalogDef is a plain object describing components/actions)
|
|
33
|
+
const catalog = defineCatalog(schema, authCatalogDef);
|
|
34
|
+
|
|
35
|
+
// 2. Build a component registry
|
|
36
|
+
const registryResult = defineRegistry(catalog, {
|
|
37
|
+
components: {
|
|
38
|
+
Home: () => <div>Welcome home!</div>,
|
|
39
|
+
Login: (ctx) => <div>Login {ctx.props.error && <span>{ctx.props.error}</span>}</div>,
|
|
40
|
+
},
|
|
41
|
+
actions: {
|
|
42
|
+
login: async (args) => actor.send({ type: "auth.login", username: args.username }),
|
|
43
|
+
logout: async () => actor.send({ type: "auth.logout" }),
|
|
44
|
+
},
|
|
52
45
|
});
|
|
53
46
|
|
|
54
|
-
//
|
|
55
|
-
const
|
|
56
|
-
LoginForm: (props) => (
|
|
57
|
-
<form
|
|
58
|
-
onSubmit={(e) => {
|
|
59
|
-
e.preventDefault();
|
|
60
|
-
const data = new FormData(e.currentTarget);
|
|
61
|
-
props.send({
|
|
62
|
-
type: "auth.login",
|
|
63
|
-
username: data.get("username"),
|
|
64
|
-
});
|
|
65
|
-
}}
|
|
66
|
-
>
|
|
67
|
-
{props.error && <p style={{ color: "red" }}>{props.error}</p>}
|
|
68
|
-
<input name="username" required placeholder="Username" />
|
|
69
|
-
<button type="submit">Log In</button>
|
|
70
|
-
</form>
|
|
71
|
-
),
|
|
72
|
-
Dashboard: (props) => (
|
|
73
|
-
<div>
|
|
74
|
-
<h1>Welcome, {props.username}!</h1>
|
|
75
|
-
<p>User ID: {props.userId}</p>
|
|
76
|
-
<button onClick={() => props.send({ type: "auth.logout" })}>Log Out</button>
|
|
77
|
-
</div>
|
|
78
|
-
),
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
// 3. Create player actor (business logic runtime)
|
|
82
|
-
const createPlayer = definePlayer({ machine: authMachine, catalog });
|
|
47
|
+
// 3. Create and start an actor
|
|
48
|
+
const createPlayer = definePlayer({ machine: myMachine });
|
|
83
49
|
const actor = createPlayer();
|
|
84
50
|
actor.start();
|
|
85
51
|
|
|
86
|
-
// 4. Render
|
|
87
|
-
|
|
88
|
-
(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
## API Reference
|
|
94
|
-
|
|
95
|
-
### PlayRenderer
|
|
96
|
-
|
|
97
|
-
Main renderer component subscribing to actor signals and dynamically rendering catalog components:
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
interface PlayRendererProps {
|
|
101
|
-
actor: AbstractActor<any>;
|
|
102
|
-
components: Record<string, Component<any>>;
|
|
103
|
-
fallback?: JSX.Element;
|
|
52
|
+
// 4. Render
|
|
53
|
+
function App() {
|
|
54
|
+
return (
|
|
55
|
+
<PlayUIProvider actor={actor} registryResult={registryResult}>
|
|
56
|
+
<PlayRenderer />
|
|
57
|
+
</PlayUIProvider>
|
|
58
|
+
);
|
|
104
59
|
}
|
|
105
60
|
```
|
|
106
61
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
- `actor` - Actor instance with `currentView` signal
|
|
110
|
-
- `components` - Map of component names to SolidJS components
|
|
111
|
-
- `fallback` - Component shown when `currentView` is null
|
|
62
|
+
## Usage
|
|
112
63
|
|
|
113
|
-
|
|
64
|
+
### `PlayUIProvider` + `PlayRenderer` (recommended)
|
|
114
65
|
|
|
115
|
-
|
|
116
|
-
2. Looks up component from `components` map using `view.component` string
|
|
117
|
-
3. Renders component with props from `view.props` + `send` function using Solid's `<Dynamic />`
|
|
118
|
-
|
|
119
|
-
**Example:**
|
|
66
|
+
`PlayUIProvider` is the batteries-included entry point. It wraps `ActorProvider` and `JSONUIProvider` into a single composite provider. `PlayRenderer` is a zero-prop leaf component that reads view context and renders the current spec.
|
|
120
67
|
|
|
121
68
|
```tsx
|
|
122
|
-
|
|
69
|
+
import { PlayUIProvider, PlayRenderer, defineRegistry } from "@xmachines/play-solid";
|
|
70
|
+
|
|
71
|
+
<PlayUIProvider
|
|
123
72
|
actor={actor}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
73
|
+
registryResult={registryResult}
|
|
74
|
+
fallback={<div>Loading…</div>}
|
|
75
|
+
onError={(err) => console.error(err)}
|
|
76
|
+
navigate={navigateFn} // optional: passed to JSONUIProvider
|
|
77
|
+
validationFunctions={valFns} // optional: form validation helpers
|
|
78
|
+
>
|
|
79
|
+
<PlayRenderer />
|
|
80
|
+
</PlayUIProvider>;
|
|
130
81
|
```
|
|
131
82
|
|
|
132
|
-
|
|
83
|
+
### `ActorProvider` (escape hatch)
|
|
133
84
|
|
|
134
|
-
|
|
85
|
+
For library authors who need direct control over provider composition:
|
|
135
86
|
|
|
136
87
|
```tsx
|
|
137
|
-
import { PlayRenderer } from "@xmachines/play-solid";
|
|
138
|
-
import { defineCatalog } from "@xmachines/play-catalog";
|
|
139
|
-
import { z } from "zod";
|
|
140
|
-
|
|
141
|
-
// Define schema in catalog
|
|
142
|
-
const catalog = defineCatalog({
|
|
143
|
-
UserProfile: z.object({
|
|
144
|
-
userId: z.string(),
|
|
145
|
-
name: z.string(),
|
|
146
|
-
avatar: z.string().url().optional(),
|
|
147
|
-
stats: z.object({
|
|
148
|
-
posts: z.number(),
|
|
149
|
-
followers: z.number(),
|
|
150
|
-
}),
|
|
151
|
-
}),
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Component receives type-safe props + send
|
|
155
|
-
const components = {
|
|
156
|
-
UserProfile: (props) => (
|
|
157
|
-
<div>
|
|
158
|
-
{props.avatar && <img src={props.avatar} alt={props.name} />}
|
|
159
|
-
<h1>{props.name}</h1>
|
|
160
|
-
<p>ID: {props.userId}</p>
|
|
161
|
-
<div>
|
|
162
|
-
<span>{props.stats.posts} posts</span>
|
|
163
|
-
<span>{props.stats.followers} followers</span>
|
|
164
|
-
</div>
|
|
165
|
-
<button
|
|
166
|
-
onClick={() =>
|
|
167
|
-
props.send({
|
|
168
|
-
type: "profile.edit",
|
|
169
|
-
userId: props.userId,
|
|
170
|
-
})
|
|
171
|
-
}
|
|
172
|
-
>
|
|
173
|
-
Edit Profile
|
|
174
|
-
</button>
|
|
175
|
-
</div>
|
|
176
|
-
),
|
|
177
|
-
};
|
|
88
|
+
import { ActorProvider, PlayRenderer } from "@xmachines/play-solid";
|
|
178
89
|
|
|
179
|
-
<
|
|
90
|
+
<ActorProvider actor={actor} registryResult={registryResult}>
|
|
91
|
+
<PlayRenderer />
|
|
92
|
+
</ActorProvider>;
|
|
180
93
|
```
|
|
181
94
|
|
|
182
|
-
###
|
|
95
|
+
### `useActor` hook
|
|
96
|
+
|
|
97
|
+
Access the raw actor instance anywhere inside an `ActorProvider` or `PlayUIProvider` tree:
|
|
183
98
|
|
|
184
99
|
```tsx
|
|
185
|
-
import {
|
|
186
|
-
import { PlayRenderer } from "@xmachines/play-solid";
|
|
187
|
-
import { createSignal, onCleanup } from "solid-js";
|
|
100
|
+
import { useActor } from "@xmachines/play-solid";
|
|
188
101
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return (
|
|
192
|
-
<PlayTanStackRouterProvider
|
|
193
|
-
actor={actor}
|
|
194
|
-
router={router}
|
|
195
|
-
routeMap={routeMap}
|
|
196
|
-
renderer={(currentActor, currentRouter) => {
|
|
197
|
-
return (
|
|
198
|
-
<div>
|
|
199
|
-
<Header actor={currentActor} />
|
|
200
|
-
<PlayRenderer actor={currentActor} components={components} />
|
|
201
|
-
<Footer />
|
|
202
|
-
</div>
|
|
203
|
-
);
|
|
204
|
-
}}
|
|
205
|
-
/>
|
|
206
|
-
);
|
|
102
|
+
function SubmitButton() {
|
|
103
|
+
const actor = useActor();
|
|
104
|
+
return <button onClick={() => actor.send({ type: "SUBMIT" })}>Submit</button>;
|
|
207
105
|
}
|
|
106
|
+
```
|
|
208
107
|
|
|
209
|
-
|
|
210
|
-
function Header(props) {
|
|
211
|
-
const [route, setRoute] = createSignal<string | null>(null);
|
|
108
|
+
### `usePlayView` hook
|
|
212
109
|
|
|
213
|
-
|
|
214
|
-
let watcher: Signal.subtle.Watcher;
|
|
110
|
+
Access the resolved view context (spec, handlers, registry, store) from within the provider tree:
|
|
215
111
|
|
|
216
|
-
|
|
112
|
+
```tsx
|
|
113
|
+
import { usePlayView } from "@xmachines/play-solid";
|
|
114
|
+
import { Renderer } from "@xmachines/json-render-solid";
|
|
217
115
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
);
|
|
223
|
-
}
|
|
116
|
+
const MyRenderer = () => {
|
|
117
|
+
const view = usePlayView();
|
|
118
|
+
return <Renderer spec={view.spec} registry={view.registry} />;
|
|
119
|
+
};
|
|
224
120
|
```
|
|
225
121
|
|
|
226
|
-
##
|
|
227
|
-
|
|
228
|
-
This package implements **Signal-Only Reactivity (INV-05)** and **Passive Infrastructure (INV-04)**:
|
|
122
|
+
## API Summary
|
|
229
123
|
|
|
230
|
-
|
|
231
|
-
- No createSignal/createStore for business state
|
|
232
|
-
- No createEffect for business side effects
|
|
233
|
-
- SolidJS only triggers renders, doesn't control state
|
|
124
|
+
### Components
|
|
234
125
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
126
|
+
| Export | Description |
|
|
127
|
+
| ---------------- | ------------------------------------------------------------------------------ |
|
|
128
|
+
| `PlayUIProvider` | Batteries-included composite provider (recommended entry point) |
|
|
129
|
+
| `PlayRenderer` | Zero-prop leaf component; renders the current view spec inside a provider tree |
|
|
130
|
+
| `ActorProvider` | Lower-level smart provider for escape-hatch composition |
|
|
239
131
|
|
|
240
|
-
|
|
241
|
-
- Components receive `send` function via props
|
|
242
|
-
- User actions send events to actor (e.g., `{ type: "auth.login" }`)
|
|
243
|
-
- Actor guards validate and process events
|
|
132
|
+
### Hooks
|
|
244
133
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
134
|
+
| Export | Description |
|
|
135
|
+
| --------------- | --------------------------------------------------------------------------------------------- |
|
|
136
|
+
| `useActor()` | Returns the raw `AnyPlayActor` instance from context; throws outside a provider tree |
|
|
137
|
+
| `usePlayView()` | Returns the current `ViewContextValue` (spec, handlers, registry, store); throws outside tree |
|
|
249
138
|
|
|
250
|
-
|
|
251
|
-
- Component teardown calls watcher `unwatch` in `onCleanup`
|
|
252
|
-
- Do not rely on GC-only cleanup
|
|
139
|
+
### Context
|
|
253
140
|
|
|
254
|
-
|
|
141
|
+
| Export | Description |
|
|
142
|
+
| -------------- | -------------------------------------------------------------------------------------- |
|
|
143
|
+
| `ActorContext` | SolidJS context for the actor; use `ActorContext.Provider` directly as an escape hatch |
|
|
255
144
|
|
|
256
|
-
-
|
|
257
|
-
- Enables composition with navigation, headers, footers
|
|
258
|
-
- Supports multiple renderers in same app
|
|
145
|
+
### Re-exports from `@xmachines/json-render-solid`
|
|
259
146
|
|
|
260
|
-
|
|
147
|
+
This package re-exports the full `@xmachines/json-render-solid` public API so consumers do not need a direct dependency:
|
|
261
148
|
|
|
262
|
-
|
|
263
|
-
|
|
149
|
+
```tsx
|
|
150
|
+
import {
|
|
151
|
+
// Providers
|
|
152
|
+
JSONUIProvider,
|
|
153
|
+
StateProvider,
|
|
154
|
+
ActionProvider,
|
|
155
|
+
VisibilityProvider,
|
|
156
|
+
ValidationProvider,
|
|
157
|
+
// Renderer
|
|
158
|
+
Renderer,
|
|
159
|
+
// Registry factory + hooks
|
|
160
|
+
defineRegistry,
|
|
161
|
+
useBoundProp,
|
|
162
|
+
useStateBinding,
|
|
163
|
+
useStateValue,
|
|
164
|
+
useStateStore,
|
|
165
|
+
useActions,
|
|
166
|
+
useAction,
|
|
167
|
+
useIsVisible,
|
|
168
|
+
useFieldValidation,
|
|
169
|
+
useOptionalValidation,
|
|
170
|
+
useVisibility,
|
|
171
|
+
} from "@xmachines/play-solid";
|
|
172
|
+
```
|
|
264
173
|
|
|
265
|
-
|
|
174
|
+
### Key Types
|
|
266
175
|
|
|
267
|
-
|
|
176
|
+
| Type | Description |
|
|
177
|
+
| --------------------- | ------------------------------------------------------------------------------ |
|
|
178
|
+
| `PlayUIProviderProps` | Props for `PlayUIProvider` |
|
|
179
|
+
| `ActorProviderProps` | Props for `ActorProvider` |
|
|
180
|
+
| `ViewContextValue` | Shape of the context value from `usePlayView()` |
|
|
181
|
+
| `AnyPlayActor` | `AbstractActor<AnyActorLogic>` — bare actor type accepted by context providers |
|
|
268
182
|
|
|
269
|
-
|
|
270
|
-
2. Schedule work with `queueMicrotask`
|
|
271
|
-
3. Drain `watcher.getPending()`
|
|
272
|
-
4. Read actor signals and update SolidJS-local signal state (`setSignal()`)
|
|
273
|
-
5. Re-arm with `watch(...)` or `watch()`
|
|
183
|
+
## Testing
|
|
274
184
|
|
|
275
|
-
|
|
185
|
+
Run tests for this package in isolation:
|
|
276
186
|
|
|
277
|
-
|
|
187
|
+
```bash
|
|
188
|
+
pnpm --filter @xmachines/play-solid test
|
|
189
|
+
```
|
|
278
190
|
|
|
279
|
-
|
|
280
|
-
- **Type Safety:** Props validated against catalog schemas
|
|
281
|
-
- **Simple Testing:** Test actors without SolidJS renderer
|
|
282
|
-
- **Performance:** Microtask batching reduces unnecessary renders
|
|
283
|
-
- **Composability:** Renderer prop enables complex layouts
|
|
191
|
+
Or from within the package directory:
|
|
284
192
|
|
|
285
|
-
|
|
193
|
+
```bash
|
|
194
|
+
pnpm test # single run (jsdom environment)
|
|
195
|
+
pnpm run test:watch # watch mode
|
|
196
|
+
pnpm run test:ui # interactive Vitest UI
|
|
197
|
+
```
|
|
286
198
|
|
|
287
|
-
|
|
288
|
-
- **[@xmachines/play-catalog](../play-catalog)** - UI schema validation
|
|
289
|
-
- **[@xmachines/play-solid-router](../play-solid-router)** - Solid Router integration
|
|
290
|
-
- **[@xmachines/play-tanstack-solid-router](../play-tanstack-solid-router)** - TanStack Solid Router integration
|
|
291
|
-
- **[@xmachines/play-actor](../play-actor)** - Actor base
|
|
292
|
-
- **[@xmachines/play-signals](../play-signals)** - TC39 Signals primitives
|
|
199
|
+
Coverage is collected with v8 (80% threshold for lines, functions, branches, and statements). Browser-specific tests live in `test/browser/` and are excluded from the default jsdom run.
|
|
293
200
|
|
|
294
201
|
## License
|
|
295
202
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
This work is licensed under the terms of the MIT license.
|
|
299
|
-
For a copy, see <https://opensource.org/licenses/MIT>.
|
|
203
|
+
MIT
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ActorProvider — Smart SolidJS provider component for the XMachines Play actor lifecycle.
|
|
3
|
+
*
|
|
4
|
+
* Escape hatch primitive for library authors who need direct control. Most users should
|
|
5
|
+
* use PlayUIProvider (batteries-included composite) instead.
|
|
6
|
+
*
|
|
7
|
+
* This component:
|
|
8
|
+
* - Subscribes to actor.currentView signal via watchSignal (in component body per Phase 29)
|
|
9
|
+
* - Manages per-view StateStore lifecycle (controlled/uncontrolled)
|
|
10
|
+
* - Resolves action handlers via inner component pattern (inside StateProvider)
|
|
11
|
+
* - Injects onRenderError into registry if provided
|
|
12
|
+
* - Provides ActorContext (actor) and ViewContext (spec + handlers + registry) to children
|
|
13
|
+
* - Wraps render path in SolidJS ErrorBoundary
|
|
14
|
+
*
|
|
15
|
+
* Per D-11: The old `ActorProvider = ActorContext.Provider` alias is removed. This new smart
|
|
16
|
+
* component takes the name. Use `ActorContext.Provider` directly for escape-hatch access.
|
|
17
|
+
*
|
|
18
|
+
* @packageDocumentation
|
|
19
|
+
*/
|
|
20
|
+
import type { Component, JSX } from "solid-js";
|
|
21
|
+
import type { DefineRegistryResult } from "@xmachines/json-render-solid";
|
|
22
|
+
import type { ComponentRegistry } from "@xmachines/json-render-solid";
|
|
23
|
+
import { type BaseActorProviderProps, type BaseViewContextValue } from "@xmachines/play-actor";
|
|
24
|
+
/**
|
|
25
|
+
* Value provided by ActorProvider's ViewContext.
|
|
26
|
+
* Access via usePlayView() inside the ActorProvider tree.
|
|
27
|
+
*/
|
|
28
|
+
export interface ViewContextValue extends BaseViewContextValue<ComponentRegistry> {
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Hook to access the current view context inside an ActorProvider tree.
|
|
32
|
+
*
|
|
33
|
+
* @throws {Error} If called outside an ActorProvider (or PlayUIProvider) tree
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* import { usePlayView } from "@xmachines/play-solid";
|
|
38
|
+
*
|
|
39
|
+
* const MyRenderer: Component = () => {
|
|
40
|
+
* const view = usePlayView();
|
|
41
|
+
* return <Renderer spec={view.spec} registry={view.registry} />;
|
|
42
|
+
* };
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function usePlayView(): ViewContextValue;
|
|
46
|
+
/**
|
|
47
|
+
* Props for ActorProvider — the escape hatch primitive.
|
|
48
|
+
*
|
|
49
|
+
* For batteries-included usage, prefer PlayUIProvider which wraps ActorProvider
|
|
50
|
+
* with JSONUIProvider and all required sub-providers.
|
|
51
|
+
*/
|
|
52
|
+
export interface ActorProviderProps extends BaseActorProviderProps<DefineRegistryResult> {
|
|
53
|
+
/** Optional fallback element shown when currentView is null or ErrorBoundary catches */
|
|
54
|
+
fallback?: JSX.Element;
|
|
55
|
+
/** Optional callback invoked when SolidJS ErrorBoundary catches an error */
|
|
56
|
+
onError?: (error: unknown) => void;
|
|
57
|
+
/** Children — required; must include <PlayRenderer /> (or use PlayUIProvider shorthand) */
|
|
58
|
+
children: JSX.Element;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Smart ActorProvider component — owns actor bridging, signal subscription,
|
|
62
|
+
* StateStore lifecycle, handler resolution, and error boundary.
|
|
63
|
+
*
|
|
64
|
+
* Per D-11: Replaces the old raw alias `ActorProvider = ActorContext.Provider`.
|
|
65
|
+
* Consumers who previously used `<ActorProvider value={actor}>` should now use
|
|
66
|
+
* `<ActorContext.Provider value={actor}>` for raw provider access, or migrate to
|
|
67
|
+
* this smart component / PlayUIProvider.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```tsx
|
|
71
|
+
* import { ActorProvider, PlayRenderer } from "@xmachines/play-solid";
|
|
72
|
+
*
|
|
73
|
+
* <ActorProvider actor={myActor} registryResult={registryResult}>
|
|
74
|
+
* <PlayRenderer />
|
|
75
|
+
* </ActorProvider>
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare const ActorProvider: Component<ActorProviderProps>;
|
|
79
|
+
//# sourceMappingURL=ActorProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActorProvider.d.ts","sourceRoot":"","sources":["../src/ActorProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAWH,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,KAAK,EAAE,oBAAoB,EAAY,MAAM,8BAA8B,CAAC;AAEnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAKtE,OAAO,EAIN,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,MAAM,uBAAuB,CAAC;AAO/B;;;GAGG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC;CAAG;AAIpF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,IAAI,gBAAgB,CAE9C;AAMD;;;;;GAKG;AACH,MAAM,WAAW,kBAAmB,SAAQ,sBAAsB,CAAC,oBAAoB,CAAC;IACvF,wFAAwF;IACxF,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAEvB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAEnC,2FAA2F;IAC3F,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC;CACtB;AA2CD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,kBAAkB,CA+FvD,CAAC"}
|
package/dist/PlayRenderer.d.ts
CHANGED
|
@@ -1,63 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PlayRenderer -
|
|
2
|
+
* PlayRenderer - Zero-prop leaf component for XMachines Play SolidJS architecture.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
import { type Component } from "solid-js";
|
|
7
|
-
import type { PlayRendererProps } from "./types.js";
|
|
8
|
-
/**
|
|
9
|
-
* Main renderer component that subscribes to actor signals and renders UI
|
|
10
|
-
*
|
|
11
|
-
* Architecture (per XMachines Play patterns):
|
|
12
|
-
* - Subscribes to actor.currentView signal via TC39 Signal.subtle.Watcher
|
|
13
|
-
* - Dynamically renders catalog components based on view.component string
|
|
14
|
-
* - Forwards user events to actor via actor.send()
|
|
15
|
-
* - SolidJS signal only for triggering renders, NOT business logic
|
|
16
|
-
*
|
|
17
|
-
* Invariant: Actor Authority - Actor decides all state transitions via guards.
|
|
18
|
-
* Invariant: Passive Infrastructure - Component observes signals and sends events.
|
|
19
|
-
* Invariant: Signal-Only Reactivity - Business logic state lives in actor signals.
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```typescript
|
|
23
|
-
* import { PlayRenderer } from "@xmachines/play-solidjs";
|
|
24
|
-
* import { definePlayer } from "@xmachines/play-xstate";
|
|
25
|
-
*
|
|
26
|
-
* const actor = definePlayer({ machine, catalog })();
|
|
27
|
-
* actor.start();
|
|
28
|
-
*
|
|
29
|
-
* const components = {
|
|
30
|
-
* Dashboard: (props) => <div>User: {props.userId}</div>,
|
|
31
|
-
* LoginForm: (props) => (
|
|
32
|
-
* <form onSubmit={(e) => {
|
|
33
|
-
* e.preventDefault();
|
|
34
|
-
* props.send({ type: "auth.login", payload: {...} });
|
|
35
|
-
* }}>...</form>
|
|
36
|
-
* )
|
|
37
|
-
* };
|
|
4
|
+
* Reads view context from the enclosing ActorProvider (or PlayUIProvider) via
|
|
5
|
+
* usePlayView() and renders the spec using @xmachines/json-render-solid's Renderer.
|
|
38
6
|
*
|
|
39
|
-
*
|
|
7
|
+
* Standard usage:
|
|
8
|
+
* ```tsx
|
|
9
|
+
* <PlayUIProvider actor={myActor} registryResult={registryResult}>
|
|
10
|
+
* <PlayRenderer />
|
|
11
|
+
* </PlayUIProvider>
|
|
40
12
|
* ```
|
|
41
13
|
*
|
|
42
|
-
* @
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*
|
|
47
|
-
* using `view.component` string from actor.currentView signal.
|
|
48
|
-
*
|
|
49
|
-
* **Event forwarding:** Injects `send` function as prop to components. Components
|
|
50
|
-
* call `send(event)` to forward intents to actor. Actor guards decide validity.
|
|
51
|
-
*
|
|
52
|
-
* **Error handling:** If component not found in catalog, logs error and shows
|
|
53
|
-
* fallback. This indicates missing component registration, not runtime error.
|
|
54
|
-
*
|
|
55
|
-
* **Signal bridge:** Uses one-shot re-watch pattern. TC39 Signal watchers stop
|
|
56
|
-
* watching after notification, so watcher.watch() must be called in microtask
|
|
57
|
-
* after getPending() to re-arm for next notification.
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
import type { Component } from "solid-js";
|
|
17
|
+
/**
|
|
18
|
+
* Zero-prop leaf renderer. Must be placed inside an ActorProvider or PlayUIProvider tree.
|
|
58
19
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
20
|
+
* Reads ViewContextValue (spec, handlers, registry) from the enclosing provider
|
|
21
|
+
* via usePlayView() and renders the spec via @xmachines/json-render-solid's Renderer.
|
|
61
22
|
*/
|
|
62
|
-
export declare const PlayRenderer: Component
|
|
23
|
+
export declare const PlayRenderer: Component;
|
|
63
24
|
//# sourceMappingURL=PlayRenderer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlayRenderer.d.ts","sourceRoot":"","sources":["../src/PlayRenderer.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"PlayRenderer.d.ts","sourceRoot":"","sources":["../src/PlayRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAI1C;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,SAG1B,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlayUIProvider — Batteries-included SolidJS provider for XMachines Play.
|
|
3
|
+
*
|
|
4
|
+
* Wraps ActorProvider + JSONUIProvider into a single composite provider.
|
|
5
|
+
* This is the recommended entry point for most users.
|
|
6
|
+
*
|
|
7
|
+
* Standard usage:
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { PlayUIProvider, PlayRenderer, defineRegistry } from "@xmachines/play-solid";
|
|
10
|
+
*
|
|
11
|
+
* const registryResult = defineRegistry(myCatalog, { components, actions });
|
|
12
|
+
*
|
|
13
|
+
* <PlayUIProvider actor={myActor} registryResult={registryResult}>
|
|
14
|
+
* <PlayRenderer />
|
|
15
|
+
* </PlayUIProvider>
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* For full control (library authors), use ActorProvider directly.
|
|
19
|
+
*
|
|
20
|
+
* @packageDocumentation
|
|
21
|
+
*/
|
|
22
|
+
import type { Component } from "solid-js";
|
|
23
|
+
import { type JSONUIProviderProps } from "@xmachines/json-render-solid";
|
|
24
|
+
import { type ActorProviderProps } from "./ActorProvider.js";
|
|
25
|
+
type JSONUIForwardedProps = Pick<JSONUIProviderProps, "validationFunctions" | "navigate" | "functions">;
|
|
26
|
+
/**
|
|
27
|
+
* Props for PlayUIProvider — all ActorProvider props plus JSONUIProvider's forwarded props.
|
|
28
|
+
*/
|
|
29
|
+
export interface PlayUIProviderProps extends ActorProviderProps, Partial<JSONUIForwardedProps> {
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Batteries-included composite provider: ActorProvider + JSONUIProvider.
|
|
33
|
+
*
|
|
34
|
+
* Provides the full JSON render context stack:
|
|
35
|
+
* - ActorContext (actor instance via ActorProvider)
|
|
36
|
+
* - ViewContext (spec, handlers, registry via ActorProvider)
|
|
37
|
+
* - StateProvider + ActionProvider + VisibilityProvider + ValidationProvider (via JSONUIProvider)
|
|
38
|
+
* - ConfirmDialogManager (via JSONUIProvider)
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* <PlayUIProvider actor={myActor} registryResult={registryResult} navigate={navigate}>
|
|
43
|
+
* <PlayRenderer />
|
|
44
|
+
* </PlayUIProvider>
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare const PlayUIProvider: Component<PlayUIProviderProps>;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=PlayUIProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlayUIProvider.d.ts","sourceRoot":"","sources":["../src/PlayUIProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAO,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACxF,OAAO,EAA8B,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGzF,KAAK,oBAAoB,GAAG,IAAI,CAC/B,mBAAmB,EACnB,qBAAqB,GAAG,UAAU,GAAG,WAAW,CAChD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB,EAAE,OAAO,CAAC,oBAAoB,CAAC;CAAG;AA8BjG;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,CAAC,mBAAmB,CAqBzD,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SolidJS renderer for XMachines Play architecture
|
|
2
|
+
* @xmachines/play-solid - SolidJS renderer for XMachines Play architecture
|
|
3
|
+
*
|
|
4
|
+
* Provides a SolidJS rendering layer that passively observes actor signals and
|
|
5
|
+
* renders UI components via @xmachines/json-render-solid. SolidJS reactivity is only used
|
|
6
|
+
* to trigger re-renders — signals are the source of truth.
|
|
7
|
+
*
|
|
8
|
+
* Primary entry point:
|
|
9
|
+
* ```tsx
|
|
10
|
+
* import { PlayUIProvider, PlayRenderer, defineRegistry } from "@xmachines/play-solid";
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* For escape-hatch provider composition:
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import { ActorProvider, ActorContext, usePlayView } from "@xmachines/play-solid";
|
|
16
|
+
* ```
|
|
3
17
|
*
|
|
4
18
|
* @packageDocumentation
|
|
5
19
|
*/
|
|
6
20
|
export { PlayRenderer } from "./PlayRenderer.js";
|
|
7
|
-
export
|
|
21
|
+
export { ActorProvider, usePlayView } from "./ActorProvider.js";
|
|
22
|
+
export { PlayUIProvider } from "./PlayUIProvider.js";
|
|
23
|
+
export { ActorContext, useActor } from "./useActor.js";
|
|
24
|
+
export { JSONUIProvider, StateProvider, ActionProvider, VisibilityProvider, ValidationProvider, Renderer, defineRegistry, useBoundProp, useStateBinding, useStateValue, useStateStore, useActions, useAction, useIsVisible, useFieldValidation, useOptionalValidation, useVisibility, } from "@xmachines/json-render-solid";
|
|
25
|
+
export type { JSONUIProviderProps, StateProviderProps, ActionProviderProps, VisibilityProviderProps, ValidationProviderProps, RendererProps, ComponentFn, ComponentContext, ComponentRegistry, DefineRegistryResult, SetState, } from "@xmachines/json-render-solid";
|
|
26
|
+
export type { RenderErrorHandler } from "@xmachines/json-render-solid";
|
|
27
|
+
export type { ActorProviderProps, PlayUIProviderProps, ViewContextValue } from "./types.js";
|
|
28
|
+
export type { AnyPlayActor } from "./useActor.js";
|
|
8
29
|
//# 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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAIrD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAMvD,OAAO,EAEN,cAAc,EACd,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAElB,QAAQ,EAER,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,EACb,aAAa,EACb,UAAU,EACV,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,aAAa,GACb,MAAM,8BAA8B,CAAC;AAGtC,YAAY,EACX,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,QAAQ,GACR,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC5F,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,28 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TypeScript type definitions for play-
|
|
2
|
+
* TypeScript type definitions for play-solid
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
import type { AbstractActor, Viewable } from "@xmachines/play-actor";
|
|
7
|
-
import type { JSX, ValidComponent } from "solid-js";
|
|
8
|
-
import type { AnyActorLogic } from "xstate";
|
|
9
|
-
export type SolidView = {
|
|
10
|
-
component: string;
|
|
11
|
-
props: Record<string, unknown>;
|
|
12
|
-
} | null;
|
|
13
|
-
/**
|
|
14
|
-
* Props for PlayRenderer component
|
|
4
|
+
* Re-exports the primary prop types from their respective source files.
|
|
5
|
+
* PlayRendererProps has been removed per D-06 (replaced by ActorProviderProps / PlayUIProviderProps).
|
|
15
6
|
*
|
|
16
|
-
* @
|
|
17
|
-
* @property components - Map of component names to SolidJS components
|
|
18
|
-
* @property fallback - Optional element shown when currentView is null
|
|
7
|
+
* @packageDocumentation
|
|
19
8
|
*/
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/** Map of component names to SolidJS components */
|
|
24
|
-
components: Record<string, ValidComponent>;
|
|
25
|
-
/** Optional element shown when currentView is null */
|
|
26
|
-
fallback?: JSX.Element;
|
|
27
|
-
}
|
|
9
|
+
export type { RenderErrorHandler } from "@xmachines/json-render-solid";
|
|
10
|
+
export type { ActorProviderProps, ViewContextValue } from "./ActorProvider.js";
|
|
11
|
+
export type { PlayUIProviderProps } from "./PlayUIProvider.js";
|
|
28
12
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC/E,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useActor — SolidJS hook for accessing the raw actor inside an ActorProvider tree.
|
|
3
|
+
*
|
|
4
|
+
* Components rendered inside ActorProvider (or PlayUIProvider) can call useActor()
|
|
5
|
+
* to get direct access to the actor instance without prop drilling.
|
|
6
|
+
*
|
|
7
|
+
* @throws {Error} If called outside an ActorProvider tree
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { useActor } from "@xmachines/play-solid";
|
|
12
|
+
*
|
|
13
|
+
* function MyComponent() {
|
|
14
|
+
* const actor = useActor();
|
|
15
|
+
* return <button onClick={() => actor.send({ type: "SUBMIT" })}>Submit</button>;
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @packageDocumentation
|
|
20
|
+
*/
|
|
21
|
+
import type { AbstractActor } from "@xmachines/play-actor";
|
|
22
|
+
import type { AnyActorLogic } from "xstate";
|
|
23
|
+
/** Bare actor type accepted by Solid context providers. For the full routing + view shape, use `PlayActor` from `@xmachines/play-router`. */
|
|
24
|
+
export type AnyPlayActor = AbstractActor<AnyActorLogic>;
|
|
25
|
+
/**
|
|
26
|
+
* SolidJS context for the actor — exported so consumers can use ActorContext.Provider
|
|
27
|
+
* directly as an escape hatch (per D-11). The smart ActorProvider component takes
|
|
28
|
+
* the name "ActorProvider" and is the recommended entry point.
|
|
29
|
+
*/
|
|
30
|
+
export declare const ActorContext: import("solid-js").Context<AnyPlayActor | null>;
|
|
31
|
+
export declare function useActor(): AnyPlayActor;
|
|
32
|
+
//# sourceMappingURL=useActor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useActor.d.ts","sourceRoot":"","sources":["../src/useActor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C,6IAA6I;AAC7I,MAAM,MAAM,YAAY,GAAG,aAAa,CAAC,aAAa,CAAC,CAAC;AAExD;;;;GAIG;AACH,eAAO,MAAM,YAAY,iDAA2C,CAAC;AAErE,wBAAgB,QAAQ,IAAI,YAAY,CAEvC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmachines/play-solid",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Solid renderer for XMachines Play architecture",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"catalog",
|
|
7
7
|
"play",
|
|
@@ -12,12 +12,18 @@
|
|
|
12
12
|
],
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"author": "XMachines Contributors",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+ssh://git@gitlab.com/xmachin-es/xmachines-js.git",
|
|
18
|
+
"directory": "packages/play-solid"
|
|
19
|
+
},
|
|
15
20
|
"files": [
|
|
16
21
|
"dist",
|
|
17
22
|
"README.md",
|
|
18
23
|
"LICENSE"
|
|
19
24
|
],
|
|
20
25
|
"type": "module",
|
|
26
|
+
"sideEffects": false,
|
|
21
27
|
"exports": {
|
|
22
28
|
".": {
|
|
23
29
|
"types": "./dist/index.d.ts",
|
|
@@ -29,29 +35,47 @@
|
|
|
29
35
|
},
|
|
30
36
|
"scripts": {
|
|
31
37
|
"build": "vite build && tsc --build",
|
|
32
|
-
"clean": "rm -rf dist
|
|
33
|
-
"
|
|
38
|
+
"clean": "rm -rf dist *.tsbuildinfo coverage .vitest-attachments test/browser/__screenshots__ node_modules/.svelte2tsx-* node_modules/.vite*",
|
|
39
|
+
"lint": "oxlint .",
|
|
40
|
+
"format": "oxfmt .",
|
|
34
41
|
"test": "vitest",
|
|
35
42
|
"test:watch": "vitest",
|
|
36
|
-
"test:ui": "vitest --ui"
|
|
37
|
-
"prepublishOnly": "npm run build"
|
|
43
|
+
"test:ui": "vitest --ui"
|
|
38
44
|
},
|
|
39
45
|
"dependencies": {
|
|
40
|
-
"@xmachines/play
|
|
41
|
-
"@xmachines/play-
|
|
42
|
-
"@xmachines/play-signals": "1.
|
|
46
|
+
"@xmachines/play": "1.1.0",
|
|
47
|
+
"@xmachines/play-actor": "1.1.0",
|
|
48
|
+
"@xmachines/play-signals": "1.1.0"
|
|
43
49
|
},
|
|
44
50
|
"devDependencies": {
|
|
45
51
|
"@solidjs/testing-library": "^0.8.10",
|
|
46
|
-
"@
|
|
47
|
-
"@
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
52
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
53
|
+
"@types/node": "^26.2.0",
|
|
54
|
+
"@vitest/browser-playwright": "^4.1.10",
|
|
55
|
+
"@xmachines/json-render-core": "^0.19.0-xm.2",
|
|
56
|
+
"@xmachines/json-render-solid": "^0.19.0-xm.2",
|
|
57
|
+
"@xmachines/json-render-xstate": "^0.19.0-xm.2",
|
|
58
|
+
"@xstate/store": "^3.17.0",
|
|
59
|
+
"jsdom": "^29.1.0",
|
|
60
|
+
"oxfmt": "^0.64.0",
|
|
61
|
+
"oxlint": "^1.79.0",
|
|
62
|
+
"solid-js": "^1.9.12",
|
|
63
|
+
"typescript": "^5.9.3 || ^6.0.3",
|
|
64
|
+
"vite": "^8.0.10",
|
|
51
65
|
"vite-plugin-solid": "^2.11.11",
|
|
52
|
-
"vitest": "^4.1.
|
|
66
|
+
"vitest": "^4.1.11",
|
|
67
|
+
"xstate": "^5.31.0",
|
|
68
|
+
"zod": "^4.4.1"
|
|
53
69
|
},
|
|
54
70
|
"peerDependencies": {
|
|
55
|
-
"
|
|
71
|
+
"@xmachines/json-render-core": "^0.19.0-xm.2",
|
|
72
|
+
"@xmachines/json-render-solid": "^0.19.0-xm.2",
|
|
73
|
+
"@xmachines/json-render-xstate": "^0.19.0-xm.2",
|
|
74
|
+
"@xstate/store": "^3.17.0",
|
|
75
|
+
"solid-js": "^1.8.0",
|
|
76
|
+
"xstate": "^5.31.0"
|
|
77
|
+
},
|
|
78
|
+
"engines": {
|
|
79
|
+
"node": ">=22.0.0"
|
|
56
80
|
}
|
|
57
81
|
}
|
package/dist/PlayRenderer.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { Dynamic as e, createComponent as t, insert as n, memo as r, mergeProps as i, template as a } from "solid-js/web";
|
|
2
|
-
import { createSignal as o, onMount as s } from "solid-js";
|
|
3
|
-
import { Signal as c } from "@xmachines/play-signals";
|
|
4
|
-
//#region src/PlayRenderer.tsx
|
|
5
|
-
var l = /* @__PURE__ */ a("<div class=play-renderer-error>Component \"<!>\" not found in catalog. Available: "), u = (a) => {
|
|
6
|
-
let [u, d] = o(a.actor.currentView.get());
|
|
7
|
-
s(() => {
|
|
8
|
-
let e = new c.subtle.Watcher(() => {
|
|
9
|
-
queueMicrotask(() => {
|
|
10
|
-
e.getPending(), d(a.actor.currentView.get()), e.watch(a.actor.currentView);
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
e.watch(a.actor.currentView);
|
|
14
|
-
});
|
|
15
|
-
let f = a.actor.send.bind(a.actor);
|
|
16
|
-
return [
|
|
17
|
-
r(() => r(() => !u())() && (a.fallback || null)),
|
|
18
|
-
r(() => r(() => !!(u() && !a.components))() && (console.error(`Components catalog is ${a.components === null ? "null" : "undefined"}. Cannot render component "${u().component}".`), a.fallback || null)),
|
|
19
|
-
r(() => r(() => !!(u() && a.components && !a.components[u().component]))() && (console.error(`Component "${u().component}" not found in catalog. Available components: ${Object.keys(a.components).join(", ")}`), (() => {
|
|
20
|
-
var e = l(), t = e.firstChild.nextSibling;
|
|
21
|
-
return t.nextSibling, n(e, () => u().component, t), n(e, () => Object.keys(a.components).join(", "), null), e;
|
|
22
|
-
})())),
|
|
23
|
-
r(() => r(() => !!(u() && a.components && a.components[u().component]))() && t(e, i({ get component() {
|
|
24
|
-
return a.components[u().component];
|
|
25
|
-
} }, () => u().props, { send: f })))
|
|
26
|
-
];
|
|
27
|
-
};
|
|
28
|
-
//#endregion
|
|
29
|
-
export { u as PlayRenderer };
|
|
30
|
-
|
|
31
|
-
//# sourceMappingURL=PlayRenderer.js.map
|
package/dist/PlayRenderer.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PlayRenderer.js","names":["createSignal","onMount","Component","Dynamic","Signal","PlayRendererProps","SolidView","PlayRenderer","props","view","setView","actor","currentView","get","watcher","subtle","Watcher","queueMicrotask","getPending","watch","sendBound","send","bind","_$memo","fallback","components","console","error","component","Object","keys","join","_el$","_tmpl$","_el$2","firstChild","_el$5","nextSibling","_el$3","_$insert","_$createComponent","_$mergeProps"],"sources":["../src/PlayRenderer.tsx"],"sourcesContent":["/**\n * PlayRenderer - Main SolidJS renderer component for XMachines Play architecture\n *\n * @packageDocumentation\n */\n\nimport { createSignal, onMount, type Component } from \"solid-js\";\nimport { Dynamic } from \"solid-js/web\";\nimport { Signal } from \"@xmachines/play-signals\";\nimport type { PlayRendererProps, SolidView } from \"./types.js\";\n\n/**\n * Main renderer component that subscribes to actor signals and renders UI\n *\n * Architecture (per XMachines Play patterns):\n * - Subscribes to actor.currentView signal via TC39 Signal.subtle.Watcher\n * - Dynamically renders catalog components based on view.component string\n * - Forwards user events to actor via actor.send()\n * - SolidJS signal only for triggering renders, NOT business logic\n *\n * Invariant: Actor Authority - Actor decides all state transitions via guards.\n * Invariant: Passive Infrastructure - Component observes signals and sends events.\n * Invariant: Signal-Only Reactivity - Business logic state lives in actor signals.\n *\n * @example\n * ```typescript\n * import { PlayRenderer } from \"@xmachines/play-solidjs\";\n * import { definePlayer } from \"@xmachines/play-xstate\";\n *\n * const actor = definePlayer({ machine, catalog })();\n * actor.start();\n *\n * const components = {\n * Dashboard: (props) => <div>User: {props.userId}</div>,\n * LoginForm: (props) => (\n * <form onSubmit={(e) => {\n * e.preventDefault();\n * props.send({ type: \"auth.login\", payload: {...} });\n * }}>...</form>\n * )\n * };\n *\n * <PlayRenderer actor={actor} components={components} />\n * ```\n *\n * @param props - Component props\n * @returns SolidJS element rendering current view from actor\n *\n * @remarks\n * **Component lookup:** Dynamically looks up component from `components` map\n * using `view.component` string from actor.currentView signal.\n *\n * **Event forwarding:** Injects `send` function as prop to components. Components\n * call `send(event)` to forward intents to actor. Actor guards decide validity.\n *\n * **Error handling:** If component not found in catalog, logs error and shows\n * fallback. This indicates missing component registration, not runtime error.\n *\n * **Signal bridge:** Uses one-shot re-watch pattern. TC39 Signal watchers stop\n * watching after notification, so watcher.watch() must be called in microtask\n * after getPending() to re-arm for next notification.\n *\n * **CRITICAL:** Never call actor.send() during render - only in event handlers.\n * Calling send during render causes infinite render loops.\n */\nexport const PlayRenderer: Component<PlayRendererProps> = (props) => {\n\t// Create SolidJS signal for view\n\t// Signal is NOT business logic state - it's just SolidJS's render trigger\n\tconst [view, setView] = createSignal<SolidView>(props.actor.currentView.get() as SolidView);\n\n\t// Bridge TC39 Signal to SolidJS signal\n\t// Uses one-shot re-watch pattern (must re-watch after each notification)\n\tonMount(() => {\n\t\tconst watcher = new Signal.subtle.Watcher(() => {\n\t\t\tqueueMicrotask(() => {\n\t\t\t\t// Acknowledge the notification\n\t\t\t\twatcher.getPending();\n\n\t\t\t\t// Update SolidJS signal (triggers SolidJS reactivity)\n\t\t\t\tsetView(props.actor.currentView.get() as SolidView);\n\n\t\t\t\t// Re-watch for next notification (one-shot pattern)\n\t\t\t\t// TC39 Signal watchers stop watching after notification\n\t\t\t\twatcher.watch(props.actor.currentView);\n\t\t\t});\n\t\t});\n\n\t\t// Watch actor.currentView for changes\n\t\twatcher.watch(props.actor.currentView);\n\n\t\t// Note: TC39 Signal watchers don't have explicit disposal\n\t\t// The watcher will be garbage collected when the component unmounts\n\t});\n\n\t// Bind send function (ensures correct 'this' context)\n\tconst sendBound = props.actor.send.bind(props.actor);\n\n\treturn (\n\t\t<>\n\t\t\t{/* No view - show fallback */}\n\t\t\t{!view() && (props.fallback || null)}\n\n\t\t\t{/* Handle null/undefined components catalog gracefully */}\n\t\t\t{view() &&\n\t\t\t\t!props.components &&\n\t\t\t\t(() => {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`Components catalog is ${props.components === null ? \"null\" : \"undefined\"}. ` +\n\t\t\t\t\t\t\t`Cannot render component \"${view()!.component}\".`,\n\t\t\t\t\t);\n\t\t\t\t\treturn props.fallback || null;\n\t\t\t\t})()}\n\n\t\t\t{/* View exists but component not found */}\n\t\t\t{view() &&\n\t\t\t\tprops.components &&\n\t\t\t\t!props.components[view()!.component] &&\n\t\t\t\t(() => {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`Component \"${view()!.component}\" not found in catalog. ` +\n\t\t\t\t\t\t\t`Available components: ${Object.keys(props.components).join(\", \")}`,\n\t\t\t\t\t);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div class=\"play-renderer-error\">\n\t\t\t\t\t\t\tComponent \"{view()!.component}\" not found in catalog. Available:{\" \"}\n\t\t\t\t\t\t\t{Object.keys(props.components).join(\", \")}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t})()}\n\n\t\t\t{/* Render matched component dynamically */}\n\t\t\t{view() && props.components && props.components[view()!.component] && (\n\t\t\t\t<Dynamic\n\t\t\t\t\tcomponent={props.components[view()!.component]}\n\t\t\t\t\t{...view()!.props}\n\t\t\t\t\tsend={sendBound}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n};\n"],"mappings":";;;;iHAiEaO,KAA8CC,MAAU;CAGpE,IAAM,CAACC,GAAMC,KAAWV,EAAwBQ,EAAMG,MAAMC,YAAYC,KAAK,CAAc;AAI3FZ,SAAc;EACb,IAAMa,IAAU,IAAIV,EAAOW,OAAOC,cAAc;AAC/CC,wBAAqB;AASpBH,IAPAA,EAAQI,YAAY,EAGpBR,EAAQF,EAAMG,MAAMC,YAAYC,KAAK,CAAc,EAInDC,EAAQK,MAAMX,EAAMG,MAAMC,YAAY;KACrC;IACD;AAGFE,IAAQK,MAAMX,EAAMG,MAAMC,YAAY;GAIrC;CAGF,IAAMQ,IAAYZ,EAAMG,MAAMU,KAAKC,KAAKd,EAAMG,MAAM;AAEpD,QAAA;EAAAY,QAGGA,QAAA,CAACd,GAAM,CAAA,EAAA,KAAKD,EAAMgB,YAAY,MAAK;EAAAD,QAGnCA,QAAA,CAAA,EAAAd,GAAM,IACN,CAACD,EAAMiB,YAAU,EAAA,KAEhBC,QAAQC,MACP,yBAAyBnB,EAAMiB,eAAe,OAAO,SAAS,YAAW,6BAC5ChB,GAAM,CAAEmB,UAAS,IAC9C,EACMpB,EAAMgB,YAAY,MACtB;EAAAD,QAGJA,QAAA,CAAA,EAAAd,GAAM,IACND,EAAMiB,cACN,CAACjB,EAAMiB,WAAWhB,GAAM,CAAEmB,YAAU,EAAA,KAEnCF,QAAQC,MACP,cAAclB,GAAM,CAAEmB,UAAS,gDACLC,OAAOC,KAAKtB,EAAMiB,WAAW,CAACM,KAAK,KAAK,GAClE,SACD;GAAA,IAAAC,IAAAC,GAAA,EAAAG,IAAAJ,EAAAG,WAAAE;AAG2C,UAH3CD,EAAAC,aAAAE,EAAAP,SAEcvB,GAAM,CAAEmB,WAASQ,EAAA,EAAAG,EAAAP,SAC5BH,OAAOC,KAAKtB,EAAMiB,WAAW,CAACM,KAAK,KAAK,EAAA,KAAA,EAAAC;MAAA,EAGxC;EAAAT,QAGJA,QAAA,CAAA,EAAAd,GAAM,IAAID,EAAMiB,cAAcjB,EAAMiB,WAAWhB,GAAM,CAAEmB,YAAU,EAAA,IAAAY,EAChErC,GAAOsC,EAAA,EAAA,IACPb,YAAS;AAAA,UAAEpB,EAAMiB,WAAWhB,GAAM,CAAEmB;KAAU,QAC1CnB,GAAM,CAAED,OAAK,EACjBa,MAAMD,GAAS,CAAA,CAEhB,CAAA;EAAA"}
|
package/dist/index.js
DELETED