@xmachines/play-xstate 1.0.0 → 2.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +77 -24
  2. package/dist/define-player.d.ts +3 -3
  3. package/dist/define-player.js +3 -3
  4. package/dist/define-player.typecheck.js +10 -10
  5. package/dist/define-player.typecheck.js.map +1 -1
  6. package/dist/errors.d.ts +38 -0
  7. package/dist/errors.d.ts.map +1 -1
  8. package/dist/errors.js +45 -0
  9. package/dist/errors.js.map +1 -1
  10. package/dist/guards/compose.d.ts +59 -41
  11. package/dist/guards/compose.d.ts.map +1 -1
  12. package/dist/guards/compose.js +97 -69
  13. package/dist/guards/compose.js.map +1 -1
  14. package/dist/guards/compose.typecheck.d.ts +2 -0
  15. package/dist/guards/compose.typecheck.d.ts.map +1 -0
  16. package/dist/guards/compose.typecheck.js +22 -0
  17. package/dist/guards/compose.typecheck.js.map +1 -0
  18. package/dist/index.d.ts +4 -3
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +4 -2
  21. package/dist/index.js.map +1 -1
  22. package/dist/player-actor.d.ts +3 -3
  23. package/dist/player-actor.d.ts.map +1 -1
  24. package/dist/player-actor.js +15 -9
  25. package/dist/player-actor.js.map +1 -1
  26. package/dist/player-actor.typecheck.js +8 -5
  27. package/dist/player-actor.typecheck.js.map +1 -1
  28. package/dist/routing/create-routed-machine.d.ts +71 -0
  29. package/dist/routing/create-routed-machine.d.ts.map +1 -0
  30. package/dist/routing/create-routed-machine.js +71 -0
  31. package/dist/routing/create-routed-machine.js.map +1 -0
  32. package/dist/routing/derive-initial-route.d.ts +1 -1
  33. package/dist/routing/derive-initial-route.js +1 -1
  34. package/dist/routing/format-play-route-transitions.d.ts +54 -11
  35. package/dist/routing/format-play-route-transitions.d.ts.map +1 -1
  36. package/dist/routing/format-play-route-transitions.js +125 -52
  37. package/dist/routing/format-play-route-transitions.js.map +1 -1
  38. package/dist/routing/index.d.ts +4 -0
  39. package/dist/routing/index.d.ts.map +1 -1
  40. package/dist/routing/index.js +2 -0
  41. package/dist/routing/index.js.map +1 -1
  42. package/dist/routing/play-route-event.typecheck.d.ts +2 -0
  43. package/dist/routing/play-route-event.typecheck.d.ts.map +1 -0
  44. package/dist/routing/play-route-event.typecheck.js +43 -0
  45. package/dist/routing/play-route-event.typecheck.js.map +1 -0
  46. package/dist/routing/schemas.d.ts +99 -0
  47. package/dist/routing/schemas.d.ts.map +1 -0
  48. package/dist/routing/schemas.js +30 -0
  49. package/dist/routing/schemas.js.map +1 -0
  50. package/dist/schemas.d.ts +28 -0
  51. package/dist/schemas.d.ts.map +1 -0
  52. package/dist/schemas.js +29 -0
  53. package/dist/schemas.js.map +1 -0
  54. package/dist/types.d.ts +1 -1
  55. package/package.json +9 -9
@@ -0,0 +1,71 @@
1
+ import { formatPlayRouteTransitions, } from "./format-play-route-transitions.js";
2
+ /**
3
+ * Returns a `createMachine` that routes: identical signature to the setup's
4
+ * own `createMachine`, with {@link formatPlayRouteTransitions} applied to the
5
+ * config at runtime.
6
+ *
7
+ * ### Why this exists
8
+ *
9
+ * `formatPlayRouteTransitions(config)` accepts a loosely-typed config. A config
10
+ * authored as a standalone value loses inline transition-function parameter
11
+ * inference entirely (implicit-any under strict mode), and even the inline
12
+ * `setup().createMachine(formatPlayRouteTransitions({ ... }))` form depends on
13
+ * TypeScript threading contextual typing through two generic calls.
14
+ *
15
+ * This wrapper removes the question: it is typed as the setup's own
16
+ * `createMachine`, so the config is contextually typed exactly as if passed to
17
+ * `setup().createMachine(...)` directly — transition-function args infer from
18
+ * the setup's schemas, sibling targets are validated, and the returned machine
19
+ * keeps its full literal state-value typing.
20
+ *
21
+ * Typing is *identical to native* — including its current gaps: as of xstate
22
+ * 6.0.0-alpha.20, `event` and `context` reads in transition args are fully
23
+ * typed, while returned context patches are property-typed but still carry a
24
+ * loose index signature in the alpha itself. Expect that to tighten upstream
25
+ * without changes here.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * import { setup, types } from "xstate";
30
+ * import { createRoutedMachine, playMetaSchema, playRouteEventSchema } from "@xmachines/play-xstate";
31
+ *
32
+ * const authSetup = setup({
33
+ * schemas: {
34
+ * context: types<{ isAuthenticated: boolean }>(),
35
+ * events: { "play.route": playRouteEventSchema },
36
+ * meta: playMetaSchema,
37
+ * },
38
+ * });
39
+ *
40
+ * const machine = createRoutedMachine(authSetup)({
41
+ * initial: "home",
42
+ * context: { isAuthenticated: false },
43
+ * states: {
44
+ * home: { id: "home", meta: { route: "/" } },
45
+ * login: { id: "login", meta: { route: "/login" } },
46
+ * },
47
+ * });
48
+ * // ({ context }) params in transitions infer from the setup schemas —
49
+ * // no explicit annotations needed.
50
+ * ```
51
+ *
52
+ * Note: routes are injected at runtime, so the machine's static type does not
53
+ * gain an `xstate.route` event for them — `play.route` (declared in the
54
+ * setup's event schemas) remains the typed public API; native `xstate.route`
55
+ * interop is runtime-only.
56
+ *
57
+ * @param s - A `setup(...)` return value (anything with `createMachine`).
58
+ * @returns A function with the same signature as `s.createMachine` that
59
+ * applies {@link formatPlayRouteTransitions} before creating the machine.
60
+ *
61
+ * @see {@link formatPlayRouteTransitions} for the routing transform itself
62
+ */
63
+ export const createRoutedMachine = (s) =>
64
+ // The runtime transform only ADDS behavior (native route configs + the
65
+ // play.route forwarder) and preserves the config's own shape, so exposing
66
+ // the setup's exact createMachine signature keeps native config typing.
67
+ ((config) =>
68
+ // One internal widening cast: the `never[]` constraint that keeps
69
+ // SetupLike any-free also makes the parameter uncallable directly.
70
+ s.createMachine(formatPlayRouteTransitions(config)));
71
+ //# sourceMappingURL=create-routed-machine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-routed-machine.js","sourceRoot":"","sources":["../../src/routing/create-routed-machine.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,0BAA0B,GAE1B,MAAM,oCAAoC,CAAC;AAa5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAA2B,CAAS,EAA2B,EAAE;AACnG,uEAAuE;AACvE,0EAA0E;AAC1E,wEAAwE;AACxE,CAAC,CAAC,MAA0B,EAAE,EAAE;AAC/B,kEAAkE;AAClE,mEAAmE;AAClE,CAAC,CAAC,aAA8C,CAChD,0BAA0B,CAAC,MAAM,CAAC,CAClC,CAA4B,CAAC"}
@@ -17,7 +17,7 @@ import { type AnyStateMachine } from "xstate";
17
17
  * a different restored route → actor wins). Because the derivation never consults a
18
18
  * restored snapshot, it always yields the machine's **default** initial route.
19
19
  *
20
- * @param machine - XState v5 state machine definition.
20
+ * @param machine - XState v6 state machine definition.
21
21
  * @param input - Actor input, applied to the machine's context factory exactly as
22
22
  * `createActor(machine, { input })` would.
23
23
  * @returns Resolved initial URL string, or `null` if the initial state has no
@@ -18,7 +18,7 @@ import { deriveCurrentRoute } from "./derive-current-route.js";
18
18
  * a different restored route → actor wins). Because the derivation never consults a
19
19
  * restored snapshot, it always yields the machine's **default** initial route.
20
20
  *
21
- * @param machine - XState v5 state machine definition.
21
+ * @param machine - XState v6 state machine definition.
22
22
  * @param input - Actor input, applied to the machine's context factory exactly as
23
23
  * `createActor(machine, { input })` would.
24
24
  * @returns Resolved initial URL string, or `null` if the initial state has no
@@ -18,6 +18,8 @@ export type RouteStateNode = {
18
18
  */
19
19
  route?: RouteMetadata;
20
20
  };
21
+ /** XState v6 native route config; injected as `{}` when absent. */
22
+ route?: unknown;
21
23
  /** Nested child states, recursively crawled for additional route declarations. */
22
24
  states?: Record<string, RouteStateNode>;
23
25
  [key: string]: unknown;
@@ -30,8 +32,15 @@ export type RouteStateNode = {
30
32
  * config object and the stricter `setup().createMachine` config without requiring
31
33
  * any type casts at the call site. The generic `T extends RouteMachineConfig`
32
34
  * parameter on `formatPlayRouteTransitions` preserves the original concrete type
33
- * through the transform, so the return value remains directly usable by
34
- * `setup().createMachine()`.
35
+ * of the RETURN value, so it remains directly usable by `setup().createMachine()`.
36
+ *
37
+ * Known limitation: a config authored as a STANDALONE value
38
+ * (`const config = formatPlayRouteTransitions({...})`) is typed against this
39
+ * loose shape only, so inline transition functions lose parameter inference
40
+ * (implicit-any under strict mode). Written inline inside a
41
+ * `setup().createMachine(formatPlayRouteTransitions({...}))` call, contextual
42
+ * typing still flows. {@link createRoutedMachine} is the recommended entry
43
+ * point either way — it keeps the setup's exact `createMachine` signature.
35
44
  */
36
45
  export type RouteMachineConfig = {
37
46
  context?: unknown;
@@ -40,12 +49,18 @@ export type RouteMachineConfig = {
40
49
  [key: string]: unknown;
41
50
  };
42
51
  /**
43
- * Formats play.route transitions from declarative route configs
52
+ * Formats play.route routing from declarative route configs.
44
53
  *
45
- * Crawls machine states looking for states with meta.route and generates
46
- * transitions that handle `play.route` events by matching event.to to state IDs.
54
+ * Crawls machine states looking for states with `meta.route` and wires them to
55
+ * XState v6's **native routing machinery**:
47
56
  *
48
- * Inspired by XState's internal formatRouteTransitions (stateUtils.ts line 391).
57
+ * - every routed state gets a native `route: {}` config (unless it already
58
+ * declares its own `route`), making it targetable via the built-in
59
+ * `{ type: "xstate.route", to: "#id" }` event (Stately tooling interop and
60
+ * statically-known graph edges), and
61
+ * - one root-level `play.route` forwarder performs the navigation for those
62
+ * targets as ONE atomic transition: target + re-entry + a shallow
63
+ * `params`/`query` context patch.
49
64
  *
50
65
  * @example
51
66
  * ```typescript
@@ -60,13 +75,41 @@ export type RouteMachineConfig = {
60
75
  * const machine = createMachine(formatPlayRouteTransitions(machineConfig));
61
76
  * ```
62
77
  *
63
- * This automatically generates play.route handlers at the root level that:
64
- * - Match event.to against state IDs (e.g., event.to === "#home")
65
- * - Target the appropriate state
66
- * - Assign params and query from the event to context
78
+ * Because the injected `route` configs are static (object form), the machine's
79
+ * route transitions keep **statically-known targets** — `toDirectedGraph()` /
80
+ * `machineToGraph()` edges point at the actual routed states, so graph
81
+ * reachability queries work. (Function-form transition candidates would have
82
+ * dynamic targets, opaque to static analysis.)
83
+ *
84
+ * The `play.route` public API and its v5 semantics are preserved:
85
+ * - `send({ type: "play.route", to: "#home", params, query })` navigates and
86
+ * patches `context.params`/`context.query` in the SAME transition — exit
87
+ * actions of the departing state observe the OLD params, entry actions of
88
+ * the target observe the NEW ones, `always` transitions never see a
89
+ * half-navigated intermediate state, and entry/exit actions receive the
90
+ * original `play.route` event;
91
+ * - user-defined `play.route` transitions (e.g. a hand-written 404 fallback)
92
+ * are preserved AFTER the forwarder: for unknown targets the forwarder
93
+ * returns `undefined`, so evaluation falls through to them;
94
+ * - `params`/`query` are only patched when the forwarder navigates — unknown
95
+ * and blocked targets leave context untouched.
96
+ *
97
+ * When a state declares its OWN `route` config (which wins over the injected
98
+ * default), the forwarder re-raises the event as `xstate.route` instead of
99
+ * navigating, so the user's resolver decides — including blocking. The
100
+ * forwarder does NOT patch `params`/`query` for such targets (a blocking
101
+ * resolver would otherwise leave context describing a route that was never
102
+ * entered); the user's function-form resolver owns any context patch, and
103
+ * that navigation microstep runs under the `xstate.route` event.
104
+ *
105
+ * Native `xstate.route` events also work directly (Stately ecosystem tooling),
106
+ * but bypass the forwarder — no `params`/`query` context patch is applied, so
107
+ * stale values from earlier `play.route` events linger in context (and a
108
+ * router bridge deriving the URL from context would interpolate those stale
109
+ * params).
67
110
  *
68
111
  * @param machineConfig - XState machine config (before createMachine). Must extend `RouteMachineConfig`.
69
- * @returns The same machine config with auto-generated `play.route` handlers merged in, preserving the original type `T`.
112
+ * @returns The machine config with native `route` configs and the `play.route` forwarder merged in, preserving the original type `T`.
70
113
  */
71
114
  export declare function formatPlayRouteTransitions<T extends RouteMachineConfig>(machineConfig: T): T;
72
115
  //# sourceMappingURL=format-play-route-transitions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format-play-route-transitions.d.ts","sourceRoot":"","sources":["../../src/routing/format-play-route-transitions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,iHAAiH;IACjH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iEAAiE;IACjE,IAAI,CAAC,EAAE;QACN;;;WAGG;QACH,KAAK,CAAC,EAAE,aAAa,CAAC;KACtB,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,CA0E5F"}
1
+ {"version":3,"file":"format-play-route-transitions.d.ts","sourceRoot":"","sources":["../../src/routing/format-play-route-transitions.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,iHAAiH;IACjH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iEAAiE;IACjE,IAAI,CAAC,EAAE;QACN;;;WAGG;QACH,KAAK,CAAC,EAAE,aAAa,CAAC;KACtB,CAAC;IACF,mEAAmE;IACnE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACxC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB,CAAC;AA4BF;;;;;;;;;;;;;;;;;GAiBG;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS,kBAAkB,EAAE,aAAa,EAAE,CAAC,GAAG,CAAC,CAyH5F"}
@@ -1,13 +1,18 @@
1
- import { assign } from "xstate";
2
1
  import { MissingStateIdError } from "../errors.js";
3
2
  import { normalizeRoute } from "./derive-route.js";
4
3
  /**
5
- * Formats play.route transitions from declarative route configs
4
+ * Formats play.route routing from declarative route configs.
6
5
  *
7
- * Crawls machine states looking for states with meta.route and generates
8
- * transitions that handle `play.route` events by matching event.to to state IDs.
6
+ * Crawls machine states looking for states with `meta.route` and wires them to
7
+ * XState v6's **native routing machinery**:
9
8
  *
10
- * Inspired by XState's internal formatRouteTransitions (stateUtils.ts line 391).
9
+ * - every routed state gets a native `route: {}` config (unless it already
10
+ * declares its own `route`), making it targetable via the built-in
11
+ * `{ type: "xstate.route", to: "#id" }` event (Stately tooling interop and
12
+ * statically-known graph edges), and
13
+ * - one root-level `play.route` forwarder performs the navigation for those
14
+ * targets as ONE atomic transition: target + re-entry + a shallow
15
+ * `params`/`query` context patch.
11
16
  *
12
17
  * @example
13
18
  * ```typescript
@@ -22,21 +27,58 @@ import { normalizeRoute } from "./derive-route.js";
22
27
  * const machine = createMachine(formatPlayRouteTransitions(machineConfig));
23
28
  * ```
24
29
  *
25
- * This automatically generates play.route handlers at the root level that:
26
- * - Match event.to against state IDs (e.g., event.to === "#home")
27
- * - Target the appropriate state
28
- * - Assign params and query from the event to context
30
+ * Because the injected `route` configs are static (object form), the machine's
31
+ * route transitions keep **statically-known targets** — `toDirectedGraph()` /
32
+ * `machineToGraph()` edges point at the actual routed states, so graph
33
+ * reachability queries work. (Function-form transition candidates would have
34
+ * dynamic targets, opaque to static analysis.)
35
+ *
36
+ * The `play.route` public API and its v5 semantics are preserved:
37
+ * - `send({ type: "play.route", to: "#home", params, query })` navigates and
38
+ * patches `context.params`/`context.query` in the SAME transition — exit
39
+ * actions of the departing state observe the OLD params, entry actions of
40
+ * the target observe the NEW ones, `always` transitions never see a
41
+ * half-navigated intermediate state, and entry/exit actions receive the
42
+ * original `play.route` event;
43
+ * - user-defined `play.route` transitions (e.g. a hand-written 404 fallback)
44
+ * are preserved AFTER the forwarder: for unknown targets the forwarder
45
+ * returns `undefined`, so evaluation falls through to them;
46
+ * - `params`/`query` are only patched when the forwarder navigates — unknown
47
+ * and blocked targets leave context untouched.
48
+ *
49
+ * When a state declares its OWN `route` config (which wins over the injected
50
+ * default), the forwarder re-raises the event as `xstate.route` instead of
51
+ * navigating, so the user's resolver decides — including blocking. The
52
+ * forwarder does NOT patch `params`/`query` for such targets (a blocking
53
+ * resolver would otherwise leave context describing a route that was never
54
+ * entered); the user's function-form resolver owns any context patch, and
55
+ * that navigation microstep runs under the `xstate.route` event.
56
+ *
57
+ * Native `xstate.route` events also work directly (Stately ecosystem tooling),
58
+ * but bypass the forwarder — no `params`/`query` context patch is applied, so
59
+ * stale values from earlier `play.route` events linger in context (and a
60
+ * router bridge deriving the URL from context would interpolate those stale
61
+ * params).
29
62
  *
30
63
  * @param machineConfig - XState machine config (before createMachine). Must extend `RouteMachineConfig`.
31
- * @returns The same machine config with auto-generated `play.route` handlers merged in, preserving the original type `T`.
64
+ * @returns The machine config with native `route` configs and the `play.route` forwarder merged in, preserving the original type `T`.
32
65
  */
33
66
  export function formatPlayRouteTransitions(machineConfig) {
34
- const routeTransitions = [];
35
- const collectRoutes = (states, parentPath = "") => {
36
- Object.entries(states).forEach(([key, stateConfig]) => {
37
- const node = stateConfig;
38
- // Build the full key-based path from the root for the transition target
39
- // (XState targets use the state key hierarchy, not explicit IDs)
67
+ /**
68
+ * Routing table: `#id` -> the state's key path (e.g. `.dashboard.stats`)
69
+ * for targets whose `route` config was injected by this transform, or
70
+ * `null` for targets with a USER-declared `route` config (the user's
71
+ * resolver decides those — the forwarder re-raises instead of navigating).
72
+ */
73
+ const routeTargets = new Map();
74
+ /**
75
+ * Rebuild the states tree, injecting a native `route` config into routed
76
+ * nodes. The input config is never mutated — every node is shallow-copied.
77
+ */
78
+ const injectRoutes = (states, parentPath = "") => {
79
+ const next = {};
80
+ for (const [key, stateConfig] of Object.entries(states)) {
81
+ const node = { ...stateConfig };
40
82
  const statePath = parentPath ? `${parentPath}.${key}` : key;
41
83
  // Validates the metadata (malformed object routes throw
42
84
  // InvalidRouteMetadataError) and normalizes to the path string. Both
@@ -48,48 +90,79 @@ export function formatPlayRouteTransitions(machineConfig) {
48
90
  if (routePath && !node.id) {
49
91
  throw new MissingStateIdError(key, routePath);
50
92
  }
51
- if (routePath && node.id) {
52
- const transition = {
53
- target: `.${statePath}`,
54
- guard: ({ event }) => event.to === `#${node.id}`,
55
- reenter: true,
56
- actions: assign({
57
- params: ({ event }) => event.params || {},
58
- query: ({ event }) => event.query || {},
59
- }),
60
- };
61
- routeTransitions.push(transition);
62
- }
63
93
  if (node.states) {
64
- collectRoutes(node.states, statePath);
94
+ node.states = injectRoutes(node.states, statePath);
95
+ }
96
+ if (routePath && node.id) {
97
+ if (node.route === undefined) {
98
+ // Native object-form route config: statically-targeted transition,
99
+ // visible to graph tooling. The play.route forwarder navigates these
100
+ // targets DIRECTLY (atomically); the native config exists for
101
+ // xstate.route interop (Stately tooling, devtools).
102
+ node.route = {};
103
+ routeTargets.set(`#${node.id}`, `.${statePath}`);
104
+ }
105
+ else {
106
+ // A user-declared route config wins: the forwarder re-raises
107
+ // xstate.route so the user's resolver decides (and owns any
108
+ // context patch).
109
+ routeTargets.set(`#${node.id}`, null);
110
+ }
65
111
  }
66
- });
112
+ next[key] = node;
113
+ }
114
+ return next;
67
115
  };
68
116
  const machineStates = machineConfig.states;
69
- if (machineStates) {
70
- collectRoutes(machineStates);
117
+ const injectedStates = machineStates ? injectRoutes(machineStates) : machineStates;
118
+ if (routeTargets.size === 0) {
119
+ return machineConfig;
71
120
  }
72
- if (routeTransitions.length > 0) {
73
- const existingOn = machineConfig.on || {};
74
- // Preserve user-defined play.route transitions (e.g. a hand-written 404
75
- // fallback) by appending them AFTER the generated ones: XState evaluates
76
- // candidate transitions in order, so generated guards win when they match
77
- // and the user's transitions act as fallbacks otherwise.
78
- const userRouteTransitions = existingOn["play.route"];
79
- const normalizedUserTransitions = userRouteTransitions === undefined
80
- ? []
81
- : Array.isArray(userRouteTransitions)
82
- ? userRouteTransitions
83
- : [userRouteTransitions];
84
- const updatedOn = {
85
- ...existingOn,
86
- "play.route": [...routeTransitions, ...normalizedUserTransitions],
87
- };
121
+ // Root play.route forwarder. For injected-route targets it returns ONE
122
+ // atomic transition (target + reenter + params/query patch): exit actions
123
+ // see the OLD params, `always` transitions never observe a half-navigated
124
+ // intermediate state, and entry/exit actions receive the original
125
+ // play.route event. For user-routed targets it re-raises xstate.route
126
+ // WITHOUT patching (the user's resolver owns the patch — otherwise a
127
+ // blocking resolver would leave context describing a route that was never
128
+ // entered). Unknown targets return undefined so user fallback candidates
129
+ // below are evaluated.
130
+ const forwarder = ({ event }, enq) => {
131
+ const target = routeTargets.get(event.to);
132
+ if (target === undefined)
133
+ return undefined;
134
+ if (target === null) {
135
+ enq.raise({ ...event, type: "xstate.route" });
136
+ return {};
137
+ }
88
138
  return {
89
- ...machineConfig,
90
- on: updatedOn,
139
+ target,
140
+ reenter: true,
141
+ // Shallow context patch — v6's replacement for assign().
142
+ context: {
143
+ params: event.params || {},
144
+ query: event.query || {},
145
+ },
91
146
  };
92
- }
93
- return machineConfig;
147
+ };
148
+ // Preserve user-defined play.route transitions (e.g. a hand-written 404
149
+ // fallback) by appending them AFTER the forwarder: XState evaluates
150
+ // candidate transitions in order, so the forwarder wins for known targets
151
+ // and the user's transitions act as fallbacks otherwise.
152
+ const existingOn = machineConfig.on || {};
153
+ const userRouteTransitions = existingOn["play.route"];
154
+ const normalizedUserTransitions = userRouteTransitions === undefined
155
+ ? []
156
+ : Array.isArray(userRouteTransitions)
157
+ ? userRouteTransitions
158
+ : [userRouteTransitions];
159
+ return {
160
+ ...machineConfig,
161
+ states: injectedStates,
162
+ on: {
163
+ ...existingOn,
164
+ "play.route": [forwarder, ...normalizedUserTransitions],
165
+ },
166
+ };
94
167
  }
95
168
  //# sourceMappingURL=format-play-route-transitions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"format-play-route-transitions.js","sourceRoot":"","sources":["../../src/routing/format-play-route-transitions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"format-play-route-transitions.js","sourceRoot":"","sources":["../../src/routing/format-play-route-transitions.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,10 @@ export { deriveCurrentRoute } from "./derive-current-route.js";
11
11
  export { deriveInitialRoute } from "./derive-initial-route.js";
12
12
  export { buildRouteUrl } from "./build-url.js";
13
13
  export { formatPlayRouteTransitions } from "./format-play-route-transitions.js";
14
+ export { playMetaSchema, playRouteEventSchema } from "./schemas.js";
15
+ export type { PlayRoutePayload, WithOptional } from "./schemas.js";
16
+ export type { SetupLike } from "./create-routed-machine.js";
17
+ export { createRoutedMachine } from "./create-routed-machine.js";
14
18
  export type { RouteMachineConfig, RouteStateNode } from "./format-play-route-transitions.js";
15
19
  export type { RouteContext, RouteObject, RouteMetadata } from "./types.js";
16
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAC7F,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACnE,YAAY,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAC7F,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -11,4 +11,6 @@ export { deriveCurrentRoute } from "./derive-current-route.js";
11
11
  export { deriveInitialRoute } from "./derive-initial-route.js";
12
12
  export { buildRouteUrl } from "./build-url.js";
13
13
  export { formatPlayRouteTransitions } from "./format-play-route-transitions.js";
14
+ export { playMetaSchema, playRouteEventSchema } from "./schemas.js";
15
+ export { createRoutedMachine } from "./create-routed-machine.js";
14
16
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=play-route-event.typecheck.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"play-route-event.typecheck.d.ts","sourceRoot":"","sources":["../../src/routing/play-route-event.typecheck.ts"],"names":[],"mappings":""}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Type-level contract for the play.route event payload as machines receive it
3
+ * through playRouteEventSchema. Pins the requirements the hand-written v5
4
+ * union enforced: `to` is required and string-typed; params/query/match are
5
+ * omittable; values constructed as PlayRouteEvent (router bridges) remain
6
+ * assignable.
7
+ */
8
+ import { setup } from "xstate";
9
+ import { playRouteEventSchema } from "./schemas.js";
10
+ const s = setup({
11
+ schemas: {
12
+ events: { "play.route": playRouteEventSchema },
13
+ },
14
+ });
15
+ const m = s.createMachine({ initial: "a", states: { a: {} } });
16
+ // Minimal send: optional payload fields are omittable.
17
+ const minimal = { type: "play.route", to: "#home" };
18
+ // Full send: every documented field accepted.
19
+ const full = {
20
+ type: "play.route",
21
+ to: "#profile",
22
+ params: { username: "alice" },
23
+ query: { tab: "posts" },
24
+ match: {},
25
+ };
26
+ // @ts-expect-error `to` is required — a routeless play.route must not compile
27
+ const missingTo = { type: "play.route" };
28
+ // @ts-expect-error `to` must be a string
29
+ const badTo = { type: "play.route", to: 123 };
30
+ const badParams = {
31
+ type: "play.route",
32
+ to: "#p",
33
+ // @ts-expect-error `params` values must be strings
34
+ params: { n: 42 },
35
+ };
36
+ const fromBridge = bridgeEvent;
37
+ void fromBridge;
38
+ void minimal;
39
+ void full;
40
+ void missingTo;
41
+ void badTo;
42
+ void badParams;
43
+ //# sourceMappingURL=play-route-event.typecheck.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"play-route-event.typecheck.js","sourceRoot":"","sources":["../../src/routing/play-route-event.typecheck.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,KAAK,EAAuB,MAAM,QAAQ,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,CAAC,GAAG,KAAK,CAAC;IACf,OAAO,EAAE;QACR,MAAM,EAAE,EAAE,YAAY,EAAE,oBAAoB,EAAE;KAC9C;CACD,CAAC,CAAC;AACH,MAAM,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAG/D,uDAAuD;AACvD,MAAM,OAAO,GAAyB,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;AAE1E,8CAA8C;AAC9C,MAAM,IAAI,GAAyB;IAClC,IAAI,EAAE,YAAY;IAClB,EAAE,EAAE,UAAU;IACd,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE;IAC7B,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE;IACvB,KAAK,EAAE,EAAE;CACT,CAAC;AAEF,8EAA8E;AAC9E,MAAM,SAAS,GAAyB,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;AAE/D,yCAAyC;AACzC,MAAM,KAAK,GAAyB,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;AAEpE,MAAM,SAAS,GAAyB;IACvC,IAAI,EAAE,YAAY;IAClB,EAAE,EAAE,IAAI;IACR,mDAAmD;IACnD,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;CACjB,CAAC;AAKF,MAAM,UAAU,GAAyB,WAAW,CAAC;AAErD,KAAK,UAAU,CAAC;AAChB,KAAK,OAAO,CAAC;AACb,KAAK,IAAI,CAAC;AACV,KAAK,SAAS,CAAC;AACf,KAAK,KAAK,CAAC;AACX,KAAK,SAAS,CAAC"}
@@ -0,0 +1,99 @@
1
+ import type { PlayRouteEvent } from "@xmachines/play-router";
2
+ import type { PlaySpec } from "@xmachines/play-actor";
3
+ import type { RouteMetadata } from "./types.js";
4
+ /**
5
+ * Event schema for the `play.route` navigation event, for use in an XState v6
6
+ * `schemas.events` map.
7
+ *
8
+ * XState v6 declares machine events as a map of event type to payload schema
9
+ * (payloads exclude the `type` field). Every Play machine handles `play.route`,
10
+ * so this constant is the single source of truth for its payload shape —
11
+ * declare it instead of re-deriving the payload shape per machine. The payload
12
+ * is authored explicitly (not `Omit<PlayRouteEvent, "type">`): `Omit` over
13
+ * PlayRouteEvent's index signature collapses the named props and would erase
14
+ * all payload checking.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { setup } from "xstate";
19
+ * import {
20
+ * emptyEventSchema,
21
+ * playMetaSchema,
22
+ * playRouteEventSchema,
23
+ * } from "@xmachines/play-xstate";
24
+ *
25
+ * const authSetup = setup({
26
+ * schemas: {
27
+ * events: {
28
+ * "play.route": playRouteEventSchema,
29
+ * "auth.logout": emptyEventSchema,
30
+ * },
31
+ * meta: playMetaSchema,
32
+ * },
33
+ * });
34
+ * ```
35
+ *
36
+ * @see {@link playMetaSchema} for the companion state-meta schema
37
+ */
38
+ /**
39
+ * Adds `TExtra` as an optional-by-union extension of `TBase`.
40
+ *
41
+ * XState v6's `InferEvents` wraps every event payload in `Required<>` (to
42
+ * undo Zod-style optional inference), which would make `?`-marked fields
43
+ * mandatory on every send. Encoding optionality as a union sidesteps that:
44
+ * `Required<>` distributes over the members, each of which is already fully
45
+ * required. Use it for your own event payload schemas with optional fields.
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * type SavePayload = WithOptional<{ id: string }, { draft: boolean }>;
50
+ * // accepts { id } and { id, draft } — and survives InferEvents' Required<>
51
+ * ```
52
+ */
53
+ export type WithOptional<TBase, TExtra> = TBase | (TBase & TExtra);
54
+ /**
55
+ * The `play.route` event payload as machines receive it: `to` required;
56
+ * `params`/`query`/`match` encoded as optional-by-union so they stay omittable
57
+ * through XState v6's `Required<>` event inference.
58
+ */
59
+ export type PlayRoutePayload = WithOptional<WithOptional<WithOptional<{
60
+ to: PlayRouteEvent["to"];
61
+ }, {
62
+ params: Record<string, string>;
63
+ }>, {
64
+ query: Record<string, string>;
65
+ }>, {
66
+ match: unknown;
67
+ }>;
68
+ export declare const playRouteEventSchema: import("xstate").TypeSchema<PlayRoutePayload>;
69
+ /**
70
+ * State meta schema for Play's routing/view metadata, for use as an XState v6
71
+ * `schemas.meta` declaration.
72
+ *
73
+ * XState v6 types state `meta` as `never` unless a meta schema is declared, so
74
+ * every machine that carries `meta.route` templates or `meta.view` specs needs
75
+ * this schema. `route` accepts both the string template form
76
+ * (`"/profile/:username"`) and the object form (`{ path, title }` — see
77
+ * {@link RouteMetadata}); `view` is a Play view {@link PlaySpec}.
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * import { createMachine } from "xstate";
82
+ * import { playMetaSchema } from "@xmachines/play-xstate";
83
+ *
84
+ * const machine = createMachine({
85
+ * schemas: { meta: playMetaSchema },
86
+ * initial: "home",
87
+ * states: {
88
+ * home: { id: "home", meta: { route: "/" } },
89
+ * },
90
+ * });
91
+ * ```
92
+ *
93
+ * @see {@link playRouteEventSchema} for the companion `play.route` event schema
94
+ */
95
+ export declare const playMetaSchema: import("xstate").TypeSchema<{
96
+ route?: RouteMetadata;
97
+ view?: PlaySpec;
98
+ }>;
99
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/routing/schemas.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,YAAY,CAAC,KAAK,EAAE,MAAM,IAAI,KAAK,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC;AAEnE;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAC1C,YAAY,CACX,YAAY,CAAC;IAAE,EAAE,EAAE,cAAc,CAAC,IAAI,CAAC,CAAA;CAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC,EAC9E;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CACjC,EACD;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAClB,CAAC;AAEF,eAAO,MAAM,oBAAoB,+CAA4B,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,cAAc;YAAmB,aAAa;WAAS,QAAQ;EAAK,CAAC"}