@xmachines/play-xstate 1.0.0-beta.2 → 1.0.0-beta.21

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.
Files changed (80) hide show
  1. package/README.md +263 -107
  2. package/dist/define-player.d.ts +6 -56
  3. package/dist/define-player.d.ts.map +1 -1
  4. package/dist/define-player.js +8 -60
  5. package/dist/define-player.js.map +1 -1
  6. package/dist/define-player.typecheck.d.ts +2 -0
  7. package/dist/define-player.typecheck.d.ts.map +1 -0
  8. package/dist/define-player.typecheck.js +48 -0
  9. package/dist/define-player.typecheck.js.map +1 -0
  10. package/dist/errors.d.ts +66 -0
  11. package/dist/errors.d.ts.map +1 -0
  12. package/dist/errors.js +76 -0
  13. package/dist/errors.js.map +1 -0
  14. package/dist/guards/compose.d.ts +14 -3
  15. package/dist/guards/compose.d.ts.map +1 -1
  16. package/dist/guards/compose.js +26 -0
  17. package/dist/guards/compose.js.map +1 -1
  18. package/dist/guards/helpers.d.ts +13 -17
  19. package/dist/guards/helpers.d.ts.map +1 -1
  20. package/dist/guards/helpers.js +20 -25
  21. package/dist/guards/helpers.js.map +1 -1
  22. package/dist/guards/index.d.ts +2 -1
  23. package/dist/guards/index.d.ts.map +1 -1
  24. package/dist/guards/index.js +1 -1
  25. package/dist/guards/index.js.map +1 -1
  26. package/dist/guards/types.d.ts +3 -2
  27. package/dist/guards/types.d.ts.map +1 -1
  28. package/dist/index.d.ts +7 -8
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +3 -5
  31. package/dist/index.js.map +1 -1
  32. package/dist/player-actor.d.ts +70 -22
  33. package/dist/player-actor.d.ts.map +1 -1
  34. package/dist/player-actor.js +290 -88
  35. package/dist/player-actor.js.map +1 -1
  36. package/dist/player-actor.typecheck.d.ts +2 -0
  37. package/dist/player-actor.typecheck.d.ts.map +1 -0
  38. package/dist/player-actor.typecheck.js +27 -0
  39. package/dist/player-actor.typecheck.js.map +1 -0
  40. package/dist/routing/build-url.d.ts +22 -16
  41. package/dist/routing/build-url.d.ts.map +1 -1
  42. package/dist/routing/build-url.js +27 -20
  43. package/dist/routing/build-url.js.map +1 -1
  44. package/dist/routing/derive-route.d.ts +2 -2
  45. package/dist/routing/derive-route.d.ts.map +1 -1
  46. package/dist/routing/derive-route.js +3 -3
  47. package/dist/routing/derive-route.js.map +1 -1
  48. package/dist/routing/format-play-route-transitions.d.ts +41 -4
  49. package/dist/routing/format-play-route-transitions.d.ts.map +1 -1
  50. package/dist/routing/format-play-route-transitions.js +22 -19
  51. package/dist/routing/format-play-route-transitions.js.map +1 -1
  52. package/dist/routing/index.d.ts +2 -1
  53. package/dist/routing/index.d.ts.map +1 -1
  54. package/dist/routing/types.d.ts +8 -13
  55. package/dist/routing/types.d.ts.map +1 -1
  56. package/dist/signals/index.d.ts +0 -1
  57. package/dist/signals/index.d.ts.map +1 -1
  58. package/dist/signals/index.js +0 -1
  59. package/dist/signals/index.js.map +1 -1
  60. package/dist/signals/state-signal.d.ts +1 -1
  61. package/dist/signals/state-signal.d.ts.map +1 -1
  62. package/dist/types.d.ts +20 -14
  63. package/dist/types.d.ts.map +1 -1
  64. package/package.json +26 -19
  65. package/dist/catalog/index.d.ts +0 -12
  66. package/dist/catalog/index.d.ts.map +0 -1
  67. package/dist/catalog/index.js +0 -11
  68. package/dist/catalog/index.js.map +0 -1
  69. package/dist/catalog/types.d.ts +0 -36
  70. package/dist/catalog/types.d.ts.map +0 -1
  71. package/dist/catalog/types.js +0 -2
  72. package/dist/catalog/types.js.map +0 -1
  73. package/dist/catalog/validate-binding.d.ts +0 -21
  74. package/dist/catalog/validate-binding.d.ts.map +0 -1
  75. package/dist/catalog/validate-binding.js +0 -30
  76. package/dist/catalog/validate-binding.js.map +0 -1
  77. package/dist/catalog/validate-props.d.ts +0 -41
  78. package/dist/catalog/validate-props.d.ts.map +0 -1
  79. package/dist/catalog/validate-props.js +0 -95
  80. package/dist/catalog/validate-props.js.map +0 -1
@@ -1,9 +1,213 @@
1
- import { createActor } from "xstate";
2
- import { AbstractActor } from "@xmachines/play-actor";
1
+ import { createActor, } from "xstate";
2
+ import { AbstractActor, } from "@xmachines/play-actor";
3
3
  import { Signal } from "@xmachines/play-signals";
4
4
  import { StateSignalManager } from "./signals/state-signal.js";
5
+ import { InvalidEventError, MissingRouteParamError } from "./errors.js";
5
6
  import { deriveRoute, buildRouteUrl } from "./routing/index.js";
6
- import { validateComponentBinding, validateViewProps, mergeViewProps } from "./catalog/index.js";
7
+ const hasSnapshotStatus = (snapshot) => {
8
+ if (!snapshot || typeof snapshot !== "object") {
9
+ return false;
10
+ }
11
+ if (!("status" in snapshot)) {
12
+ return false;
13
+ }
14
+ return typeof snapshot.status === "string";
15
+ };
16
+ const isActiveSnapshot = (snapshot) => {
17
+ return hasSnapshotStatus(snapshot) && snapshot.status === "active";
18
+ };
19
+ /**
20
+ * Derive the actor's current URL from state metadata and context.
21
+ *
22
+ * Resolves the route template from the current state's `meta.route` and substitutes
23
+ * any `:param` placeholders from `context.routeParams` (preferred) or flat `context`.
24
+ *
25
+ * Returns `null` — rather than throwing — when:
26
+ * - The snapshot has no route metadata (non-routable state)
27
+ * - A required route parameter is absent from context (`MissingRouteParamError`)
28
+ *
29
+ * The `null` return on missing params is intentional: it keeps the computed signal
30
+ * stable during transient states (e.g. mid-transition before context is fully updated,
31
+ * or after logout when `context.username` is `null` but the router bridge has not yet
32
+ * synced to the new state). The router bridge and URL bar are updated on the next
33
+ * stable snapshot once context is complete.
34
+ *
35
+ * @param snapshot - Current XState machine snapshot.
36
+ * @returns Resolved URL string, or `null` if the route cannot be resolved.
37
+ */
38
+ const deriveCurrentRoute = (snapshot) => {
39
+ if (!snapshot || typeof snapshot.getMeta !== "function") {
40
+ return null;
41
+ }
42
+ const meta = snapshot.getMeta();
43
+ if (!meta || typeof meta !== "object") {
44
+ return null;
45
+ }
46
+ const routeTemplate = deriveRoute(meta);
47
+ if (!routeTemplate) {
48
+ return null;
49
+ }
50
+ try {
51
+ return buildRouteUrl(routeTemplate, snapshot.context ?? {});
52
+ }
53
+ catch (error) {
54
+ // A required route parameter is absent from context — this can happen transiently
55
+ // when the machine is mid-transition (e.g. actor in profile state but username not
56
+ // yet assigned, or after logout before the router bridge has redirected).
57
+ // Return null and let the signal recompute on the next stable snapshot.
58
+ if (error instanceof MissingRouteParamError) {
59
+ return null;
60
+ }
61
+ throw error;
62
+ }
63
+ };
64
+ const resolveViewMeta = (meta) => {
65
+ for (const stateMeta of Object.values(meta)) {
66
+ const maybeView = stateMeta && typeof stateMeta === "object"
67
+ ? stateMeta.view
68
+ : undefined;
69
+ if (maybeView && typeof maybeView === "object") {
70
+ return maybeView;
71
+ }
72
+ }
73
+ return null;
74
+ };
75
+ /**
76
+ * Merge route parameters and an explicit allowlist of context fields into a single
77
+ * element's props.
78
+ *
79
+ * Priority rule (highest → lowest):
80
+ * 1. Explicit non-`undefined` spec prop — always wins (static values are authoritative).
81
+ * 2. URL route params (`context.routeParams`) — fills `undefined` slots from the URL path.
82
+ * 3. Allowlisted context fields (`contextProps`) — fills remaining `undefined` slots from
83
+ * the machine context (e.g. `context.username` on a state with no URL username param).
84
+ *
85
+ * The `contextProps` allowlist is the key safety mechanism: only fields the machine
86
+ * explicitly opts in to are ever exposed to components.
87
+ *
88
+ * @param routeParams - Extracted URL path parameters from `context.routeParams`.
89
+ * @param contextValues - Object containing only the allowlisted context fields.
90
+ * @param existingProps - The element's props as declared in the machine spec.
91
+ * @returns Merged props object with URL params and allowlisted context values filling
92
+ * undefined slots.
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * // spec: { section: undefined, username: undefined, title: "Dashboard" }
97
+ * // routeParams: { section: "profile" } ← from URL /:section
98
+ * // contextValues: { username: "alice" } ← from contextProps: ["username"]
99
+ * // result: { section: "profile", username: "alice", title: "Dashboard" }
100
+ * ```
101
+ */
102
+ function mergeRouteParamsIntoProps(routeParams, contextValues, existingProps) {
103
+ // Layer 3 (lowest priority): allowlisted context values
104
+ const merged = { ...contextValues };
105
+ // Layer 2: route params override context values for the same key
106
+ for (const [k, v] of Object.entries(routeParams)) {
107
+ merged[k] = v;
108
+ }
109
+ // Layer 1 (highest priority): explicit non-undefined spec props always win
110
+ for (const [k, v] of Object.entries(existingProps)) {
111
+ if (v !== undefined)
112
+ merged[k] = v;
113
+ }
114
+ return merged;
115
+ }
116
+ /**
117
+ * Derive the actor's current view from state metadata.
118
+ *
119
+ * Always returns a **fresh** `ViewMetadata` object so `Signal.State.set()` always notifies
120
+ * watchers — including on self-transitions (`reenter: true`) where the component name and
121
+ * spec reference would otherwise be identical. Returning the same object reference would
122
+ * cause `Signal.State`'s `Object.is` equality check to suppress the notification.
123
+ *
124
+ * ### Prop enrichment
125
+ *
126
+ * Each spec element's `props` are enriched before the view is emitted using two sources:
127
+ *
128
+ * **1. URL route params** (`context.routeParams`, populated by `formatPlayRouteTransitions`):
129
+ * Makes `:section?` / `:username` URL path parameters automatically available as component
130
+ * props without manual wiring.
131
+ *
132
+ * **2. Context fields** (`spec.contextProps` allowlist):
133
+ * Exposes selected context fields as component props for states where no URL param covers
134
+ * the data (e.g. `context.username` on a `/dashboard` route). Only fields explicitly named
135
+ * in `spec.contextProps: string[]` are ever exposed — nothing leaks from context implicitly.
136
+ *
137
+ * Merge priority (see `mergeRouteParamsIntoProps`):
138
+ * 1. Explicit non-`undefined` spec prop — always wins (authoritative static value).
139
+ * 2. URL route param (`context.routeParams`) — fills `undefined` slots from the URL path.
140
+ * 3. Allowlisted context field (`contextProps`) — fills remaining `undefined` slots.
141
+ *
142
+ * @param snapshot - Current XState machine snapshot.
143
+ * @returns Enriched `ViewMetadata`, or `null` if the current state has no view metadata.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * // spec: { contextProps: ["username"], elements: { root: { props: { username: undefined } } } }
148
+ * // context.username = "alice", routeParams = {}
149
+ * // Derived props: { username: "alice" }
150
+ *
151
+ * // spec: { contextProps: ["username"], elements: { root: { props: { username: undefined } } } }
152
+ * // context.username = "alice", routeParams = { username: "demo" }
153
+ * // Derived props: { username: "demo" } ← routeParams wins
154
+ * ```
155
+ */
156
+ const deriveCurrentView = (snapshot) => {
157
+ if (!snapshot) {
158
+ return null;
159
+ }
160
+ const meta = snapshot.getMeta();
161
+ if (!meta || typeof meta !== "object") {
162
+ return null;
163
+ }
164
+ const viewMeta = resolveViewMeta(meta);
165
+ if (!viewMeta) {
166
+ return null;
167
+ }
168
+ // Extract routeParams from context (set by formatPlayRouteTransitions on play.route events)
169
+ const context = snapshot.context !== null &&
170
+ snapshot.context !== undefined &&
171
+ typeof snapshot.context === "object"
172
+ ? snapshot.context
173
+ : null;
174
+ const routeParams = context !== null && typeof context.routeParams === "object" && context.routeParams !== null
175
+ ? context.routeParams
176
+ : {};
177
+ // Extract the allowlisted context fields declared in spec.contextProps.
178
+ // Only fields explicitly named in the allowlist are ever exposed to components.
179
+ const contextPropsAllowlist = viewMeta.spec?.contextProps ?? [];
180
+ const contextValues = {};
181
+ if (context !== null && contextPropsAllowlist.length > 0) {
182
+ for (const key of contextPropsAllowlist) {
183
+ if (key in context && context[key] !== null && context[key] !== undefined) {
184
+ contextValues[key] = context[key];
185
+ }
186
+ }
187
+ }
188
+ // Enrich element props when there is anything to merge.
189
+ if (viewMeta.spec?.elements) {
190
+ const enrichedElements = Object.fromEntries(Object.entries(viewMeta.spec.elements).map(([key, el]) => {
191
+ const element = el;
192
+ const existingProps = element.props ?? {};
193
+ return [
194
+ key,
195
+ {
196
+ ...element,
197
+ props: mergeRouteParamsIntoProps(routeParams, contextValues, existingProps),
198
+ },
199
+ ];
200
+ }));
201
+ return {
202
+ component: viewMeta.component,
203
+ spec: { ...viewMeta.spec, elements: enrichedElements },
204
+ };
205
+ }
206
+ return {
207
+ component: viewMeta.component,
208
+ spec: viewMeta.spec,
209
+ };
210
+ };
7
211
  /**
8
212
  * Concrete XState actor implementing Play Architecture signal protocol
9
213
  *
@@ -64,7 +268,7 @@ import { validateComponentBinding, validateViewProps, mergeViewProps } from "./c
64
268
  * // Watcher notification scheduled via microtask
65
269
  * ```
66
270
  *
67
- * @see {@link https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md | RFC Play v1}
271
+ * @see [Play RFC](../../docs/rfc/play.md)
68
272
  * @see {@link definePlayer} for factory creation
69
273
  * @see {@link @xmachines/play-actor!AbstractActor} for signal protocol
70
274
  * @see {@link @xmachines/play-actor!Routable} for routing capability
@@ -83,30 +287,87 @@ export class PlayerActor extends AbstractActor {
83
287
  _xstateActor;
84
288
  _stateManager;
85
289
  _options;
86
- _catalog;
87
290
  _viewSignal;
88
291
  // AbstractActor protocol requirements
89
292
  state;
293
+ /**
294
+ * A TC39 `Signal.Computed` that derives the current URL path from the active
295
+ * machine state's `meta.route` template and the actor's context.
296
+ *
297
+ * Returns `null` when the current state has no `meta.route`, or when the route
298
+ * template cannot be fully resolved (e.g. a required parameter is absent from
299
+ * context).
300
+ *
301
+ * @throws {MissingRouteParamError} When a required `:param` placeholder in the
302
+ * route template has no matching value in the actor's context. Import the class
303
+ * from `@xmachines/play-xstate/errors`.
304
+ *
305
+ * @example
306
+ * ```typescript
307
+ * // Returns "/profile/alice" when context.userId === "alice"
308
+ * const route = actor.currentRoute.get();
309
+ * ```
310
+ */
90
311
  currentRoute;
312
+ /**
313
+ * The route derived from the machine's initial state — fixed at construction,
314
+ * never changes even when the actor is restored from a snapshot.
315
+ *
316
+ * Router bridges compare this against the browser URL to distinguish a deep-link
317
+ * (non-initial URL → router wins) from a restore (initial URL + actor at a
318
+ * different restored route → actor wins).
319
+ */
320
+ initialRoute;
321
+ /**
322
+ * Reactive signal containing the current view spec derived from the active state's
323
+ * `meta.view` metadata.
324
+ *
325
+ * Always emits a **fresh object reference** on every state transition (including
326
+ * self-transitions with `reenter: true`) so TC39 Signal equality checks reliably
327
+ * detect changes.
328
+ *
329
+ * The emitted `ViewMetadata` has its spec element `props` enriched with
330
+ * `context.routeParams` before emission — URL path parameters (e.g. `:section?`)
331
+ * flow into component props automatically. See `mergeRouteParamsIntoProps` for the
332
+ * merge priority rules.
333
+ *
334
+ * Returns `null` when the current state has no `meta.view` metadata.
335
+ *
336
+ * @example
337
+ * ```typescript
338
+ * const view = actor.currentView.get();
339
+ * if (view) {
340
+ * console.log(view.component); // e.g. "Dashboard"
341
+ * console.log(view.spec); // @json-render/core Spec object
342
+ * }
343
+ * ```
344
+ */
91
345
  currentView;
92
- catalog;
93
- constructor(machine, catalog, options, input) {
346
+ constructor(machine, options, input, snapshot) {
94
347
  // Defensive check: Validate machine before passing to createActor
95
348
  if (!machine || typeof machine !== "object") {
96
349
  throw new Error("PlayerActor requires a valid XState machine");
97
350
  }
98
351
  // Create XState actor
99
- const xstateActor = createActor(machine, { input });
352
+ // XState 5.28.0: createActor() options has a conditional type constraint on `input` that
353
+ // TypeScript cannot resolve against an unbound generic TMachine parameter.
354
+ // Option B: Cast to ActorOptions<TMachine> — narrower than `as any` because we remain
355
+ // within the XState type system. The `input` parameter is typed as InputFrom<TMachine>
356
+ // at the constructor call site, so consumers receive compile-time validation.
357
+ // Track XState typing improvements: https://github.com/statelyai/xstate/issues
358
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
359
+ const xstateActor = createActor(machine, { input, snapshot });
100
360
  // Defensive check: Validate actor was created successfully
101
361
  if (!xstateActor) {
102
362
  throw new Error("Failed to create XState actor - machine may be null or invalid");
103
363
  }
104
364
  // Call AbstractActor constructor with actor logic
105
365
  super(xstateActor.logic);
366
+ // Derive the machine's initial route from a fresh (non-restored) snapshot.
367
+ // createActor(machine) without options is cheap and synchronous — never started.
368
+ this.initialRoute = deriveCurrentRoute(createActor(machine).getSnapshot());
106
369
  this._xstateActor = xstateActor;
107
370
  this._options = options || {};
108
- this._catalog = catalog || {};
109
- this.catalog = this._catalog;
110
371
  // Initialize state signal manager
111
372
  this._stateManager = new StateSignalManager(xstateActor.getSnapshot());
112
373
  this.state = this._stateManager.signal;
@@ -115,30 +376,14 @@ export class PlayerActor extends AbstractActor {
115
376
  // Initialize currentRoute computed signal
116
377
  this.currentRoute = new Signal.Computed(() => {
117
378
  const snapshot = this.state.get();
118
- // Defensive check: Validate snapshot and getMeta exists
119
- if (!snapshot || typeof snapshot.getMeta !== "function") {
120
- return null;
121
- }
122
- const meta = snapshot.getMeta();
123
- // Defensive check: Validate meta is an object
124
- if (!meta || typeof meta !== "object") {
125
- return null;
126
- }
127
- // Derive route from meta.route per Plan 04
128
- const routeTemplate = deriveRoute(meta);
129
- if (!routeTemplate) {
130
- return null;
131
- }
132
- // Build full URL with params, query, hash
133
- // Per CONTEXT.md: "Full URL generation including query params, hash, base path from context"
134
- return buildRouteUrl(routeTemplate, snapshot.context);
379
+ return deriveCurrentRoute(snapshot);
135
380
  });
136
381
  // Expose view signal directly for proper watcher propagation
137
382
  this.currentView = this._viewSignal;
138
383
  // Subscribe to XState actor transitions
139
384
  this._xstateActor.subscribe((snapshot) => {
140
385
  // Only update on stable/active states per CONTEXT.md
141
- if (snapshot.status === "active") {
386
+ if (isActiveSnapshot(snapshot)) {
142
387
  // Update state signal with microtask batching
143
388
  this._stateManager.scheduleUpdate(snapshot);
144
389
  // Validate view at state entry per CONTEXT.md: "Prop validation: At state entry"
@@ -177,33 +422,34 @@ export class PlayerActor extends AbstractActor {
177
422
  return this;
178
423
  }
179
424
  /**
180
- * Send event to actor
425
+ * Send an event to the underlying XState actor.
426
+ *
427
+ * The actor's state machine guards decide whether the event causes a transition.
428
+ * Pass any event from the machine's event union — domain events, routing events, etc.
181
429
  *
182
- * Forwards events to the underlying XState actor. The actor's state machine
183
- * guards determine whether each event is valid from the current state.
430
+ * @param event - An event from the machine's `EventFromLogic<TMachine>` union.
184
431
  *
185
- * @param event - Any event object with a type property
432
+ * @throws {InvalidEventError} When `event` is not a plain object (`null`, `undefined`,
433
+ * a string, number, etc.). Import the class from `@xmachines/play-xstate/errors`.
186
434
  *
187
435
  * @example
188
436
  * ```typescript
189
- * // Domain event
190
- * actor.send({ type: 'auth.login', userId: '123' });
437
+ * // Domain event (typed to machine's event union)
438
+ * actor.send({ type: "auth.login", userId: "123" });
191
439
  *
192
440
  * // Routing event
193
- * actor.send({ type: 'play.route', to: '#home' });
441
+ * actor.send({ type: "play.route", to: "#home" });
194
442
  * ```
195
443
  */
196
444
  send(event) {
197
445
  // Defensive check: Validate event is not null/undefined
198
446
  if (!event || typeof event !== "object") {
199
- console.warn("PlayerActor.send() called with invalid event:", event);
200
- return;
447
+ throw new InvalidEventError(event);
201
448
  }
202
449
  // Store previous state for onTransition hook
203
450
  const prevSnapshot = this._xstateActor.getSnapshot();
204
451
  // Send to XState actor
205
- // Note: TypeScript cast needed because XState's generic event type is narrower
206
- // than our generic PlayEvent. At runtime, both are { type: string, ...fields }.
452
+ // EventFromLogic<TMachine> is exactly what _xstateActor.send expects no cast needed.
207
453
  this._xstateActor.send(event);
208
454
  // Call onTransition hook
209
455
  if (this._options.onTransition) {
@@ -226,61 +472,17 @@ export class PlayerActor extends AbstractActor {
226
472
  * @param snapshot - Current XState snapshot
227
473
  */
228
474
  _validateAndCacheView(snapshot) {
229
- // Defensive check: Validate snapshot and getMeta exists
230
- if (!snapshot || typeof snapshot.getMeta !== "function") {
231
- this._viewSignal.set(null);
232
- return;
233
- }
234
- const meta = snapshot.getMeta();
235
- // Defensive check: Validate meta is an object
236
- if (!meta || typeof meta !== "object") {
237
- this._viewSignal.set(null);
238
- return;
239
- }
240
- // Find meta.view in active state nodes
241
- let viewMeta = null;
242
- for (const [_stateId, stateMeta] of Object.entries(meta)) {
243
- if (stateMeta && stateMeta.view) {
244
- viewMeta = stateMeta.view;
245
- break;
246
- }
247
- }
248
- if (!viewMeta) {
249
- this._viewSignal.set(null);
250
- return;
251
- }
252
- // Validate component binding
253
475
  try {
254
- validateComponentBinding(viewMeta, this._catalog);
476
+ const view = deriveCurrentView(snapshot);
477
+ this._viewSignal.set(view);
255
478
  }
256
479
  catch (error) {
257
- // Call onError hook if validation fails
258
480
  if (this._options.onError) {
259
- this._options.onError(this, error);
481
+ const err = error instanceof Error ? error : new Error(String(error));
482
+ this._options.onError(this, err);
260
483
  }
261
- console.error("Catalog binding validation failed:", error);
262
- this._viewSignal.set(null);
263
- return;
484
+ // On error: keep the last valid view (don't clear)
264
485
  }
265
- // Merge meta.view with context for props
266
- const props = mergeViewProps(viewMeta, snapshot.context);
267
- // Validate props against Zod schema
268
- const validation = validateViewProps(viewMeta.component, props, this._catalog);
269
- if (!validation.success) {
270
- // Call onError hook if prop validation fails
271
- if (this._options.onError) {
272
- this._options.onError(this, new Error(`Invalid props for component "${viewMeta.component}": ${validation.error?.message || "Unknown error"}`));
273
- }
274
- console.error("View props validation failed:", validation.error);
275
- // Store view structure even if validation fails (allow app to handle gracefully)
276
- // Per RESEARCH.md open question: prefer error events over throwing in production
277
- }
278
- // Cache validated view structure (validated once at state entry)
279
- const newView = {
280
- component: viewMeta.component,
281
- props: validation.success ? validation.data : props,
282
- };
283
- this._viewSignal.set(newView);
284
486
  }
285
487
  /**
286
488
  * Convenience dispose method for cleanup
@@ -1 +1 @@
1
- {"version":3,"file":"player-actor.js","sourceRoot":"","sources":["../src/player-actor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"player-actor.js","sourceRoot":"","sources":["../src/player-actor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=player-actor.typecheck.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"player-actor.typecheck.d.ts","sourceRoot":"","sources":["../src/player-actor.typecheck.ts"],"names":[],"mappings":""}
@@ -0,0 +1,27 @@
1
+ import { setup } from "xstate";
2
+ import { definePlayer } from "./define-player.js";
3
+ const machine = setup({
4
+ types: {
5
+ context: {},
6
+ input: {},
7
+ events: {},
8
+ },
9
+ }).createMachine({
10
+ context: ({ input }) => ({ value: input.value }),
11
+ initial: "idle",
12
+ states: {
13
+ idle: {},
14
+ },
15
+ });
16
+ const createPlayer = definePlayer({ machine });
17
+ const actor = createPlayer({ value: 1 });
18
+ const restoredActor = createPlayer({ value: 1 }, { snapshot: actor.getSnapshot() });
19
+ // Verify actor instances are created successfully
20
+ void actor;
21
+ void restoredActor;
22
+ // CONS-16: [key: string]: unknown index signature removed.
23
+ // Accessing unknown properties on PlayerActor is now a type error (correct behavior).
24
+ // @ts-expect-error — unknown fields no longer accessible via index signature
25
+ const unknownField = actor["custom-extension-field"];
26
+ void unknownField;
27
+ //# sourceMappingURL=player-actor.typecheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"player-actor.typecheck.js","sourceRoot":"","sources":["../src/player-actor.typecheck.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,OAAO,GAAG,KAAK,CAAC;IACrB,KAAK,EAAE;QACN,OAAO,EAAE,EAAuB;QAChC,KAAK,EAAE,EAAuB;QAC9B,MAAM,EAAE,EAAsB;KAC9B;CACD,CAAC,CAAC,aAAa,CAAC;IAChB,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IAChD,OAAO,EAAE,MAAM;IACf,MAAM,EAAE;QACP,IAAI,EAAE,EAAE;KACR;CACD,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;AAC/C,MAAM,KAAK,GAAG,YAAY,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;AACzC,MAAM,aAAa,GAAG,YAAY,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AAEpF,kDAAkD;AAClD,KAAK,KAAK,CAAC;AACX,KAAK,aAAa,CAAC;AAEnB,2DAA2D;AAC3D,sFAAsF;AACtF,6EAA6E;AAC7E,MAAM,YAAY,GAAG,KAAK,CAAC,wBAAwB,CAAC,CAAC;AACrD,KAAK,YAAY,CAAC"}
@@ -1,27 +1,33 @@
1
1
  import type { RouteContext } from "./types.js";
2
2
  /**
3
- * Build full URL from route template and context
3
+ * Build a full URL from a route template and the actor's context.
4
4
  *
5
- * Per CONTEXT.md:
6
- * - "currentRoute derivation: Full URL generation including query params, hash, base path"
7
- * - "Parameters: String template syntax — /user/:id"
8
- * - "Inheritance: relative paths inherit parent route"
5
+ * Substitutes `:param` and `:param?` placeholders from `context.routeParams`
6
+ * or flat `context` values, then appends query params and hash fragments.
9
7
  *
10
- * Per RESEARCH.md Pattern 3: Replace :param with context values
8
+ * Parameter lookup order for each placeholder:
9
+ * 1. `context.routeParams[param]` — preferred when the state machine stores
10
+ * route parameters in a dedicated sub-object
11
+ * 2. `context[param]` — flat context fallback
12
+ *
13
+ * @param routeTemplate - Route path template, e.g. `"/profile/:userId"` or
14
+ * `"/settings/:section?"`.
15
+ * @param context - Actor context object containing parameter values, optional
16
+ * `query` record, optional `hash` string, and optional `basePath` prefix.
17
+ * @returns The fully resolved URL string.
18
+ *
19
+ * @throws {MissingRouteParamError} When a **required** `:param` placeholder has no
20
+ * matching value in context. Optional parameters (`:param?`) are silently omitted
21
+ * when missing. Import the class from `@xmachines/play-xstate/errors`.
11
22
  *
12
23
  * @example
13
24
  * ```typescript
14
- * const url = buildRouteUrl('/user/:id', {
15
- * id: '123',
16
- * query: { tab: 'profile' },
17
- * hash: 'section-1'
18
- * });
19
- * // Result: '/user/123?tab=profile#section-1'
20
- * ```
25
+ * buildRouteUrl("/user/:id", { id: "123", query: { tab: "profile" }, hash: "top" });
26
+ * // → "/user/123?tab=profile#top"
21
27
  *
22
- * @param routeTemplate - Route path with :param placeholders
23
- * @param context - Route context with parameters, query, hash
24
- * @returns Full URL string
28
+ * buildRouteUrl("/settings/:section?", {});
29
+ * // "/settings" (optional param omitted)
30
+ * ```
25
31
  */
26
32
  export declare const buildRouteUrl: (routeTemplate: string, context?: RouteContext) => string;
27
33
  //# sourceMappingURL=build-url.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-url.d.ts","sourceRoot":"","sources":["../../src/routing/build-url.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,aAAa,GAAI,eAAe,MAAM,EAAE,UAAS,YAAiB,KAAG,MA0BjF,CAAC"}
1
+ {"version":3,"file":"build-url.d.ts","sourceRoot":"","sources":["../../src/routing/build-url.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,aAAa,GAAI,eAAe,MAAM,EAAE,UAAS,YAAiB,KAAG,MA6BjF,CAAC"}