@xmachines/play-xstate 1.0.0-beta.25 → 1.0.0-beta.26
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/README.md +15 -15
- package/dist/define-player.d.ts +1 -1
- package/dist/define-player.d.ts.map +1 -1
- package/dist/define-player.js +10 -2
- package/dist/define-player.js.map +1 -1
- package/dist/errors.d.ts +72 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +83 -0
- package/dist/errors.js.map +1 -1
- package/dist/player-actor.d.ts +42 -4
- package/dist/player-actor.d.ts.map +1 -1
- package/dist/player-actor.js +88 -33
- package/dist/player-actor.js.map +1 -1
- package/dist/routing/build-url.d.ts +16 -11
- package/dist/routing/build-url.d.ts.map +1 -1
- package/dist/routing/build-url.js +29 -18
- package/dist/routing/build-url.js.map +1 -1
- package/dist/routing/format-play-route-transitions.d.ts +1 -1
- package/dist/routing/format-play-route-transitions.d.ts.map +1 -1
- package/dist/routing/format-play-route-transitions.js +7 -3
- package/dist/routing/format-play-route-transitions.js.map +1 -1
- package/dist/routing/types.d.ts +11 -4
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/signals/state-signal.d.ts +3 -1
- package/dist/signals/state-signal.d.ts.map +1 -1
- package/dist/signals/state-signal.js +5 -2
- package/dist/signals/state-signal.js.map +1 -1
- package/package.json +5 -5
- package/dist/signals/debounce.d.ts +0 -18
- package/dist/signals/debounce.d.ts.map +0 -1
- package/dist/signals/debounce.js +0 -35
- package/dist/signals/debounce.js.map +0 -1
package/README.md
CHANGED
|
@@ -38,8 +38,8 @@ const machine = setup({
|
|
|
38
38
|
types: {
|
|
39
39
|
context: {} as {
|
|
40
40
|
isAuthenticated: boolean;
|
|
41
|
-
|
|
42
|
-
|
|
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,
|
|
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.
|
|
182
|
-
- `currentView: Signal.State<ViewMetadata | null>` — Current view spec (updated on every state transition). The spec is automatically enriched with `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.
|
|
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
|
-
|
|
318
|
-
|
|
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
|
|
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
|
|
528
|
-
|
|
|
529
|
-
| Explicit non-`undefined` spec prop
|
|
530
|
-
| URL route param (`context.
|
|
531
|
-
| `contextProps` field
|
|
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 →
|
|
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
|
```
|
package/dist/define-player.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-player.d.ts","sourceRoot":"","sources":["../src/define-player.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
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"}
|
package/dist/define-player.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
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":"
|
|
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);
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -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) {
|
package/dist/errors.js.map
CHANGED
|
@@ -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"}
|
package/dist/player-actor.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
114
|
-
*
|
|
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.
|
|
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;
|
|
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"}
|
package/dist/player-actor.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
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.
|
|
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
|
|
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
|
-
* //
|
|
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(
|
|
117
|
+
function mergeRouteParamsIntoProps(urlParams, contextValues, existingProps) {
|
|
103
118
|
// Layer 3 (lowest priority): allowlisted context values
|
|
104
119
|
const merged = { ...contextValues };
|
|
105
|
-
// Layer 2:
|
|
106
|
-
for (const [k, v] of Object.entries(
|
|
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.
|
|
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.
|
|
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",
|
|
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",
|
|
153
|
-
* // Derived props: { username: "demo" } ←
|
|
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
|
|
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
|
|
175
|
-
? context.
|
|
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(
|
|
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
|
-
*
|
|
321
|
-
*
|
|
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.
|
|
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
|
|
370
|
-
//
|
|
371
|
-
|
|
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
|
package/dist/player-actor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"player-actor.js","sourceRoot":"","sources":["../src/player-actor.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,WAAW,GASX,MAAM,QAAQ,CAAC;AAChB,OAAO,EACN,aAAa,GAIb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAExE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;
|
|
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.
|
|
6
|
-
*
|
|
5
|
+
* Substitutes `:param` and `:param?` placeholders from `context.params`,
|
|
6
|
+
* then appends query params and hash fragments.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* 2. `context[param]` — flat context fallback
|
|
8
|
+
* All parameter values must be in `context.params`. Flat context fields are not
|
|
9
|
+
* inspected — this 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
|
|
16
|
-
*
|
|
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
|
|
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.
|
|
7
|
-
*
|
|
6
|
+
* Substitutes `:param` and `:param?` placeholders from `context.params`,
|
|
7
|
+
* then appends query params and hash fragments.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 2. `context[param]` — flat context fallback
|
|
9
|
+
* All parameter values must be in `context.params`. Flat context fields are not
|
|
10
|
+
* inspected — this 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
|
|
17
|
-
*
|
|
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
|
-
* -
|
|
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
|
|
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
|
-
|
|
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":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;
|
|
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
|
|
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":"
|
|
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
|
|
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
|
-
|
|
46
|
-
|
|
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;
|
|
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"}
|
package/dist/routing/types.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
13
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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.
|
|
3
|
+
"version": "1.0.0-beta.26",
|
|
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.
|
|
47
|
-
"@xmachines/play-actor": "1.0.0-beta.
|
|
48
|
-
"@xmachines/play-signals": "1.0.0-beta.
|
|
46
|
+
"@xmachines/play": "1.0.0-beta.26",
|
|
47
|
+
"@xmachines/play-actor": "1.0.0-beta.26",
|
|
48
|
+
"@xmachines/play-signals": "1.0.0-beta.26"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@xmachines/shared": "1.0.0-beta.
|
|
51
|
+
"@xmachines/shared": "1.0.0-beta.26",
|
|
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"}
|
package/dist/signals/debounce.js
DELETED
|
@@ -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"}
|