@xmachines/play-xstate 2.0.0-alpha.1 → 2.0.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.
Files changed (84) hide show
  1. package/README.md +90 -91
  2. package/dist/define-player.d.ts +3 -3
  3. package/dist/define-player.js +3 -3
  4. package/dist/errors.d.ts +46 -70
  5. package/dist/errors.d.ts.map +1 -1
  6. package/dist/errors.js +69 -77
  7. package/dist/errors.js.map +1 -1
  8. package/dist/guards/compose.d.ts +49 -59
  9. package/dist/guards/compose.d.ts.map +1 -1
  10. package/dist/guards/compose.js +68 -95
  11. package/dist/guards/compose.js.map +1 -1
  12. package/dist/guards/helpers.d.ts +6 -2
  13. package/dist/guards/helpers.d.ts.map +1 -1
  14. package/dist/guards/helpers.js +6 -2
  15. package/dist/guards/helpers.js.map +1 -1
  16. package/dist/guards/index.d.ts +7 -0
  17. package/dist/guards/index.d.ts.map +1 -1
  18. package/dist/guards/index.js +7 -0
  19. package/dist/guards/index.js.map +1 -1
  20. package/dist/guards/types.d.ts +4 -5
  21. package/dist/guards/types.d.ts.map +1 -1
  22. package/dist/index.d.ts +3 -4
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +2 -4
  25. package/dist/index.js.map +1 -1
  26. package/dist/player-actor.d.ts +112 -44
  27. package/dist/player-actor.d.ts.map +1 -1
  28. package/dist/player-actor.js +311 -327
  29. package/dist/player-actor.js.map +1 -1
  30. package/dist/routing/build-url.d.ts +2 -7
  31. package/dist/routing/build-url.d.ts.map +1 -1
  32. package/dist/routing/build-url.js +21 -25
  33. package/dist/routing/build-url.js.map +1 -1
  34. package/dist/routing/derive-current-route.d.ts +32 -0
  35. package/dist/routing/derive-current-route.d.ts.map +1 -1
  36. package/dist/routing/derive-current-route.js +20 -19
  37. package/dist/routing/derive-current-route.js.map +1 -1
  38. package/dist/routing/derive-initial-route.d.ts +1 -4
  39. package/dist/routing/derive-initial-route.d.ts.map +1 -1
  40. package/dist/routing/derive-initial-route.js +1 -4
  41. package/dist/routing/derive-initial-route.js.map +1 -1
  42. package/dist/routing/format-play-route-transitions.d.ts +11 -54
  43. package/dist/routing/format-play-route-transitions.d.ts.map +1 -1
  44. package/dist/routing/format-play-route-transitions.js +65 -125
  45. package/dist/routing/format-play-route-transitions.js.map +1 -1
  46. package/dist/routing/index.d.ts +1 -5
  47. package/dist/routing/index.d.ts.map +1 -1
  48. package/dist/routing/index.js +1 -3
  49. package/dist/routing/index.js.map +1 -1
  50. package/dist/types.d.ts +87 -14
  51. package/dist/types.d.ts.map +1 -1
  52. package/dist/view/derive-current-view.d.ts +49 -0
  53. package/dist/view/derive-current-view.d.ts.map +1 -0
  54. package/dist/view/derive-current-view.js +115 -0
  55. package/dist/view/derive-current-view.js.map +1 -0
  56. package/package.json +22 -22
  57. package/dist/define-player.typecheck.d.ts +0 -2
  58. package/dist/define-player.typecheck.d.ts.map +0 -1
  59. package/dist/define-player.typecheck.js +0 -48
  60. package/dist/define-player.typecheck.js.map +0 -1
  61. package/dist/guards/compose.typecheck.d.ts +0 -2
  62. package/dist/guards/compose.typecheck.d.ts.map +0 -1
  63. package/dist/guards/compose.typecheck.js +0 -22
  64. package/dist/guards/compose.typecheck.js.map +0 -1
  65. package/dist/player-actor.typecheck.d.ts +0 -2
  66. package/dist/player-actor.typecheck.d.ts.map +0 -1
  67. package/dist/player-actor.typecheck.js +0 -30
  68. package/dist/player-actor.typecheck.js.map +0 -1
  69. package/dist/routing/create-routed-machine.d.ts +0 -71
  70. package/dist/routing/create-routed-machine.d.ts.map +0 -1
  71. package/dist/routing/create-routed-machine.js +0 -71
  72. package/dist/routing/create-routed-machine.js.map +0 -1
  73. package/dist/routing/play-route-event.typecheck.d.ts +0 -2
  74. package/dist/routing/play-route-event.typecheck.d.ts.map +0 -1
  75. package/dist/routing/play-route-event.typecheck.js +0 -43
  76. package/dist/routing/play-route-event.typecheck.js.map +0 -1
  77. package/dist/routing/schemas.d.ts +0 -99
  78. package/dist/routing/schemas.d.ts.map +0 -1
  79. package/dist/routing/schemas.js +0 -30
  80. package/dist/routing/schemas.js.map +0 -1
  81. package/dist/schemas.d.ts +0 -28
  82. package/dist/schemas.d.ts.map +0 -1
  83. package/dist/schemas.js +0 -29
  84. package/dist/schemas.js.map +0 -1
@@ -1,18 +1,26 @@
1
+ import { assign } from "xstate";
2
+ import { shallowEqualExcept } from "@xmachines/play-actor";
1
3
  import { MissingStateIdError } from "../errors.js";
2
4
  import { normalizeRoute } from "./derive-route.js";
3
5
  /**
4
- * Formats play.route routing from declarative route configs.
6
+ * Reuse the previous params/query container when the incoming one is
7
+ * shallow-equal.
5
8
  *
6
- * Crawls machine states looking for states with `meta.route` and wires them to
7
- * XState v6's **native routing machinery**:
9
+ * Route params/query arrive as a FRESH object on every `play.route` event —
10
+ * bridges rebuild them per URL parse, and `|| {}` allocates even for the
11
+ * empty case — while the view emit gate compares context per field with
12
+ * `Object.is`. Without reuse, a value-identical re-navigation (popstate to
13
+ * the current URL, a redundant programmatic send) would re-emit the view
14
+ * for nothing.
15
+ */
16
+ const keepEqualContainer = (prev, next) => (prev !== undefined && shallowEqualExcept(prev, next) ? prev : next);
17
+ /**
18
+ * Formats play.route transitions from declarative route configs
8
19
  *
9
- * - every routed state gets a native `route: {}` config (unless it already
10
- * declares its own `route`), making it targetable via the built-in
11
- * `{ type: "xstate.route", to: "#id" }` event (Stately tooling interop and
12
- * statically-known graph edges), and
13
- * - one root-level `play.route` forwarder performs the navigation for those
14
- * targets as ONE atomic transition: target + re-entry + a shallow
15
- * `params`/`query` context patch.
20
+ * Crawls machine states looking for states with meta.route and generates
21
+ * transitions that handle `play.route` events by matching event.to to state IDs.
22
+ *
23
+ * Inspired by XState's internal formatRouteTransitions (stateUtils.ts line 391).
16
24
  *
17
25
  * @example
18
26
  * ```typescript
@@ -27,58 +35,21 @@ import { normalizeRoute } from "./derive-route.js";
27
35
  * const machine = createMachine(formatPlayRouteTransitions(machineConfig));
28
36
  * ```
29
37
  *
30
- * Because the injected `route` configs are static (object form), the machine's
31
- * route transitions keep **statically-known targets** — `toDirectedGraph()` /
32
- * `machineToGraph()` edges point at the actual routed states, so graph
33
- * reachability queries work. (Function-form transition candidates would have
34
- * dynamic targets, opaque to static analysis.)
35
- *
36
- * The `play.route` public API and its v5 semantics are preserved:
37
- * - `send({ type: "play.route", to: "#home", params, query })` navigates and
38
- * patches `context.params`/`context.query` in the SAME transition — exit
39
- * actions of the departing state observe the OLD params, entry actions of
40
- * the target observe the NEW ones, `always` transitions never see a
41
- * half-navigated intermediate state, and entry/exit actions receive the
42
- * original `play.route` event;
43
- * - user-defined `play.route` transitions (e.g. a hand-written 404 fallback)
44
- * are preserved AFTER the forwarder: for unknown targets the forwarder
45
- * returns `undefined`, so evaluation falls through to them;
46
- * - `params`/`query` are only patched when the forwarder navigates — unknown
47
- * and blocked targets leave context untouched.
48
- *
49
- * When a state declares its OWN `route` config (which wins over the injected
50
- * default), the forwarder re-raises the event as `xstate.route` instead of
51
- * navigating, so the user's resolver decides — including blocking. The
52
- * forwarder does NOT patch `params`/`query` for such targets (a blocking
53
- * resolver would otherwise leave context describing a route that was never
54
- * entered); the user's function-form resolver owns any context patch, and
55
- * that navigation microstep runs under the `xstate.route` event.
56
- *
57
- * Native `xstate.route` events also work directly (Stately ecosystem tooling),
58
- * but bypass the forwarder — no `params`/`query` context patch is applied, so
59
- * stale values from earlier `play.route` events linger in context (and a
60
- * router bridge deriving the URL from context would interpolate those stale
61
- * params).
38
+ * This automatically generates play.route handlers at the root level that:
39
+ * - Match event.to against state IDs (e.g., event.to === "#home")
40
+ * - Target the appropriate state
41
+ * - Assign params and query from the event to context
62
42
  *
63
43
  * @param machineConfig - XState machine config (before createMachine). Must extend `RouteMachineConfig`.
64
- * @returns The machine config with native `route` configs and the `play.route` forwarder merged in, preserving the original type `T`.
44
+ * @returns The same machine config with auto-generated `play.route` handlers merged in, preserving the original type `T`.
65
45
  */
66
46
  export function formatPlayRouteTransitions(machineConfig) {
67
- /**
68
- * Routing table: `#id` -> the state's key path (e.g. `.dashboard.stats`)
69
- * for targets whose `route` config was injected by this transform, or
70
- * `null` for targets with a USER-declared `route` config (the user's
71
- * resolver decides those — the forwarder re-raises instead of navigating).
72
- */
73
- const routeTargets = new Map();
74
- /**
75
- * Rebuild the states tree, injecting a native `route` config into routed
76
- * nodes. The input config is never mutated — every node is shallow-copied.
77
- */
78
- const injectRoutes = (states, parentPath = "") => {
79
- const next = {};
80
- for (const [key, stateConfig] of Object.entries(states)) {
81
- const node = { ...stateConfig };
47
+ const routeTransitions = [];
48
+ const collectRoutes = (states, parentPath = "") => {
49
+ Object.entries(states).forEach(([key, stateConfig]) => {
50
+ const node = stateConfig;
51
+ // Build the full key-based path from the root for the transition target
52
+ // (XState targets use the state key hierarchy, not explicit IDs)
82
53
  const statePath = parentPath ? `${parentPath}.${key}` : key;
83
54
  // Validates the metadata (malformed object routes throw
84
55
  // InvalidRouteMetadataError) and normalizes to the path string. Both
@@ -90,79 +61,48 @@ export function formatPlayRouteTransitions(machineConfig) {
90
61
  if (routePath && !node.id) {
91
62
  throw new MissingStateIdError(key, routePath);
92
63
  }
93
- if (node.states) {
94
- node.states = injectRoutes(node.states, statePath);
95
- }
96
64
  if (routePath && node.id) {
97
- if (node.route === undefined) {
98
- // Native object-form route config: statically-targeted transition,
99
- // visible to graph tooling. The play.route forwarder navigates these
100
- // targets DIRECTLY (atomically); the native config exists for
101
- // xstate.route interop (Stately tooling, devtools).
102
- node.route = {};
103
- routeTargets.set(`#${node.id}`, `.${statePath}`);
104
- }
105
- else {
106
- // A user-declared route config wins: the forwarder re-raises
107
- // xstate.route so the user's resolver decides (and owns any
108
- // context patch).
109
- routeTargets.set(`#${node.id}`, null);
110
- }
65
+ const transition = {
66
+ target: `.${statePath}`,
67
+ guard: ({ event }) => event.to === `#${node.id}`,
68
+ reenter: true,
69
+ actions: assign({
70
+ params: ({ context, event }) => keepEqualContainer(context.params, event.params || {}),
71
+ query: ({ context, event }) => keepEqualContainer(context.query, event.query || {}),
72
+ }),
73
+ };
74
+ routeTransitions.push(transition);
111
75
  }
112
- next[key] = node;
113
- }
114
- return next;
76
+ if (node.states) {
77
+ collectRoutes(node.states, statePath);
78
+ }
79
+ });
115
80
  };
116
81
  const machineStates = machineConfig.states;
117
- const injectedStates = machineStates ? injectRoutes(machineStates) : machineStates;
118
- if (routeTargets.size === 0) {
119
- return machineConfig;
82
+ if (machineStates) {
83
+ collectRoutes(machineStates);
120
84
  }
121
- // Root play.route forwarder. For injected-route targets it returns ONE
122
- // atomic transition (target + reenter + params/query patch): exit actions
123
- // see the OLD params, `always` transitions never observe a half-navigated
124
- // intermediate state, and entry/exit actions receive the original
125
- // play.route event. For user-routed targets it re-raises xstate.route
126
- // WITHOUT patching (the user's resolver owns the patch — otherwise a
127
- // blocking resolver would leave context describing a route that was never
128
- // entered). Unknown targets return undefined so user fallback candidates
129
- // below are evaluated.
130
- const forwarder = ({ event }, enq) => {
131
- const target = routeTargets.get(event.to);
132
- if (target === undefined)
133
- return undefined;
134
- if (target === null) {
135
- enq.raise({ ...event, type: "xstate.route" });
136
- return {};
137
- }
85
+ if (routeTransitions.length > 0) {
86
+ const existingOn = machineConfig.on || {};
87
+ // Preserve user-defined play.route transitions (e.g. a hand-written 404
88
+ // fallback) by appending them AFTER the generated ones: XState evaluates
89
+ // candidate transitions in order, so generated guards win when they match
90
+ // and the user's transitions act as fallbacks otherwise.
91
+ const userRouteTransitions = existingOn["play.route"];
92
+ const normalizedUserTransitions = userRouteTransitions === undefined
93
+ ? []
94
+ : Array.isArray(userRouteTransitions)
95
+ ? userRouteTransitions
96
+ : [userRouteTransitions];
97
+ const updatedOn = {
98
+ ...existingOn,
99
+ "play.route": [...routeTransitions, ...normalizedUserTransitions],
100
+ };
138
101
  return {
139
- target,
140
- reenter: true,
141
- // Shallow context patch — v6's replacement for assign().
142
- context: {
143
- params: event.params || {},
144
- query: event.query || {},
145
- },
102
+ ...machineConfig,
103
+ on: updatedOn,
146
104
  };
147
- };
148
- // Preserve user-defined play.route transitions (e.g. a hand-written 404
149
- // fallback) by appending them AFTER the forwarder: XState evaluates
150
- // candidate transitions in order, so the forwarder wins for known targets
151
- // and the user's transitions act as fallbacks otherwise.
152
- const existingOn = machineConfig.on || {};
153
- const userRouteTransitions = existingOn["play.route"];
154
- const normalizedUserTransitions = userRouteTransitions === undefined
155
- ? []
156
- : Array.isArray(userRouteTransitions)
157
- ? userRouteTransitions
158
- : [userRouteTransitions];
159
- return {
160
- ...machineConfig,
161
- states: injectedStates,
162
- on: {
163
- ...existingOn,
164
- "play.route": [forwarder, ...normalizedUserTransitions],
165
- },
166
- };
105
+ }
106
+ return machineConfig;
167
107
  }
168
108
  //# sourceMappingURL=format-play-route-transitions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"format-play-route-transitions.js","sourceRoot":"","sources":["../../src/routing/format-play-route-transitions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"format-play-route-transitions.js","sourceRoot":"","sources":["../../src/routing/format-play-route-transitions.ts"],"names":[],"mappings":"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"}
@@ -7,14 +7,10 @@
7
7
  * @packageDocumentation
8
8
  */
9
9
  export { deriveRoute, isAbsoluteRoute } from "./derive-route.js";
10
- export { deriveCurrentRoute } from "./derive-current-route.js";
10
+ export { activeStateMeta, deriveCurrentRoute, firstActiveBranchMeta, } from "./derive-current-route.js";
11
11
  export { deriveInitialRoute } from "./derive-initial-route.js";
12
12
  export { buildRouteUrl } from "./build-url.js";
13
13
  export { formatPlayRouteTransitions } from "./format-play-route-transitions.js";
14
- export { playMetaSchema, playRouteEventSchema } from "./schemas.js";
15
- export type { PlayRoutePayload, WithOptional } from "./schemas.js";
16
- export type { SetupLike } from "./create-routed-machine.js";
17
- export { createRoutedMachine } from "./create-routed-machine.js";
18
14
  export type { RouteMachineConfig, RouteStateNode } from "./format-play-route-transitions.js";
19
15
  export type { RouteContext, RouteObject, RouteMetadata } from "./types.js";
20
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACnE,YAAY,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAC7F,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EACN,eAAe,EACf,kBAAkB,EAClB,qBAAqB,GACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAC7F,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -7,10 +7,8 @@
7
7
  * @packageDocumentation
8
8
  */
9
9
  export { deriveRoute, isAbsoluteRoute } from "./derive-route.js";
10
- export { deriveCurrentRoute } from "./derive-current-route.js";
10
+ export { activeStateMeta, deriveCurrentRoute, firstActiveBranchMeta, } from "./derive-current-route.js";
11
11
  export { deriveInitialRoute } from "./derive-initial-route.js";
12
12
  export { buildRouteUrl } from "./build-url.js";
13
13
  export { formatPlayRouteTransitions } from "./format-play-route-transitions.js";
14
- export { playMetaSchema, playRouteEventSchema } from "./schemas.js";
15
- export { createRoutedMachine } from "./create-routed-machine.js";
16
14
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EACN,eAAe,EACf,kBAAkB,EAClB,qBAAqB,GACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC"}
package/dist/types.d.ts CHANGED
@@ -1,30 +1,93 @@
1
- import type { AnyStateMachine, InputFrom, SnapshotFrom } from "xstate";
1
+ import type { ActorOptions, AnyStateMachine, InputFrom, SnapshotFrom } from "xstate";
2
2
  import type { PlayerActor as PlayerActorClass } from "./player-actor.js";
3
3
  /**
4
4
  * Configuration for definePlayer()
5
5
  */
6
6
  export interface PlayerConfig<TMachine extends AnyStateMachine> {
7
- /** XState v6 state machine */
7
+ /** XState v5 state machine */
8
8
  machine: TMachine;
9
9
  /** Lifecycle hooks and configuration */
10
10
  options?: PlayerOptions<TMachine>;
11
11
  }
12
12
  /**
13
- * Player lifecycle hooks
14
- *
15
- * Per CONTEXT.md: Rich set of hooks for observability
13
+ * Player lifecycle hooks — the observability surface around the actor.
16
14
  */
17
15
  export interface PlayerOptions<TMachine extends AnyStateMachine> {
18
- /** Called when actor starts */
16
+ /**
17
+ * Called on each real start: every transition from not-running to running,
18
+ * including a start after a stop. A repeated `start()` while the actor is
19
+ * already running does not re-fire it.
20
+ */
19
21
  onStart?: (actor: PlayerActorClass<TMachine>) => void;
20
- /** Called when actor stops */
22
+ /**
23
+ * Called on each real stop: only when a running actor was actually torn
24
+ * down. Repeated `stop()`/`dispose()` calls, and stopping an actor that
25
+ * never started, do not fire it.
26
+ */
21
27
  onStop?: (actor: PlayerActorClass<TMachine>) => void;
22
- /** Called on every state transition */
28
+ /**
29
+ * Called after every event `send()` processes — including events the
30
+ * machine ignores, where `prevState` and `nextState` are the same
31
+ * snapshot. Compare the two when only real transitions matter.
32
+ */
23
33
  onTransition?: (actor: PlayerActorClass<TMachine>, prevState: SnapshotFrom<TMachine>, nextState: SnapshotFrom<TMachine>) => void;
24
34
  /** Called when state signal changes */
25
35
  onStateChange?: (actor: PlayerActorClass<TMachine>, state: SnapshotFrom<TMachine>) => void;
26
- /** Called on actor errors */
36
+ /**
37
+ * Called on actor errors: a snapshot restore that fails at `start()`, a
38
+ * throwing action or guard, and view-derivation failures.
39
+ *
40
+ * Read when an error is delivered, not at construction: the options bag is
41
+ * shared by reference, so a handler attached to it later still receives
42
+ * actor errors, and removing the handler restores the loud default below.
43
+ *
44
+ * With a handler in place, the error is routed there and counts as
45
+ * handled. XState decides its global rethrow per observer, beyond this
46
+ * option's reach: a subscription of your own without an `error` listener
47
+ * still forces it, `onError` or not.
48
+ *
49
+ * Without `onError`, actor errors stay loud (an unhandled rethrow via
50
+ * `setTimeout`) so they are never silently swallowed — and your own
51
+ * `error` subscribers do not suppress that default; only `onError` does.
52
+ *
53
+ * An actor failure that is already an `Error` arrives unchanged, keeping the
54
+ * identity the machine gave it. A machine that throws a non-`Error` value
55
+ * arrives as `ActorThrewNonErrorError` with the thrown value on `cause`.
56
+ */
27
57
  onError?: (actor: PlayerActorClass<TMachine>, error: Error) => void;
58
+ /**
59
+ * Inspection observer forwarded verbatim to XState's `createActor`.
60
+ *
61
+ * This is the creation-time attachment route, and the only one that observes
62
+ * the actor's construction events. Attaching later via
63
+ * `actor.system.inspect(fn)` also works, but only sees events from that
64
+ * point on.
65
+ *
66
+ * A `PlayerActor` is itself the XState actor, so its own events carry
67
+ * `actorRef === playerActor` and can be recognised by identity. Each
68
+ * `PlayerActor` is also its own root system, so `event.rootId ===
69
+ * actor.sessionId` demultiplexes the whole tree — including events from
70
+ * invoked and spawned children, whose `actorRef` is the child.
71
+ *
72
+ * One caveat comes with that identity: the `@xstate.actor` event fires from
73
+ * inside the actor's constructor, so its `actorRef` is the instance
74
+ * mid-construction. `state`, `currentRoute`, `currentView` and
75
+ * `initialRoute` do not exist yet — reading them there throws. Capture the
76
+ * reference and read the signals from a later event, or from outside the
77
+ * observer.
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * import { createBrowserInspector } from "@statelyai/inspect";
82
+ *
83
+ * const { inspect } = createBrowserInspector();
84
+ * const createPlayer = definePlayer({ machine, options: { inspect } });
85
+ * ```
86
+ *
87
+ * For an inspector created after the factory (e.g. behind a dev-tools toggle),
88
+ * pass a forwarding function: `inspect: (event) => currentInspector?.(event)`.
89
+ */
90
+ inspect?: ActorOptions<TMachine>["inspect"];
28
91
  }
29
92
  /**
30
93
  * Optional restore arguments for the player factory.
@@ -33,13 +96,23 @@ export interface PlayerOptions<TMachine extends AnyStateMachine> {
33
96
  * `createPlayer(input?)` calling convention for fresh actors.
34
97
  */
35
98
  export interface PlayerFactoryResumeOptions<TMachine extends AnyStateMachine> {
36
- /** Persisted XState snapshot used to restore actor state. */
37
- snapshot?: SnapshotFrom<TMachine>;
99
+ /**
100
+ * Persisted XState snapshot used to restore actor state.
101
+ *
102
+ * Typed as XState's own `ActorOptions["snapshot"]` — the exact type
103
+ * `createActor` accepts and `getPersistedSnapshot()` returns — so restoring
104
+ * a stored snapshot never needs a cast.
105
+ */
106
+ snapshot?: ActorOptions<TMachine>["snapshot"];
38
107
  }
39
108
  /**
40
- * Factory function returned by definePlayer()
109
+ * Factory function returned by definePlayer() — each call creates an
110
+ * independent actor instance from the same configuration.
41
111
  *
42
- * Per CONTEXT.md: Factory supports creating multiple actor instances
112
+ * Input requiredness mirrors XState's `createActor`: a machine whose input
113
+ * cannot be `undefined` makes the factory's first argument required, so
114
+ * forgetting it is a compile error instead of an actor that starts in an
115
+ * error status with a `null` initial route.
43
116
  */
44
- export type PlayerFactory<TMachine extends AnyStateMachine> = (input?: InputFrom<TMachine>, options?: PlayerFactoryResumeOptions<TMachine>) => PlayerActorClass<TMachine>;
117
+ export type PlayerFactory<TMachine extends AnyStateMachine> = undefined extends InputFrom<TMachine> ? (input?: InputFrom<TMachine>, options?: PlayerFactoryResumeOptions<TMachine>) => PlayerActorClass<TMachine> : (input: InputFrom<TMachine>, options?: PlayerFactoryResumeOptions<TMachine>) => PlayerActorClass<TMachine>;
45
118
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,QAAQ,SAAS,eAAe;IAC7D,8BAA8B;IAC9B,OAAO,EAAE,QAAQ,CAAC;IAElB,wCAAwC;IACxC,OAAO,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CAClC;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa,CAAC,QAAQ,SAAS,eAAe;IAC9D,+BAA+B;IAC/B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAEtD,8BAA8B;IAC9B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAErD,uCAAuC;IACvC,YAAY,CAAC,EAAE,CACd,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EACjC,SAAS,EAAE,YAAY,CAAC,QAAQ,CAAC,EACjC,SAAS,EAAE,YAAY,CAAC,QAAQ,CAAC,KAC7B,IAAI,CAAC;IAEV,uCAAuC;IACvC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAE3F,6BAA6B;IAC7B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACpE;AAED;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B,CAAC,QAAQ,SAAS,eAAe;IAC3E,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;CAClC;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,QAAQ,SAAS,eAAe,IAAI,CAC7D,KAAK,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,EAC3B,OAAO,CAAC,EAAE,0BAA0B,CAAC,QAAQ,CAAC,KAC1C,gBAAgB,CAAC,QAAQ,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACrF,OAAO,KAAK,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,QAAQ,SAAS,eAAe;IAC7D,8BAA8B;IAC9B,OAAO,EAAE,QAAQ,CAAC;IAElB,wCAAwC;IACxC,OAAO,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,QAAQ,SAAS,eAAe;IAC9D;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAEtD;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAErD;;;;OAIG;IACH,YAAY,CAAC,EAAE,CACd,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EACjC,SAAS,EAAE,YAAY,CAAC,QAAQ,CAAC,EACjC,SAAS,EAAE,YAAY,CAAC,QAAQ,CAAC,KAC7B,IAAI,CAAC;IAEV,uCAAuC;IACvC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IAE3F;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC;CAC5C;AAED;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B,CAAC,QAAQ,SAAS,eAAe;IAC3E;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,CAAC;CAC9C;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,CAAC,QAAQ,SAAS,eAAe,IACzD,SAAS,SAAS,SAAS,CAAC,QAAQ,CAAC,GAClC,CACA,KAAK,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,EAC3B,OAAO,CAAC,EAAE,0BAA0B,CAAC,QAAQ,CAAC,KAC1C,gBAAgB,CAAC,QAAQ,CAAC,GAC9B,CACA,KAAK,EAAE,SAAS,CAAC,QAAQ,CAAC,EAC1B,OAAO,CAAC,EAAE,0BAA0B,CAAC,QAAQ,CAAC,KAC1C,gBAAgB,CAAC,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * View derivation — the meta.view half of the player pipeline.
3
+ *
4
+ * Kept beside src/routing/ so player-actor.ts holds only the actor itself:
5
+ * signals, hooks, and lifecycle. The active-branch walk is shared with route
6
+ * derivation so a parallel machine's view always belongs to the region its
7
+ * URL points at.
8
+ */
9
+ import type { AnyMachineSnapshot } from "xstate";
10
+ import { type PlaySpec } from "@xmachines/play-actor";
11
+ /**
12
+ * Derive the actor's current view from state metadata.
13
+ *
14
+ * Always returns a **fresh** `PlaySpec` object so a genuine view change is never
15
+ * suppressed by `Signal.State`'s `Object.is` equality check. Whether that fresh
16
+ * object is actually emitted is decided by `PlayerActor.validateAndCacheView`,
17
+ * which compares against the last emitted spec (reusing the composed `state`
18
+ * reference when the projection is value-unchanged) and skips emission when the
19
+ * rendered view is unchanged — preventing downstream providers from remounting
20
+ * the UI on every event.
21
+ *
22
+ * ### Context projection
23
+ *
24
+ * The machine's context is projected into the derived spec's `state` under the
25
+ * read-only `/context` subtree: `state: { ...meta.view.state, context: slice }`.
26
+ * The emitted spec is therefore **self-consistent** — its `state` honestly
27
+ * describes the store contents — so `repeat.statePath: "/context/tasks"`,
28
+ * `visible: { $state: "/context/…" }`, and `{ $state: "/context/…" }` props all
29
+ * resolve through the ordinary state grammar, and tools like `validateSpec`
30
+ * pass on derived views with no special-casing (validate the derived view, not
31
+ * the raw `meta.view`).
32
+ *
33
+ * The whole context is always projected; the authored `meta.view` object is
34
+ * never mutated.
35
+ *
36
+ * ### viewKey
37
+ *
38
+ * The derived spec carries `viewKey` — the meta record key (state node id) of
39
+ * the entry this derivation actually selected. Providers key their store
40
+ * lifecycle on it: changed → reseed, unchanged → refresh `/context` in place.
41
+ * It is taken from the selected entry rather than "the active state id"
42
+ * because for parallel machines the walked branch and the flat `getMeta()`
43
+ * fallback can select a view from a different region than the branch leaf.
44
+ *
45
+ * @param snapshot - Current XState machine snapshot.
46
+ * @returns Derived `PlaySpec`, or `null` if the current state has no view metadata.
47
+ */
48
+ export declare const deriveCurrentView: (snapshot: AnyMachineSnapshot) => PlaySpec | null;
49
+ //# sourceMappingURL=derive-current-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive-current-view.d.ts","sourceRoot":"","sources":["../../src/view/derive-current-view.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAiC,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAsDrF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,kBAAkB,KAAG,QAAQ,GAAG,IA4C3E,CAAC"}
@@ -0,0 +1,115 @@
1
+ import { composePlayState, toAtomState } from "@xmachines/play-actor";
2
+ import { activeStateMeta } from "../routing/index.js";
3
+ /**
4
+ * Once-per-spec migration warning for the removed 1.x `contextProps` field.
5
+ * Hand-written meta.view objects bypass `typedSpec`, so a spec still carrying
6
+ * the field compiles clean — but its meaning inverted (an allowlist, or an
7
+ * empty filter projecting nothing, is now the whole context). Keyed on the
8
+ * static meta.view object so the warning fires once, not per snapshot.
9
+ */
10
+ const warnedContextProps = new WeakSet();
11
+ const warnRemovedContextProps = (viewMeta) => {
12
+ if (warnedContextProps.has(viewMeta))
13
+ return;
14
+ warnedContextProps.add(viewMeta);
15
+ console.warn("[play-xstate] meta.view.contextProps has been removed: the whole machine context " +
16
+ "is always projected under /context. Delete the field — it no longer filters anything.");
17
+ };
18
+ const resolveViewMeta = (meta) => {
19
+ // Iterate last-to-first: snapshot.getMeta() orders keys ancestors-first, so
20
+ // scanning from the end makes the deepest active state's meta.view win over
21
+ // an ancestor's instead of being shadowed by it.
22
+ const entries = Object.entries(meta);
23
+ for (let i = entries.length - 1; i >= 0; i--) {
24
+ // Loop-bounded array index, not user-controlled property access.
25
+ const [key, stateMeta] = entries[i]; // nosemgrep: gitlab.eslint.detect-object-injection
26
+ const maybeView = stateMeta && typeof stateMeta === "object"
27
+ ? stateMeta.view
28
+ : undefined;
29
+ if (maybeView &&
30
+ typeof maybeView === "object" &&
31
+ "root" in maybeView &&
32
+ "elements" in maybeView) {
33
+ return { key, view: maybeView };
34
+ }
35
+ }
36
+ return null;
37
+ };
38
+ /**
39
+ * Derive the actor's current view from state metadata.
40
+ *
41
+ * Always returns a **fresh** `PlaySpec` object so a genuine view change is never
42
+ * suppressed by `Signal.State`'s `Object.is` equality check. Whether that fresh
43
+ * object is actually emitted is decided by `PlayerActor.validateAndCacheView`,
44
+ * which compares against the last emitted spec (reusing the composed `state`
45
+ * reference when the projection is value-unchanged) and skips emission when the
46
+ * rendered view is unchanged — preventing downstream providers from remounting
47
+ * the UI on every event.
48
+ *
49
+ * ### Context projection
50
+ *
51
+ * The machine's context is projected into the derived spec's `state` under the
52
+ * read-only `/context` subtree: `state: { ...meta.view.state, context: slice }`.
53
+ * The emitted spec is therefore **self-consistent** — its `state` honestly
54
+ * describes the store contents — so `repeat.statePath: "/context/tasks"`,
55
+ * `visible: { $state: "/context/…" }`, and `{ $state: "/context/…" }` props all
56
+ * resolve through the ordinary state grammar, and tools like `validateSpec`
57
+ * pass on derived views with no special-casing (validate the derived view, not
58
+ * the raw `meta.view`).
59
+ *
60
+ * The whole context is always projected; the authored `meta.view` object is
61
+ * never mutated.
62
+ *
63
+ * ### viewKey
64
+ *
65
+ * The derived spec carries `viewKey` — the meta record key (state node id) of
66
+ * the entry this derivation actually selected. Providers key their store
67
+ * lifecycle on it: changed → reseed, unchanged → refresh `/context` in place.
68
+ * It is taken from the selected entry rather than "the active state id"
69
+ * because for parallel machines the walked branch and the flat `getMeta()`
70
+ * fallback can select a view from a different region than the branch leaf.
71
+ *
72
+ * @param snapshot - Current XState machine snapshot.
73
+ * @returns Derived `PlaySpec`, or `null` if the current state has no view metadata.
74
+ */
75
+ export const deriveCurrentView = (snapshot) => {
76
+ // Follow the SAME single active branch the route derivation walks, so that
77
+ // for a parallel machine the rendered view belongs to the region the URL
78
+ // points at whenever that region declares one.
79
+ const meta = activeStateMeta(snapshot);
80
+ if (!meta) {
81
+ return null;
82
+ }
83
+ // A parallel machine may keep routes and views in DIFFERENT regions (one
84
+ // region owns navigation, another owns the shell). When the walked branch
85
+ // declares no view, scan the flat record of every active region — deepest
86
+ // entry wins — before concluding there is no view at all.
87
+ let resolved = resolveViewMeta(meta);
88
+ if (!resolved) {
89
+ const flatMeta = snapshot.getMeta();
90
+ if (flatMeta && typeof flatMeta === "object") {
91
+ resolved = resolveViewMeta(flatMeta);
92
+ }
93
+ }
94
+ if (!resolved) {
95
+ return null;
96
+ }
97
+ const { key: viewKey, view: viewMeta } = resolved;
98
+ if ("contextProps" in viewMeta) {
99
+ warnRemovedContextProps(viewMeta);
100
+ }
101
+ // Project the whole context under /context. composePlayState skips the
102
+ // projection (authored state wins, with a dev warning) when spec.state
103
+ // already declares a top-level "context" key.
104
+ const slice = snapshot.context !== null && typeof snapshot.context === "object"
105
+ ? snapshot.context
106
+ : undefined;
107
+ const authoredState = viewMeta.state !== undefined ? toAtomState(viewMeta.state) : undefined;
108
+ const state = composePlayState(authoredState, slice);
109
+ return {
110
+ ...viewMeta,
111
+ viewKey,
112
+ ...(state !== undefined && { state }),
113
+ };
114
+ };
115
+ //# sourceMappingURL=derive-current-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive-current-view.js","sourceRoot":"","sources":["../../src/view/derive-current-view.ts"],"names":[],"mappings":"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"}