@xmachines/play-xstate 1.0.0-beta.25 → 1.0.0-beta.27

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 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
@@ -38,8 +38,8 @@ const machine = setup({
38
38
  types: {
39
39
  context: {} as {
40
40
  isAuthenticated: boolean;
41
- routeParams: Record<string, string>;
42
- queryParams: Record<string, string>;
41
+ params: Record<string, string>;
42
+ query: Record<string, string>;
43
43
  },
44
44
  events: {} as
45
45
  | { type: "play.route"; to: string; params?: Record<string, string> }
@@ -50,7 +50,7 @@ const machine = setup({
50
50
  formatPlayRouteTransitions({
51
51
  id: "app",
52
52
  initial: "login",
53
- context: { isAuthenticated: false, routeParams: {}, queryParams: {} },
53
+ context: { isAuthenticated: false, params: {}, query: {} },
54
54
  states: {
55
55
  login: {
56
56
  id: "login",
@@ -178,8 +178,8 @@ Concrete actor implementing Play signal protocol:
178
178
  **Signal Properties:**
179
179
 
180
180
  - `state: Signal.State<AnyMachineSnapshot>` — Reactive snapshot of current state
181
- - `currentRoute: Signal.Computed<string | null>` — Derived URL from the current state's `meta.route` and `context.routeParams`. Returns `null` when no route metadata is present or a required route parameter is missing from context.
182
- - `currentView: Signal.State<ViewMetadata | null>` — Current view spec (updated on every state transition). The spec is automatically enriched with `context.routeParams` (URL params) and any `contextProps`-allowlisted context fields before being emitted (see [Prop Enrichment from Routing and Context](#prop-enrichment-from-routing-and-context)).
181
+ - `currentRoute: Signal.Computed<string | null>` — Derived URL from the current state's `meta.route` and `context.params`. Returns `null` when no route metadata is present or a required route parameter is missing from context.
182
+ - `currentView: Signal.State<ViewMetadata | null>` — Current view spec (updated on every state transition). The spec is automatically enriched with `context.params` (URL params) and any `contextProps`-allowlisted context fields before being emitted (see [Prop Enrichment from Routing and Context](#prop-enrichment-from-routing-and-context)).
183
183
 
184
184
  **Lifecycle ordering:**
185
185
 
@@ -205,7 +205,7 @@ Concrete actor implementing Play signal protocol:
205
205
  **View derivation and route-param enrichment:**
206
206
 
207
207
  - `currentView` emits a fresh `ViewMetadata` object on every transition so TC39 Signal equality checks always detect changes (including re-entries to the same state with different params).
208
- - `context.routeParams` and `contextProps`-allowlisted context fields are merged into spec element props — see [Prop Enrichment from Routing and Context](#prop-enrichment-from-routing-and-context).
208
+ - `context.params` and `contextProps`-allowlisted context fields are merged into spec element props — see [Prop Enrichment from Routing and Context](#prop-enrichment-from-routing-and-context).
209
209
 
210
210
  **Example:**
211
211
 
@@ -314,8 +314,8 @@ const machine = setup({
314
314
  types: {
315
315
  context: {} as {
316
316
  isAuthenticated: boolean;
317
- routeParams: Record<string, string>;
318
- queryParams: Record<string, string>;
317
+ params: Record<string, string>;
318
+ query: Record<string, string>;
319
319
  },
320
320
  events: {} as
321
321
  | { type: "play.route"; to: string; params?: Record<string, string> }
@@ -472,7 +472,7 @@ spec: {
472
472
  }
473
473
  // After play.route to /settings/profile:
474
474
  // Component receives: { section: "profile", username: "alice", theme: "dark" }
475
- // section → from routeParams (URL)
475
+ // section → from context.params (URL)
476
476
  // username → from contextProps (context)
477
477
  // theme → from spec (explicit value, untouched)
478
478
  ```
@@ -524,11 +524,11 @@ it to be filled in automatically.
524
524
 
525
525
  ### Merge priority (highest → lowest)
526
526
 
527
- | Source | When it applies | Wins over |
528
- | --------------------------------------- | -------------------------------- | ------------------------- |
529
- | Explicit non-`undefined` spec prop | Always | Everything |
530
- | URL route param (`context.routeParams`) | State has a `:param` URL segment | `contextProps` values |
531
- | `contextProps` field | Listed in `spec.contextProps` | Nothing (lowest priority) |
527
+ | Source | When it applies | Wins over |
528
+ | ---------------------------------- | -------------------------------- | ------------------------- |
529
+ | Explicit non-`undefined` spec prop | Always | Everything |
530
+ | URL route param (`context.params`) | State has a `:param` URL segment | `contextProps` values |
531
+ | `contextProps` field | Listed in `spec.contextProps` | Nothing (lowest priority) |
532
532
 
533
533
  ### URL route parameters
534
534
 
@@ -578,7 +578,7 @@ If both a route param and a `contextProps` field share the same key, the route p
578
578
 
579
579
  ```typescript
580
580
  // context.username = "alice" (logged-in user)
581
- // play.route to /profile/demo → routeParams.username = "demo"
581
+ // play.route to /profile/demo → context.params.username = "demo"
582
582
  // contextProps: ["username"], props: { username: undefined }
583
583
  // Component receives: { username: "demo" } ← route param wins
584
584
  ```
@@ -1,4 +1,4 @@
1
- import type { AnyStateMachine } from "xstate";
1
+ import { type AnyStateMachine } from "xstate";
2
2
  import type { PlayerConfig, PlayerFactory } from "./types.js";
3
3
  /**
4
4
  * Create a player factory from an XState machine
@@ -1 +1 @@
1
- {"version":3,"file":"define-player.d.ts","sourceRoot":"","sources":["../src/define-player.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAa,MAAM,QAAQ,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAA8B,MAAM,YAAY,CAAC;AAG1F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,eAAO,MAAM,YAAY,GAAI,QAAQ,SAAS,eAAe,EAC5D,QAAQ,YAAY,CAAC,QAAQ,CAAC,KAC5B,aAAa,CAAC,QAAQ,CAMxB,CAAC"}
1
+ {"version":3,"file":"define-player.d.ts","sourceRoot":"","sources":["../src/define-player.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,eAAe,EAA2C,MAAM,QAAQ,CAAC;AACpG,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAA8B,MAAM,YAAY,CAAC;AAG1F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,eAAO,MAAM,YAAY,GAAI,QAAQ,SAAS,eAAe,EAC5D,QAAQ,YAAY,CAAC,QAAQ,CAAC,KAC5B,aAAa,CAAC,QAAQ,CAsBxB,CAAC"}
@@ -1,4 +1,5 @@
1
- import { PlayerActor } from "./player-actor.js";
1
+ import { createActor } from "xstate";
2
+ import { PlayerActor, deriveCurrentRoute } from "./player-actor.js";
2
3
  /**
3
4
  * Create a player factory from an XState machine
4
5
  *
@@ -57,8 +58,15 @@ import { PlayerActor } from "./player-actor.js";
57
58
  */
58
59
  export const definePlayer = (config) => {
59
60
  const { machine, options } = config;
61
+ // Pre-compute the initial route once per machine definition.
62
+ // initialRoute is determined by the machine's initial state node and meta.route
63
+ // template — both fixed at definition time, independent of input or snapshot.
64
+ // Passed to every PlayerActor constructed from this factory, eliminating a
65
+ // redundant createActor() call per actor construction.
66
+ // The actor is never started — getSnapshot() is synchronous and free.
67
+ const cachedInitialRoute = deriveCurrentRoute(createActor(machine).getSnapshot());
60
68
  return (input, restore) => {
61
- return new PlayerActor(machine, options ?? {}, input, restore?.snapshot);
69
+ return new PlayerActor(machine, options ?? {}, input, restore?.snapshot, cachedInitialRoute);
62
70
  };
63
71
  };
64
72
  //# sourceMappingURL=define-player.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"define-player.js","sourceRoot":"","sources":["../src/define-player.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAC3B,MAA8B,EACJ,EAAE;IAC5B,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAEpC,OAAO,CAAC,KAA2B,EAAE,OAA8C,EAAE,EAAE;QACtF,OAAO,IAAI,WAAW,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC1E,CAAC,CAAC;AACH,CAAC,CAAC"}
1
+ {"version":3,"file":"define-player.js","sourceRoot":"","sources":["../src/define-player.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiE,MAAM,QAAQ,CAAC;AAEpG,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAC3B,MAA8B,EACJ,EAAE;IAC5B,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAEpC,6DAA6D;IAC7D,gFAAgF;IAChF,8EAA8E;IAC9E,2EAA2E;IAC3E,uDAAuD;IACvD,sEAAsE;IACtE,MAAM,kBAAkB,GAAG,kBAAkB,CAC5C,WAAW,CAAC,OAAO,CAAC,CAAC,WAAW,EAAwB,CACxD,CAAC;IAEF,OAAO,CAAC,KAA2B,EAAE,OAA8C,EAAE,EAAE;QACtF,OAAO,IAAI,WAAW,CACrB,OAAO,EACP,OAAO,IAAI,EAAE,EACb,KAAK,EACL,OAAO,EAAE,QAAQ,EACjB,kBAAkB,CAClB,CAAC;IACH,CAAC,CAAC;AACH,CAAC,CAAC"}
package/dist/errors.d.ts CHANGED
@@ -59,6 +59,78 @@ export declare class MissingRouteParamError extends PlayError {
59
59
  * }
60
60
  * ```
61
61
  */
62
+ /**
63
+ * Thrown by `buildRouteUrl()` when the context has a `params` field (indicating a
64
+ * routing-aware machine context) but no `query` field.
65
+ *
66
+ * The `params` field signals the context was set up for routing (either manually or via
67
+ * `formatPlayRouteTransitions`). A routing-aware context without `query` will silently
68
+ * drop any query parameters carried by `play.route` events, producing incorrect URLs.
69
+ *
70
+ * **Fix:** Ensure the machine context type includes `query: Record<string, string>`
71
+ * and that the context initializer sets `query: {}` (or a populated value).
72
+ * If using `formatPlayRouteTransitions`, the field is assigned automatically on each
73
+ * `play.route` event — but the machine's initial context must still declare it.
74
+ *
75
+ * **Error code:** `PLAY_MISSING_QUERY_CONTEXT`
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * import { MissingQueryContextError } from "@xmachines/play-xstate/errors";
80
+ *
81
+ * try {
82
+ * // params present but no query field — throws
83
+ * buildRouteUrl("/profile/:userId", { params: { userId: "42" } });
84
+ * } catch (err) {
85
+ * if (err instanceof MissingQueryContextError) {
86
+ * console.error("Machine context missing query field");
87
+ * }
88
+ * }
89
+ *
90
+ * // Correct — both params and query declared
91
+ * buildRouteUrl("/profile/:userId", { params: { userId: "42" }, query: {} });
92
+ * ```
93
+ */
94
+ export declare class MissingQueryContextError extends PlayError {
95
+ constructor();
96
+ }
97
+ /**
98
+ * Thrown by `formatPlayRouteTransitions()` when a state node declares `meta.route`
99
+ * but omits an explicit `id`.
100
+ *
101
+ * Without `id`, the function cannot generate a `play.route` guard that targets the
102
+ * state — the guard compares `event.to` against `#<id>`, so a missing `id` means
103
+ * the state is silently unreachable via routing. This error surfaces the
104
+ * misconfiguration at machine-definition time rather than silently skipping the state.
105
+ *
106
+ * **Error code:** `PLAY_MISSING_STATE_ID`
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * // Bad — meta.route without id:
111
+ * states: {
112
+ * profile: {
113
+ * meta: { route: "/profile/:username" },
114
+ * // ← missing id: "profile"
115
+ * }
116
+ * }
117
+ *
118
+ * // Good:
119
+ * states: {
120
+ * profile: {
121
+ * id: "profile",
122
+ * meta: { route: "/profile/:username" },
123
+ * }
124
+ * }
125
+ * ```
126
+ */
127
+ export declare class MissingStateIdError extends PlayError {
128
+ /** The state key (position in the states tree) that is missing an explicit `id`. */
129
+ readonly stateKey: string;
130
+ /** The route template declared on the state (e.g. `"/profile/:username"`). */
131
+ readonly route: string;
132
+ constructor(stateKey: string, route: string);
133
+ }
62
134
  export declare class InvalidEventError extends PlayError {
63
135
  readonly detail: unknown;
64
136
  constructor(detail: unknown);
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,0FAA0F;IAC1F,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAU3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;IAC/C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;gBAEb,MAAM,EAAE,OAAO;CAU3B"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,0FAA0F;IAC1F,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAU3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAAa,wBAAyB,SAAQ,SAAS;;CAWtD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAW3C;AAED,qBAAa,iBAAkB,SAAQ,SAAS;IAC/C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;gBAEb,MAAM,EAAE,OAAO;CAU3B"}
package/dist/errors.js CHANGED
@@ -64,6 +64,89 @@ export class MissingRouteParamError extends PlayError {
64
64
  * }
65
65
  * ```
66
66
  */
67
+ /**
68
+ * Thrown by `buildRouteUrl()` when the context has a `params` field (indicating a
69
+ * routing-aware machine context) but no `query` field.
70
+ *
71
+ * The `params` field signals the context was set up for routing (either manually or via
72
+ * `formatPlayRouteTransitions`). A routing-aware context without `query` will silently
73
+ * drop any query parameters carried by `play.route` events, producing incorrect URLs.
74
+ *
75
+ * **Fix:** Ensure the machine context type includes `query: Record<string, string>`
76
+ * and that the context initializer sets `query: {}` (or a populated value).
77
+ * If using `formatPlayRouteTransitions`, the field is assigned automatically on each
78
+ * `play.route` event — but the machine's initial context must still declare it.
79
+ *
80
+ * **Error code:** `PLAY_MISSING_QUERY_CONTEXT`
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * import { MissingQueryContextError } from "@xmachines/play-xstate/errors";
85
+ *
86
+ * try {
87
+ * // params present but no query field — throws
88
+ * buildRouteUrl("/profile/:userId", { params: { userId: "42" } });
89
+ * } catch (err) {
90
+ * if (err instanceof MissingQueryContextError) {
91
+ * console.error("Machine context missing query field");
92
+ * }
93
+ * }
94
+ *
95
+ * // Correct — both params and query declared
96
+ * buildRouteUrl("/profile/:userId", { params: { userId: "42" }, query: {} });
97
+ * ```
98
+ */
99
+ export class MissingQueryContextError extends PlayError {
100
+ constructor() {
101
+ super("buildRouteUrl", "PLAY_MISSING_QUERY_CONTEXT", "buildRouteUrl() received a context without a 'query' field. " +
102
+ "Declare 'query: Record<string, string>' in the machine context type and " +
103
+ "initialise it to {} so query parameters from play.route events are not silently dropped.");
104
+ this.name = "MissingQueryContextError";
105
+ }
106
+ }
107
+ /**
108
+ * Thrown by `formatPlayRouteTransitions()` when a state node declares `meta.route`
109
+ * but omits an explicit `id`.
110
+ *
111
+ * Without `id`, the function cannot generate a `play.route` guard that targets the
112
+ * state — the guard compares `event.to` against `#<id>`, so a missing `id` means
113
+ * the state is silently unreachable via routing. This error surfaces the
114
+ * misconfiguration at machine-definition time rather than silently skipping the state.
115
+ *
116
+ * **Error code:** `PLAY_MISSING_STATE_ID`
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * // Bad — meta.route without id:
121
+ * states: {
122
+ * profile: {
123
+ * meta: { route: "/profile/:username" },
124
+ * // ← missing id: "profile"
125
+ * }
126
+ * }
127
+ *
128
+ * // Good:
129
+ * states: {
130
+ * profile: {
131
+ * id: "profile",
132
+ * meta: { route: "/profile/:username" },
133
+ * }
134
+ * }
135
+ * ```
136
+ */
137
+ export class MissingStateIdError extends PlayError {
138
+ /** The state key (position in the states tree) that is missing an explicit `id`. */
139
+ stateKey;
140
+ /** The route template declared on the state (e.g. `"/profile/:username"`). */
141
+ route;
142
+ constructor(stateKey, route) {
143
+ super("formatPlayRouteTransitions", "PLAY_MISSING_STATE_ID", `State "${stateKey}" declares meta.route "${route}" but has no explicit id. ` +
144
+ `Add id: "${stateKey}" to the state config so play.route events can target it.`);
145
+ this.name = "MissingStateIdError";
146
+ this.stateKey = stateKey;
147
+ this.route = route;
148
+ }
149
+ }
67
150
  export class InvalidEventError extends PlayError {
68
151
  detail;
69
152
  constructor(detail) {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,sBAAuB,SAAQ,SAAS;IACpD,0EAA0E;IACjE,KAAK,CAAS;IAEvB,0FAA0F;IACjF,QAAQ,CAAS;IAE1B,YAAY,KAAa,EAAE,QAAgB;QAC1C,KAAK,CACJ,eAAe,EACf,0BAA0B,EAC1B,oBAAoB,KAAK,8BAA8B,QAAQ,iCAAiC,CAChG,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC1B,CAAC;CACD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,iBAAkB,SAAQ,SAAS;IACtC,MAAM,CAAU;IAEzB,YAAY,MAAe;QAC1B,KAAK,CACJ,aAAa,EACb,oBAAoB,EACpB,iDAAiD,CACjD,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,6EAA6E;QAC7E,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5E,CAAC;CACD"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,sBAAuB,SAAQ,SAAS;IACpD,0EAA0E;IACjE,KAAK,CAAS;IAEvB,0FAA0F;IACjF,QAAQ,CAAS;IAE1B,YAAY,KAAa,EAAE,QAAgB;QAC1C,KAAK,CACJ,eAAe,EACf,0BAA0B,EAC1B,oBAAoB,KAAK,8BAA8B,QAAQ,iCAAiC,CAChG,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC1B,CAAC;CACD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,OAAO,wBAAyB,SAAQ,SAAS;IACtD;QACC,KAAK,CACJ,eAAe,EACf,4BAA4B,EAC5B,8DAA8D;YAC7D,0EAA0E;YAC1E,0FAA0F,CAC3F,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACxC,CAAC;CACD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,OAAO,mBAAoB,SAAQ,SAAS;IACjD,oFAAoF;IAC3E,QAAQ,CAAS;IAC1B,8EAA8E;IACrE,KAAK,CAAS;IAEvB,YAAY,QAAgB,EAAE,KAAa;QAC1C,KAAK,CACJ,4BAA4B,EAC5B,uBAAuB,EACvB,UAAU,QAAQ,0BAA0B,KAAK,4BAA4B;YAC5E,YAAY,QAAQ,2DAA2D,CAChF,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;CACD;AAED,MAAM,OAAO,iBAAkB,SAAQ,SAAS;IACtC,MAAM,CAAU;IAEzB,YAAY,MAAe;QAC1B,KAAK,CACJ,aAAa,EACb,oBAAoB,EACpB,iDAAiD,CACjD,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,6EAA6E;QAC7E,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5E,CAAC;CACD"}
@@ -2,6 +2,27 @@ import { type AnyStateMachine, type Actor, type AnyActorLogic, type AnyMachineSn
2
2
  import { AbstractActor, type Routable, type Viewable, type ViewMetadata } from "@xmachines/play-actor";
3
3
  import { Signal } from "@xmachines/play-signals";
4
4
  import type { PlayerOptions } from "./types.js";
5
+ /**
6
+ * Derive the actor's current URL from state metadata and context.
7
+ *
8
+ * Resolves the route template from the current state's `meta.route` and substitutes
9
+ * any `:param` placeholders from `context.params` (preferred) or flat `context`.
10
+ *
11
+ * Returns `null` — rather than throwing — when:
12
+ * - The snapshot has no route metadata (non-routable state)
13
+ * - A required route parameter is absent from context (`MissingRouteParamError`)
14
+ *
15
+ * The `null` return on missing params is intentional: it keeps the computed signal
16
+ * stable during transient states (e.g. mid-transition before context is fully updated,
17
+ * or after logout when `context.username` is `null` but the router bridge has not yet
18
+ * synced to the new state). The router bridge and URL bar are updated on the next
19
+ * stable snapshot once context is complete.
20
+ *
21
+ * @param snapshot - Current XState machine snapshot.
22
+ * @returns Resolved URL string, or `null` if the route cannot be resolved.
23
+ */
24
+ /** @internal — exported for `definePlayer` pre-computation only. */
25
+ export declare const deriveCurrentRoute: (snapshot: AnyMachineSnapshot) => string | null;
5
26
  /**
6
27
  * Concrete XState actor implementing Play Architecture signal protocol
7
28
  *
@@ -110,8 +131,15 @@ export declare class PlayerActor<TMachine extends AnyStateMachine> extends Abstr
110
131
  * (non-initial URL → router wins) from a restore (initial URL + actor at a
111
132
  * different restored route → actor wins).
112
133
  *
113
- * This value is intentionally derived from a fresh, non-restored machine snapshot,
114
- * never from the runtime actor snapshot passed via `restore?.snapshot`.
134
+ * Determined by the machine's initial state node and its `meta.route` template —
135
+ * both are fixed at machine definition time. `definePlayer` pre-computes this value
136
+ * once from the machine definition and passes it to every actor it constructs, so no
137
+ * extra `createActor()` call is needed per construction.
138
+ *
139
+ * When `PlayerActor` is constructed directly (without `definePlayer`), the value is
140
+ * derived from `xstateActor.getSnapshot()` (normal path) or a transient
141
+ * `createActor(machine, { input })` call (restore path, to avoid using the
142
+ * snapshot's state instead of the machine's default).
115
143
  */
116
144
  readonly initialRoute: string | null;
117
145
  /**
@@ -123,7 +151,7 @@ export declare class PlayerActor<TMachine extends AnyStateMachine> extends Abstr
123
151
  * detect changes.
124
152
  *
125
153
  * The emitted `ViewMetadata` has its spec element `props` enriched with
126
- * `context.routeParams` before emission — URL path parameters (e.g. `:section?`)
154
+ * `context.params` before emission — URL path parameters (e.g. `:section?`)
127
155
  * flow into component props automatically. See `mergeRouteParamsIntoProps` for the
128
156
  * merge priority rules.
129
157
  *
@@ -139,7 +167,17 @@ export declare class PlayerActor<TMachine extends AnyStateMachine> extends Abstr
139
167
  * ```
140
168
  */
141
169
  currentView: Signal.State<ViewMetadata | null>;
142
- constructor(machine: TMachine, options: PlayerOptions<TMachine>, input?: InputFrom<TMachine>, snapshot?: SnapshotFrom<TMachine>);
170
+ constructor(machine: TMachine, options: PlayerOptions<TMachine>, input?: InputFrom<TMachine>, snapshot?: SnapshotFrom<TMachine>,
171
+ /**
172
+ * Pre-computed initial route from `definePlayer`.
173
+ * `initialRoute` is determined by the machine's initial state node and its
174
+ * `meta.route` template — both fixed at machine definition time regardless
175
+ * of `input` or `snapshot`. When provided, used directly on every construction,
176
+ * eliminating a redundant `createActor()` call per actor instance.
177
+ *
178
+ * @internal — set by `definePlayer`; callers should not pass this directly.
179
+ */
180
+ _cachedInitialRoute?: string | null);
143
181
  /**
144
182
  * Start the actor
145
183
  *
@@ -1 +1 @@
1
- {"version":3,"file":"player-actor.d.ts","sourceRoot":"","sources":["../src/player-actor.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,eAAe,EACpB,KAAK,KAAK,EACV,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAEd,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,MAAM,QAAQ,CAAC;AAChB,OAAO,EACN,aAAa,EACb,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAIjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA2OhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,qBAAa,WAAW,CAAC,QAAQ,SAAS,eAAe,CACxD,SAAQ,aAAa,CAAC,aAAa,EAAE,cAAc,CAAC,QAAQ,CAAC,CAC7D,YAAW,QAAQ,EAAE,QAAQ;IAE7B,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,aAAa,CAAyC;IAC9D,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,WAAW,CAAoC;IAGhD,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAE/C;;;;;;;;;;;;;;;;;OAiBG;IACI,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAEpD;;;;;;;;;;OAUG;IACH,SAAgB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5C;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACI,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;gBAGrD,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAC,EAChC,KAAK,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,EAC3B,QAAQ,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC;IAyElC;;;;OAIG;IACM,KAAK,IAAI,IAAI;IAWtB;;OAEG;IACM,IAAI,IAAI,IAAI;IAYrB;;;;;;;;;;;;;;;;;;;OAmBG;IACM,IAAI,CAAC,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,IAAI;IAoBpD;;OAEG;IACM,WAAW,IAAI,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,CAAC;IAIlE;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAc7B;;;;OAIG;IACH,OAAO,IAAI,IAAI;CAGf"}
1
+ {"version":3,"file":"player-actor.d.ts","sourceRoot":"","sources":["../src/player-actor.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,eAAe,EACpB,KAAK,KAAK,EACV,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAEd,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,MAAM,QAAQ,CAAC;AAChB,OAAO,EACN,aAAa,EACb,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAIjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAoBhD;;;;;;;;;;;;;;;;;;GAkBG;AACH,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,GAAI,UAAU,kBAAkB,KAAG,MAAM,GAAG,IAyC1E,CAAC;AA0KF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,qBAAa,WAAW,CAAC,QAAQ,SAAS,eAAe,CACxD,SAAQ,aAAa,CAAC,aAAa,EAAE,cAAc,CAAC,QAAQ,CAAC,CAC7D,YAAW,QAAQ,EAAE,QAAQ;IAE7B,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,aAAa,CAAyC;IAC9D,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,WAAW,CAAoC;IAGhD,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAE/C;;;;;;;;;;;;;;;;;OAiBG;IACI,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAEpD;;;;;;;;;;;;;;;;;OAiBG;IACH,SAAgB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5C;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACI,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;gBAGrD,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAC,EAChC,KAAK,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,EAC3B,QAAQ,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC;IACjC;;;;;;;;OAQG;IACH,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI;IA6FpC;;;;OAIG;IACM,KAAK,IAAI,IAAI;IAWtB;;OAEG;IACM,IAAI,IAAI,IAAI;IAYrB;;;;;;;;;;;;;;;;;;;OAmBG;IACM,IAAI,CAAC,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,IAAI;IAoBpD;;OAEG;IACM,WAAW,IAAI,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,CAAC;IAIlE;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAc7B;;;;OAIG;IACH,OAAO,IAAI,IAAI;CAGf"}
@@ -20,7 +20,7 @@ const isActiveSnapshot = (snapshot) => {
20
20
  * Derive the actor's current URL from state metadata and context.
21
21
  *
22
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`.
23
+ * any `:param` placeholders from `context.params` (preferred) or flat `context`.
24
24
  *
25
25
  * Returns `null` — rather than throwing — when:
26
26
  * - The snapshot has no route metadata (non-routable state)
@@ -35,7 +35,8 @@ const isActiveSnapshot = (snapshot) => {
35
35
  * @param snapshot - Current XState machine snapshot.
36
36
  * @returns Resolved URL string, or `null` if the route cannot be resolved.
37
37
  */
38
- const deriveCurrentRoute = (snapshot) => {
38
+ /** @internal exported for `definePlayer` pre-computation only. */
39
+ export const deriveCurrentRoute = (snapshot) => {
39
40
  if (!snapshot || typeof snapshot.getMeta !== "function") {
40
41
  return null;
41
42
  }
@@ -48,16 +49,30 @@ const deriveCurrentRoute = (snapshot) => {
48
49
  return null;
49
50
  }
50
51
  try {
51
- return buildRouteUrl(routeTemplate, snapshot.context ?? {});
52
+ return buildRouteUrl(routeTemplate, (snapshot.context ?? {}));
52
53
  }
53
54
  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.
55
+ // MissingRouteParamError: transient and self-resolving a required `:param` is absent
56
+ // from context during a mid-transition state (e.g. actor in profile state before the
57
+ // first play.route event populates context.params, or after logout before the bridge
58
+ // redirects). Returning null is correct:
59
+ // - Router bridges handle null by skipping navigation (syncRouterFromActor line 242)
60
+ // - The signal recomputes automatically on the next snapshot when params are populated
61
+ // - Throwing would propagate into signal watchers as an unhandled exception for a
62
+ // state that resolves itself — worse than a one-tick null.
63
+ // Do NOT convert this to a throw. See MissingQueryContextError for the structural-error
64
+ // case where throwing is appropriate (non-self-resolving programmer error).
58
65
  if (error instanceof MissingRouteParamError) {
59
66
  return null;
60
67
  }
68
+ // MissingQueryContextError: structural programmer error — the machine context
69
+ // declares `params` but no `query` field. This cannot self-resolve on the next
70
+ // snapshot; re-throw so it surfaces immediately. Frameworks with error boundaries
71
+ // (React, Svelte, SvelteKit) will catch it; signal watchers will see it as an
72
+ // unhandled exception. Fix: add `query: Record<string, string>` to the machine's
73
+ // context type and initializer.
74
+ //
75
+ // All other unexpected errors also re-throw unchanged.
61
76
  throw error;
62
77
  }
63
78
  };
@@ -78,14 +93,14 @@ const resolveViewMeta = (meta) => {
78
93
  *
79
94
  * Priority rule (highest → lowest):
80
95
  * 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.
96
+ * 2. URL route params (`context.params`) — fills `undefined` slots from the URL path.
82
97
  * 3. Allowlisted context fields (`contextProps`) — fills remaining `undefined` slots from
83
98
  * the machine context (e.g. `context.username` on a state with no URL username param).
84
99
  *
85
100
  * The `contextProps` allowlist is the key safety mechanism: only fields the machine
86
101
  * explicitly opts in to are ever exposed to components.
87
102
  *
88
- * @param routeParams - Extracted URL path parameters from `context.routeParams`.
103
+ * @param urlParams - Extracted URL path parameters from `context.params`.
89
104
  * @param contextValues - Object containing only the allowlisted context fields.
90
105
  * @param existingProps - The element's props as declared in the machine spec.
91
106
  * @returns Merged props object with URL params and allowlisted context values filling
@@ -94,16 +109,16 @@ const resolveViewMeta = (meta) => {
94
109
  * @example
95
110
  * ```ts
96
111
  * // spec: { section: undefined, username: undefined, title: "Dashboard" }
97
- * // routeParams: { section: "profile" } ← from URL /:section
112
+ * // urlParams: { section: "profile" } ← from URL /:section
98
113
  * // contextValues: { username: "alice" } ← from contextProps: ["username"]
99
114
  * // result: { section: "profile", username: "alice", title: "Dashboard" }
100
115
  * ```
101
116
  */
102
- function mergeRouteParamsIntoProps(routeParams, contextValues, existingProps) {
117
+ function mergeRouteParamsIntoProps(urlParams, contextValues, existingProps) {
103
118
  // Layer 3 (lowest priority): allowlisted context values
104
119
  const merged = { ...contextValues };
105
- // Layer 2: route params override context values for the same key
106
- for (const [k, v] of Object.entries(routeParams)) {
120
+ // Layer 2: URL params override context values for the same key
121
+ for (const [k, v] of Object.entries(urlParams)) {
107
122
  merged[k] = v;
108
123
  }
109
124
  // Layer 1 (highest priority): explicit non-undefined spec props always win
@@ -125,7 +140,7 @@ function mergeRouteParamsIntoProps(routeParams, contextValues, existingProps) {
125
140
  *
126
141
  * Each spec element's `props` are enriched before the view is emitted using two sources:
127
142
  *
128
- * **1. URL route params** (`context.routeParams`, populated by `formatPlayRouteTransitions`):
143
+ * **1. URL route params** (`context.params`, populated by `formatPlayRouteTransitions`):
129
144
  * Makes `:section?` / `:username` URL path parameters automatically available as component
130
145
  * props without manual wiring.
131
146
  *
@@ -136,7 +151,7 @@ function mergeRouteParamsIntoProps(routeParams, contextValues, existingProps) {
136
151
  *
137
152
  * Merge priority (see `mergeRouteParamsIntoProps`):
138
153
  * 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.
154
+ * 2. URL route param (`context.params`) — fills `undefined` slots from the URL path.
140
155
  * 3. Allowlisted context field (`contextProps`) — fills remaining `undefined` slots.
141
156
  *
142
157
  * @param snapshot - Current XState machine snapshot.
@@ -145,12 +160,12 @@ function mergeRouteParamsIntoProps(routeParams, contextValues, existingProps) {
145
160
  * @example
146
161
  * ```ts
147
162
  * // spec: { contextProps: ["username"], elements: { root: { props: { username: undefined } } } }
148
- * // context.username = "alice", routeParams = {}
163
+ * // context.username = "alice", context.params = {}
149
164
  * // Derived props: { username: "alice" }
150
165
  *
151
166
  * // spec: { contextProps: ["username"], elements: { root: { props: { username: undefined } } } }
152
- * // context.username = "alice", routeParams = { username: "demo" }
153
- * // Derived props: { username: "demo" } ← routeParams wins
167
+ * // context.username = "alice", context.params = { username: "demo" }
168
+ * // Derived props: { username: "demo" } ← URL param wins
154
169
  * ```
155
170
  */
156
171
  const deriveCurrentView = (snapshot) => {
@@ -165,14 +180,14 @@ const deriveCurrentView = (snapshot) => {
165
180
  if (!viewMeta) {
166
181
  return null;
167
182
  }
168
- // Extract routeParams from context (set by formatPlayRouteTransitions on play.route events)
183
+ // Extract params from context (set by formatPlayRouteTransitions on play.route events)
169
184
  const context = snapshot.context !== null &&
170
185
  snapshot.context !== undefined &&
171
186
  typeof snapshot.context === "object"
172
187
  ? snapshot.context
173
188
  : null;
174
- const routeParams = context !== null && typeof context.routeParams === "object" && context.routeParams !== null
175
- ? context.routeParams
189
+ const urlParams = context !== null && typeof context.params === "object" && context.params !== null
190
+ ? context.params
176
191
  : {};
177
192
  // Extract the allowlisted context fields declared in spec.contextProps.
178
193
  // Only fields explicitly named in the allowlist are ever exposed to components.
@@ -194,7 +209,7 @@ const deriveCurrentView = (snapshot) => {
194
209
  key,
195
210
  {
196
211
  ...element,
197
- props: mergeRouteParamsIntoProps(routeParams, contextValues, existingProps),
212
+ props: mergeRouteParamsIntoProps(urlParams, contextValues, existingProps),
198
213
  },
199
214
  ];
200
215
  }));
@@ -317,8 +332,15 @@ export class PlayerActor extends AbstractActor {
317
332
  * (non-initial URL → router wins) from a restore (initial URL + actor at a
318
333
  * different restored route → actor wins).
319
334
  *
320
- * This value is intentionally derived from a fresh, non-restored machine snapshot,
321
- * never from the runtime actor snapshot passed via `restore?.snapshot`.
335
+ * Determined by the machine's initial state node and its `meta.route` template —
336
+ * both are fixed at machine definition time. `definePlayer` pre-computes this value
337
+ * once from the machine definition and passes it to every actor it constructs, so no
338
+ * extra `createActor()` call is needed per construction.
339
+ *
340
+ * When `PlayerActor` is constructed directly (without `definePlayer`), the value is
341
+ * derived from `xstateActor.getSnapshot()` (normal path) or a transient
342
+ * `createActor(machine, { input })` call (restore path, to avoid using the
343
+ * snapshot's state instead of the machine's default).
322
344
  */
323
345
  initialRoute;
324
346
  /**
@@ -330,7 +352,7 @@ export class PlayerActor extends AbstractActor {
330
352
  * detect changes.
331
353
  *
332
354
  * The emitted `ViewMetadata` has its spec element `props` enriched with
333
- * `context.routeParams` before emission — URL path parameters (e.g. `:section?`)
355
+ * `context.params` before emission — URL path parameters (e.g. `:section?`)
334
356
  * flow into component props automatically. See `mergeRouteParamsIntoProps` for the
335
357
  * merge priority rules.
336
358
  *
@@ -346,7 +368,17 @@ export class PlayerActor extends AbstractActor {
346
368
  * ```
347
369
  */
348
370
  currentView;
349
- constructor(machine, options, input, snapshot) {
371
+ constructor(machine, options, input, snapshot,
372
+ /**
373
+ * Pre-computed initial route from `definePlayer`.
374
+ * `initialRoute` is determined by the machine's initial state node and its
375
+ * `meta.route` template — both fixed at machine definition time regardless
376
+ * of `input` or `snapshot`. When provided, used directly on every construction,
377
+ * eliminating a redundant `createActor()` call per actor instance.
378
+ *
379
+ * @internal — set by `definePlayer`; callers should not pass this directly.
380
+ */
381
+ _cachedInitialRoute) {
350
382
  // Defensive check: Validate machine before passing to createActor
351
383
  if (!machine || typeof machine !== "object") {
352
384
  throw new Error("PlayerActor requires a valid XState machine");
@@ -360,15 +392,38 @@ export class PlayerActor extends AbstractActor {
360
392
  // Track XState typing improvements: https://github.com/statelyai/xstate/issues
361
393
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
362
394
  const xstateActor = createActor(machine, { input, snapshot });
363
- // Defensive check: Validate actor was created successfully
364
- if (!xstateActor) {
365
- throw new Error("Failed to create XState actor - machine may be null or invalid");
366
- }
367
395
  // Call AbstractActor constructor with actor logic
368
396
  super(xstateActor.logic);
369
- // Derive the machine's initial route from a fresh (non-restored) snapshot.
370
- // createActor(machine) without options is cheap and synchronous — never started.
371
- this.initialRoute = deriveCurrentRoute(createActor(machine).getSnapshot());
397
+ // Derive the machine's initial route for restore-vs-deeplink detection.
398
+ //
399
+ // Fast path: when `definePlayer` pre-computed the no-input initial route and no
400
+ // per-call `input` was provided, use the cached value directly — zero extra
401
+ // createActor() calls per construction.
402
+ //
403
+ // Slow path A (no cache / input provided): read from xstateActor.getSnapshot()
404
+ // before start() — already constructed with caller's input, free.
405
+ //
406
+ // Slow path B (restore + input): spin a minimal actor with only input (no
407
+ // snapshot) to get the machine's default initial state rather than the restored
408
+ // one, so bridges can distinguish deep-link from restore.
409
+ if (_cachedInitialRoute !== undefined) {
410
+ // Fast path — pre-computed by definePlayer from machine's initial state.
411
+ // initialRoute is determined solely by the machine's initial state node and
412
+ // its meta.route template, which are fixed at machine definition time.
413
+ // input/snapshot do not affect which state is initial.
414
+ this.initialRoute = _cachedInitialRoute;
415
+ }
416
+ else if (snapshot) {
417
+ // Restore path without cache — spin a minimal actor to get the default initial
418
+ // state (not the snapshot state) so bridges can distinguish deeplink from restore.
419
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
420
+ const initialActor = createActor(machine, { input });
421
+ this.initialRoute = deriveCurrentRoute(initialActor.getSnapshot());
422
+ }
423
+ else {
424
+ // Normal path without cache — xstateActor snapshot is already the initial state.
425
+ this.initialRoute = deriveCurrentRoute(xstateActor.getSnapshot());
426
+ }
372
427
  this._xstateActor = xstateActor;
373
428
  this._options = options || {};
374
429
  // Initialize state signal manager
@@ -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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"}
@@ -2,31 +2,36 @@ import type { RouteContext } from "./types.js";
2
2
  /**
3
3
  * Build a full URL from a route template and the actor's context.
4
4
  *
5
- * Substitutes `:param` and `:param?` placeholders from `context.routeParams`
6
- * or flat `context` values, then appends query params and hash fragments.
5
+ * Substitutes `:param` and `:param?` placeholders from `context.params`,
6
+ * then appends query params and hash fragments.
7
7
  *
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
8
+ * All parameter values must be in `context.params`. Flat context fields are not
9
+ * inspectedthis is intentional so misspelled placeholders produce a compile-time
10
+ * error rather than silently resolving to `undefined`.
12
11
  *
13
12
  * @param routeTemplate - Route path template, e.g. `"/profile/:userId"` or
14
13
  * `"/settings/:section?"`.
15
- * @param context - Actor context object containing parameter values, optional
16
- * `query` record, optional `hash` string, and optional `basePath` prefix.
14
+ * @param context - Actor context object. Route parameters must be in `context.params`;
15
+ * flat context fields are not inspected. **Must include a `query` field** (use `query: {}`
16
+ * when no query parameters are needed). Omitting `query` when `params` is present throws
17
+ * `MissingQueryContextError` to prevent silent query-string loss.
17
18
  * @returns The fully resolved URL string.
18
19
  *
19
20
  * @throws {MissingRouteParamError} When a **required** `:param` placeholder has no
20
21
  * matching value in context. Optional parameters (`:param?`) are silently omitted
21
22
  * when missing. Import the class from `@xmachines/play-xstate/errors`.
23
+ * @throws {MissingQueryContextError} When the context has a `params` field but no
24
+ * `query` field. This indicates a routing-aware machine context that was not set up
25
+ * to receive query parameters, which would silently drop query params from
26
+ * `play.route` events. Import the class from `@xmachines/play-xstate/errors`.
22
27
  *
23
28
  * @example
24
29
  * ```typescript
25
- * buildRouteUrl("/user/:id", { id: "123", query: { tab: "profile" }, hash: "top" });
30
+ * buildRouteUrl("/user/:id", { params: { id: "123" }, query: { tab: "profile" }, hash: "top" });
26
31
  * // → "/user/123?tab=profile#top"
27
32
  *
28
- * buildRouteUrl("/settings/:section?", {});
29
- * // → "/settings" (optional param omitted)
33
+ * buildRouteUrl("/settings/:section?", { params: {}, query: {} });
34
+ * // → "/settings" (optional param omitted, no query string)
30
35
  * ```
31
36
  */
32
37
  export declare const buildRouteUrl: (routeTemplate: string, context?: RouteContext) => string;
@@ -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;AAI/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,aAAa,GAAI,eAAe,MAAM,EAAE,UAAS,YAAiB,KAAG,MA6BjF,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,aAAa,GACzB,eAAe,MAAM,EACrB,UAAS,YAA4B,KACnC,MAsCF,CAAC"}
@@ -1,36 +1,49 @@
1
1
  import { isAbsoluteRoute } from "./derive-route.js";
2
- import { MissingRouteParamError } from "../errors.js";
2
+ import { MissingRouteParamError, MissingQueryContextError } from "../errors.js";
3
3
  /**
4
4
  * Build a full URL from a route template and the actor's context.
5
5
  *
6
- * Substitutes `:param` and `:param?` placeholders from `context.routeParams`
7
- * or flat `context` values, then appends query params and hash fragments.
6
+ * Substitutes `:param` and `:param?` placeholders from `context.params`,
7
+ * then appends query params and hash fragments.
8
8
  *
9
- * Parameter lookup order for each placeholder:
10
- * 1. `context.routeParams[param]` preferred when the state machine stores
11
- * route parameters in a dedicated sub-object
12
- * 2. `context[param]` — flat context fallback
9
+ * All parameter values must be in `context.params`. Flat context fields are not
10
+ * inspectedthis is intentional so misspelled placeholders produce a compile-time
11
+ * error rather than silently resolving to `undefined`.
13
12
  *
14
13
  * @param routeTemplate - Route path template, e.g. `"/profile/:userId"` or
15
14
  * `"/settings/:section?"`.
16
- * @param context - Actor context object containing parameter values, optional
17
- * `query` record, optional `hash` string, and optional `basePath` prefix.
15
+ * @param context - Actor context object. Route parameters must be in `context.params`;
16
+ * flat context fields are not inspected. **Must include a `query` field** (use `query: {}`
17
+ * when no query parameters are needed). Omitting `query` when `params` is present throws
18
+ * `MissingQueryContextError` to prevent silent query-string loss.
18
19
  * @returns The fully resolved URL string.
19
20
  *
20
21
  * @throws {MissingRouteParamError} When a **required** `:param` placeholder has no
21
22
  * matching value in context. Optional parameters (`:param?`) are silently omitted
22
23
  * when missing. Import the class from `@xmachines/play-xstate/errors`.
24
+ * @throws {MissingQueryContextError} When the context has a `params` field but no
25
+ * `query` field. This indicates a routing-aware machine context that was not set up
26
+ * to receive query parameters, which would silently drop query params from
27
+ * `play.route` events. Import the class from `@xmachines/play-xstate/errors`.
23
28
  *
24
29
  * @example
25
30
  * ```typescript
26
- * buildRouteUrl("/user/:id", { id: "123", query: { tab: "profile" }, hash: "top" });
31
+ * buildRouteUrl("/user/:id", { params: { id: "123" }, query: { tab: "profile" }, hash: "top" });
27
32
  * // → "/user/123?tab=profile#top"
28
33
  *
29
- * buildRouteUrl("/settings/:section?", {});
30
- * // → "/settings" (optional param omitted)
34
+ * buildRouteUrl("/settings/:section?", { params: {}, query: {} });
35
+ * // → "/settings" (optional param omitted, no query string)
31
36
  * ```
32
37
  */
33
- export const buildRouteUrl = (routeTemplate, context = {}) => {
38
+ export const buildRouteUrl = (routeTemplate, context = { query: {} }) => {
39
+ // Guard: when the context has a `params` field (indicating a routing-aware machine
40
+ // context set up via formatPlayRouteTransitions or manually), it must also declare
41
+ // a `query` field. Without `query`, any query parameters from play.route events are
42
+ // silently dropped. Using "query" in context distinguishes an absent field from
43
+ // query: undefined.
44
+ if ("params" in context && !("query" in context)) {
45
+ throw new MissingQueryContextError();
46
+ }
34
47
  // Handle relative vs absolute paths
35
48
  const basePath = context.basePath || "";
36
49
  const isAbsolute = isAbsoluteRoute(routeTemplate);
@@ -63,11 +76,10 @@ export const buildRouteUrl = (routeTemplate, context = {}) => {
63
76
  * - Required parameters without values log warnings
64
77
  *
65
78
  * Parameter lookup:
66
- * - First checks context.routeParams[param] (common pattern in state machines)
67
- * - Falls back to context[param] (flat context)
79
+ * - Reads from context.params[param] exclusively. Flat context fields are not inspected.
68
80
  *
69
81
  * @param template - URL template with :param or :param? syntax
70
- * @param context - Context with parameter values (may have routeParams field)
82
+ * @param context - Context with parameter values (may have params field)
71
83
  * @returns URL with parameters substituted and double slashes cleaned
72
84
  * @throws {MissingRouteParamError} When a required route parameter is missing
73
85
  */
@@ -75,8 +87,7 @@ const substituteParams = (template, context) => {
75
87
  // Replace parameters, handling optional syntax
76
88
  let hasOptionalRemoval = false;
77
89
  const result = template.replace(/:(\w+)(\?)?/g, (_match, param, optional) => {
78
- // Check routeParams first (common pattern), then flat context
79
- const value = context.routeParams?.[param] ?? context[param];
90
+ const value = context.params?.[param];
80
91
  // Parameter has a non-empty value - substitute it
81
92
  // For optional params, treat empty string as "no value"
82
93
  if (value !== undefined && value !== null && value !== "") {
@@ -1 +1 @@
1
- {"version":3,"file":"build-url.js","sourceRoot":"","sources":["../../src/routing/build-url.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"build-url.js","sourceRoot":"","sources":["../../src/routing/build-url.ts"],"names":[],"mappings":"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"}
@@ -59,7 +59,7 @@ export type RouteMachineConfig = {
59
59
  * This automatically generates play.route handlers at the root level that:
60
60
  * - Match event.to against state IDs (e.g., event.to === "#home")
61
61
  * - Target the appropriate state
62
- * - Assign routeParams and queryParams from the event to context
62
+ * - Assign params and query from the event to context
63
63
  *
64
64
  * @param machineConfig - XState machine config (before createMachine). Must extend `RouteMachineConfig`.
65
65
  * @returns The same machine config with auto-generated `play.route` handlers merged in, preserving the original type `T`.
@@ -1 +1 @@
1
- {"version":3,"file":"format-play-route-transitions.d.ts","sourceRoot":"","sources":["../../src/routing/format-play-route-transitions.ts"],"names":[],"mappings":"AAYA;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,iHAAiH;IACjH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iEAAiE;IACjE,IAAI,CAAC,EAAE;QACN,gFAAgF;QAChF,KAAK,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACxC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB,CAAC;AASF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC7C,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IACzC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS,kBAAkB,EAAE,aAAa,EAAE,CAAC,GAAG,CAAC,CAiD5F"}
1
+ {"version":3,"file":"format-play-route-transitions.d.ts","sourceRoot":"","sources":["../../src/routing/format-play-route-transitions.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,iHAAiH;IACjH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iEAAiE;IACjE,IAAI,CAAC,EAAE;QACN,gFAAgF;QAChF,KAAK,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACxC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB,CAAC;AAaF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC7C,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IACzC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS,kBAAkB,EAAE,aAAa,EAAE,CAAC,GAAG,CAAC,CAqD5F"}
@@ -1,4 +1,5 @@
1
1
  import { assign } from "xstate";
2
+ import { MissingStateIdError } from "../errors.js";
2
3
  /**
3
4
  * Formats play.route transitions from declarative route configs
4
5
  *
@@ -23,7 +24,7 @@ import { assign } from "xstate";
23
24
  * This automatically generates play.route handlers at the root level that:
24
25
  * - Match event.to against state IDs (e.g., event.to === "#home")
25
26
  * - Target the appropriate state
26
- * - Assign routeParams and queryParams from the event to context
27
+ * - Assign params and query from the event to context
27
28
  *
28
29
  * @param machineConfig - XState machine config (before createMachine). Must extend `RouteMachineConfig`.
29
30
  * @returns The same machine config with auto-generated `play.route` handlers merged in, preserving the original type `T`.
@@ -36,14 +37,17 @@ export function formatPlayRouteTransitions(machineConfig) {
36
37
  // Build the full key-based path from the root for the transition target
37
38
  // (XState targets use the state key hierarchy, not explicit IDs)
38
39
  const statePath = parentPath ? `${parentPath}.${key}` : key;
40
+ if (node.meta?.route && !node.id) {
41
+ throw new MissingStateIdError(key, node.meta.route);
42
+ }
39
43
  if (node.meta?.route && node.id) {
40
44
  const transition = {
41
45
  target: `.${statePath}`,
42
46
  guard: ({ event }) => event.to === `#${node.id}`,
43
47
  reenter: true,
44
48
  actions: assign({
45
- routeParams: ({ event }) => event.params || {},
46
- queryParams: ({ event }) => event.query || {},
49
+ params: ({ event }) => event.params || {},
50
+ query: ({ event }) => event.query || {},
47
51
  }),
48
52
  };
49
53
  routeTransitions.push(transition);
@@ -1 +1 @@
1
- {"version":3,"file":"format-play-route-transitions.js","sourceRoot":"","sources":["../../src/routing/format-play-route-transitions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AA0DhC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,0BAA0B,CAA+B,aAAgB;IACxF,MAAM,gBAAgB,GAAsB,EAAE,CAAC;IAE/C,MAAM,aAAa,GAAG,CAAC,MAA+B,EAAE,UAAU,GAAG,EAAE,EAAE,EAAE;QAC1E,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,EAAE;YACrD,MAAM,IAAI,GAAG,WAA6B,CAAC;YAC3C,wEAAwE;YACxE,iEAAiE;YACjE,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;YAE5D,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;gBACjC,MAAM,UAAU,GAAoB;oBACnC,MAAM,EAAE,IAAI,SAAS,EAAE;oBACvB,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC,EAAE,EAAE;oBAChD,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE,MAAM,CAAC;wBACf,WAAW,EAAE,CAAC,EAAE,KAAK,EAAkB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE;wBAC9D,WAAW,EAAE,CAAC,EAAE,KAAK,EAAkB,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE;qBAC7D,CAAC;iBACF,CAAC;gBAEF,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACnC,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjB,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YACvC,CAAC;QACF,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,aAAa,CAAC,MAAM,CAAC;IAC3C,IAAI,aAAa,EAAE,CAAC;QACnB,aAAa,CAAC,aAAa,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,MAAM,UAAU,GAAG,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC;QAC1C,MAAM,SAAS,GAAG;YACjB,GAAG,UAAU;YACb,YAAY,EAAE,gBAAgB;SAC9B,CAAC;QAEF,OAAO;YACN,GAAG,aAAa;YAChB,EAAE,EAAE,SAAS;SACR,CAAC;IACR,CAAC;IAED,OAAO,aAAa,CAAC;AACtB,CAAC"}
1
+ {"version":3,"file":"format-play-route-transitions.js","sourceRoot":"","sources":["../../src/routing/format-play-route-transitions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAoDnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,0BAA0B,CAA+B,aAAgB;IACxF,MAAM,gBAAgB,GAAsB,EAAE,CAAC;IAE/C,MAAM,aAAa,GAAG,CAAC,MAA+B,EAAE,UAAU,GAAG,EAAE,EAAE,EAAE;QAC1E,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,EAAE;YACrD,MAAM,IAAI,GAAG,WAA6B,CAAC;YAC3C,wEAAwE;YACxE,iEAAiE;YACjE,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;YAE5D,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBAClC,MAAM,IAAI,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC;YAC/D,CAAC;YAED,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;gBACjC,MAAM,UAAU,GAAoB;oBACnC,MAAM,EAAE,IAAI,SAAS,EAAE;oBACvB,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC,EAAE,EAAE;oBAChD,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE,MAAM,CAAC;wBACf,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE;wBACzC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE;qBACvC,CAAC;iBACF,CAAC;gBAEF,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACnC,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjB,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YACvC,CAAC;QACF,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,aAAa,CAAC,MAAM,CAAC;IAC3C,IAAI,aAAa,EAAE,CAAC;QACnB,aAAa,CAAC,aAAa,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,MAAM,UAAU,GAAG,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC;QAC1C,MAAM,SAAS,GAAG;YACjB,GAAG,UAAU;YACb,YAAY,EAAE,gBAAgB;SAC9B,CAAC;QAEF,OAAO;YACN,GAAG,aAAa;YAChB,EAAE,EAAE,SAAS;SACR,CAAC;IACR,CAAC;IAED,OAAO,aAAa,CAAC;AACtB,CAAC"}
@@ -4,17 +4,24 @@ export interface RouteObject {
4
4
  }
5
5
  export type RouteMetadata = string | RouteObject;
6
6
  /**
7
- * Route build context from machine context
7
+ * Route build context from machine context.
8
+ *
9
+ * All URL parameter substitution must go through `params` — flat context fields are not
10
+ * inspected. This is intentional: the index signature was removed to enable compile-time
11
+ * validation of context shapes and IDE autocomplete on context fields.
12
+ *
13
+ * Machines using `formatPlayRouteTransitions` have `params` and `query` assigned
14
+ * automatically from each `play.route` event. Machines that call `buildRouteUrl` directly
15
+ * must populate `params` explicitly.
8
16
  */
9
17
  export interface RouteContext {
10
18
  /** Base path for relative routes */
11
19
  basePath?: string;
12
- /** Route parameters to substitute */
13
- routeParams?: Record<string, unknown>;
20
+ /** Path-only route parameters to substitute (e.g., `:userId` from `/profile/:userId`) */
21
+ params?: Record<string, unknown>;
14
22
  /** Query parameters */
15
23
  query?: Record<string, unknown>;
16
24
  /** Hash fragment */
17
25
  hash?: string;
18
- [key: string]: unknown;
19
26
  }
20
27
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/routing/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,uBAAuB;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/routing/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,CAAC;AAEjD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAC5B,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,uBAAuB;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd"}
@@ -26,7 +26,9 @@ export declare class StateSignalManager<TSnapshot = unknown> {
26
26
  */
27
27
  scheduleUpdate(snapshot: TSnapshot): void;
28
28
  /**
29
- * Cleanup (no-op since we're synchronous now)
29
+ * Cleanup — currently a no-op because `StateSignalManager` holds no
30
+ * timers, subscriptions, or external resources. If state requiring cleanup
31
+ * is added in the future, implement it here before calling `stop()`.
30
32
  */
31
33
  dispose(): void;
32
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"state-signal.d.ts","sourceRoot":"","sources":["../../src/signals/state-signal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD;;;;;;;;;GASG;AACH,qBAAa,kBAAkB,CAAC,SAAS,GAAG,OAAO;IAClD,OAAO,CAAC,OAAO,CAA0B;gBAE7B,eAAe,EAAE,SAAS;IAItC;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAEpC;IAED;;;;;;;OAOG;IACH,cAAc,CAAC,QAAQ,EAAE,SAAS,GAAG,IAAI;IAIzC;;OAEG;IACH,OAAO,IAAI,IAAI;CAGf"}
1
+ {"version":3,"file":"state-signal.d.ts","sourceRoot":"","sources":["../../src/signals/state-signal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD;;;;;;;;;GASG;AACH,qBAAa,kBAAkB,CAAC,SAAS,GAAG,OAAO;IAClD,OAAO,CAAC,OAAO,CAA0B;gBAE7B,eAAe,EAAE,SAAS;IAItC;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAEpC;IAED;;;;;;;OAOG;IACH,cAAc,CAAC,QAAQ,EAAE,SAAS,GAAG,IAAI;IAIzC;;;;OAIG;IACH,OAAO,IAAI,IAAI;CAIf"}
@@ -32,10 +32,13 @@ export class StateSignalManager {
32
32
  this._signal.set(snapshot);
33
33
  }
34
34
  /**
35
- * Cleanup (no-op since we're synchronous now)
35
+ * Cleanup — currently a no-op because `StateSignalManager` holds no
36
+ * timers, subscriptions, or external resources. If state requiring cleanup
37
+ * is added in the future, implement it here before calling `stop()`.
36
38
  */
37
39
  dispose() {
38
- // No cleanup needed for synchronous updates
40
+ // Intentional no-op: Signal.State has no teardown. Add cleanup here
41
+ // if StateSignalManager gains subscriptions or timers.
39
42
  }
40
43
  }
41
44
  //# sourceMappingURL=state-signal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"state-signal.js","sourceRoot":"","sources":["../../src/signals/state-signal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD;;;;;;;;;GASG;AACH,MAAM,OAAO,kBAAkB;IACtB,OAAO,CAA0B;IAEzC,YAAY,eAA0B;QACrC,IAAI,CAAC,OAAO,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAClD,CAAC;IAED;;OAEG;IACH,IAAI,MAAM;QACT,OAAO,IAAI,CAAC,OAAO,CAAC;IACrB,CAAC;IAED;;;;;;;OAOG;IACH,cAAc,CAAC,QAAmB;QACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,OAAO;QACN,4CAA4C;IAC7C,CAAC;CACD"}
1
+ {"version":3,"file":"state-signal.js","sourceRoot":"","sources":["../../src/signals/state-signal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD;;;;;;;;;GASG;AACH,MAAM,OAAO,kBAAkB;IACtB,OAAO,CAA0B;IAEzC,YAAY,eAA0B;QACrC,IAAI,CAAC,OAAO,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAClD,CAAC;IAED;;OAEG;IACH,IAAI,MAAM;QACT,OAAO,IAAI,CAAC,OAAO,CAAC;IACrB,CAAC;IAED;;;;;;;OAOG;IACH,cAAc,CAAC,QAAmB;QACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACH,OAAO;QACN,oEAAoE;QACpE,uDAAuD;IACxD,CAAC;CACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmachines/play-xstate",
3
- "version": "1.0.0-beta.25",
3
+ "version": "1.0.0-beta.27",
4
4
  "description": "XState v5 adapter for Play Architecture",
5
5
  "keywords": [
6
6
  "actor-model",
@@ -43,12 +43,12 @@
43
43
  "prepublishOnly": "npm run build"
44
44
  },
45
45
  "dependencies": {
46
- "@xmachines/play": "1.0.0-beta.25",
47
- "@xmachines/play-actor": "1.0.0-beta.25",
48
- "@xmachines/play-signals": "1.0.0-beta.25"
46
+ "@xmachines/play": "1.0.0-beta.27",
47
+ "@xmachines/play-actor": "1.0.0-beta.27",
48
+ "@xmachines/play-signals": "1.0.0-beta.27"
49
49
  },
50
50
  "devDependencies": {
51
- "@xmachines/shared": "1.0.0-beta.25",
51
+ "@xmachines/shared": "1.0.0-beta.27",
52
52
  "oxfmt": "^0.43.0",
53
53
  "oxlint": "^1.57.0",
54
54
  "typescript": "^5.9.3 || ^6.0.2",
@@ -1,18 +0,0 @@
1
- /**
2
- * Microtask debouncing for glitch-free signal updates
3
- *
4
- * Per CONTEXT.md: "Microtask batching for glitch-free guarantee"
5
- * Per RESEARCH.md Pattern 5: Updates batch in microtask queue
6
- *
7
- * Leverages TC39 Signal primitives for proper batching.
8
- */
9
- /**
10
- * Schedule callback in microtask queue
11
- *
12
- * Multiple calls in same tick coalesce into single execution.
13
- *
14
- * @param callback - Function to execute in microtask
15
- * @returns Function to cancel scheduled execution
16
- */
17
- export declare const scheduleMicrotask: (callback: () => void) => (() => void);
18
- //# sourceMappingURL=debounce.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"debounce.d.ts","sourceRoot":"","sources":["../../src/signals/debounce.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,IAAI,KAAG,CAAC,MAAM,IAAI,CAoBnE,CAAC"}
@@ -1,35 +0,0 @@
1
- /**
2
- * Microtask debouncing for glitch-free signal updates
3
- *
4
- * Per CONTEXT.md: "Microtask batching for glitch-free guarantee"
5
- * Per RESEARCH.md Pattern 5: Updates batch in microtask queue
6
- *
7
- * Leverages TC39 Signal primitives for proper batching.
8
- */
9
- /**
10
- * Schedule callback in microtask queue
11
- *
12
- * Multiple calls in same tick coalesce into single execution.
13
- *
14
- * @param callback - Function to execute in microtask
15
- * @returns Function to cancel scheduled execution
16
- */
17
- export const scheduleMicrotask = (callback) => {
18
- let scheduled = false;
19
- let cancelled = false;
20
- const execute = () => {
21
- if (!cancelled) {
22
- callback();
23
- }
24
- scheduled = false;
25
- };
26
- if (!scheduled) {
27
- scheduled = true;
28
- queueMicrotask(execute);
29
- }
30
- // Return cancel function
31
- return () => {
32
- cancelled = true;
33
- };
34
- };
35
- //# sourceMappingURL=debounce.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"debounce.js","sourceRoot":"","sources":["../../src/signals/debounce.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAoB,EAAgB,EAAE;IACvE,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,MAAM,OAAO,GAAG,GAAG,EAAE;QACpB,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,QAAQ,EAAE,CAAC;QACZ,CAAC;QACD,SAAS,GAAG,KAAK,CAAC;IACnB,CAAC,CAAC;IAEF,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,SAAS,GAAG,IAAI,CAAC;QACjB,cAAc,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IAED,yBAAyB;IACzB,OAAO,GAAG,EAAE;QACX,SAAS,GAAG,IAAI,CAAC;IAClB,CAAC,CAAC;AACH,CAAC,CAAC"}