@xmachines/play-router 1.0.0-beta.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +259 -335
  3. package/dist/base-route-map.d.ts +136 -0
  4. package/dist/base-route-map.d.ts.map +1 -0
  5. package/dist/base-route-map.js +182 -0
  6. package/dist/base-route-map.js.map +1 -0
  7. package/dist/build-tree.d.ts +7 -0
  8. package/dist/build-tree.d.ts.map +1 -1
  9. package/dist/build-tree.js +21 -6
  10. package/dist/build-tree.js.map +1 -1
  11. package/dist/create-route-map-from-tree.d.ts +33 -0
  12. package/dist/create-route-map-from-tree.d.ts.map +1 -0
  13. package/dist/create-route-map-from-tree.js +43 -0
  14. package/dist/create-route-map-from-tree.js.map +1 -0
  15. package/dist/create-route-map.d.ts +29 -33
  16. package/dist/create-route-map.d.ts.map +1 -1
  17. package/dist/create-route-map.js +25 -64
  18. package/dist/create-route-map.js.map +1 -1
  19. package/dist/errors.d.ts +152 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +186 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/extract-routes.d.ts +5 -31
  24. package/dist/extract-routes.d.ts.map +1 -1
  25. package/dist/extract-routes.js +73 -51
  26. package/dist/extract-routes.js.map +1 -1
  27. package/dist/find-route.d.ts +44 -0
  28. package/dist/find-route.d.ts.map +1 -0
  29. package/dist/find-route.js +103 -0
  30. package/dist/find-route.js.map +1 -0
  31. package/dist/index.d.ts +13 -51
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +13 -133
  34. package/dist/index.js.map +1 -1
  35. package/dist/machine-to-graph.d.ts +17 -0
  36. package/dist/machine-to-graph.d.ts.map +1 -0
  37. package/dist/machine-to-graph.js +115 -0
  38. package/dist/machine-to-graph.js.map +1 -0
  39. package/dist/query.d.ts +49 -1
  40. package/dist/query.d.ts.map +1 -1
  41. package/dist/query.js +98 -3
  42. package/dist/query.js.map +1 -1
  43. package/dist/router-bridge-base.d.ts +196 -32
  44. package/dist/router-bridge-base.d.ts.map +1 -1
  45. package/dist/router-bridge-base.js +319 -85
  46. package/dist/router-bridge-base.js.map +1 -1
  47. package/dist/router-sync.d.ts +95 -0
  48. package/dist/router-sync.d.ts.map +1 -0
  49. package/dist/router-sync.js +161 -0
  50. package/dist/router-sync.js.map +1 -0
  51. package/dist/types.d.ts +163 -22
  52. package/dist/types.d.ts.map +1 -1
  53. package/dist/url-pattern-utils.d.ts +117 -0
  54. package/dist/url-pattern-utils.d.ts.map +1 -0
  55. package/dist/url-pattern-utils.js +176 -0
  56. package/dist/url-pattern-utils.js.map +1 -0
  57. package/dist/validate-routes.d.ts +32 -14
  58. package/dist/validate-routes.d.ts.map +1 -1
  59. package/dist/validate-routes.js +28 -21
  60. package/dist/validate-routes.js.map +1 -1
  61. package/package.json +42 -14
  62. package/dist/connect-router.d.ts +0 -56
  63. package/dist/connect-router.d.ts.map +0 -1
  64. package/dist/connect-router.js +0 -119
  65. package/dist/connect-router.js.map +0 -1
  66. package/dist/crawl-machine.d.ts +0 -74
  67. package/dist/crawl-machine.d.ts.map +0 -1
  68. package/dist/crawl-machine.js +0 -95
  69. package/dist/crawl-machine.js.map +0 -1
  70. package/dist/create-browser-history.d.ts +0 -68
  71. package/dist/create-browser-history.d.ts.map +0 -1
  72. package/dist/create-browser-history.js +0 -94
  73. package/dist/create-browser-history.js.map +0 -1
  74. package/dist/create-router.d.ts +0 -73
  75. package/dist/create-router.d.ts.map +0 -1
  76. package/dist/create-router.js +0 -63
  77. package/dist/create-router.js.map +0 -1
  78. package/dist/extract-route.d.ts +0 -25
  79. package/dist/extract-route.d.ts.map +0 -1
  80. package/dist/extract-route.js +0 -63
  81. package/dist/extract-route.js.map +0 -1
@@ -1,73 +1,34 @@
1
- import { URLPattern } from "urlpattern-polyfill";
1
+ import { extractMachineRoutes } from "./extract-routes.js";
2
+ import { getRoutableRoutes } from "./query.js";
3
+ import { RouteMap } from "./base-route-map.js";
2
4
  /**
3
- * Create a RouteMap from a RouteTree for efficient path lookups.
5
+ * Create a `RouteMap` from an XState state machine.
4
6
  *
5
- * Architecture:
6
- * - Pure function - no side effects
7
- * - O(1) lookups for exact matches via Map
8
- * - O(n) fallback for pattern matching with URLPattern
9
- * - Returns formatted state IDs (with # prefix)
10
- * - Extracts params from matched patterns
7
+ * Extracts all routable states (those with `meta.route`) and builds a bidirectional
8
+ * path ↔ stateId lookup structure. The returned map is used by `RouterBridgeBase`
9
+ * subclasses to translate browser URL changes into `play.route` actor events and
10
+ * vice-versa.
11
11
  *
12
- * Usage:
12
+ * @param machine - XState v5 state machine with `meta.route` annotations on states.
13
+ * @param options - Optional configuration. Pass `{ cacheSize }` to override the
14
+ * default LRU cache size for parameterized path lookups.
15
+ * @returns A `RouteMap` for passing to any `RouterBridgeBase`-based adapter.
16
+ *
17
+ * @example
13
18
  * ```typescript
14
- * const routeTree = extractMachineRoutes(machine);
15
- * const routeMap = createRouteMap(routeTree);
19
+ * import { createRouteMap } from '@xmachines/play-router';
20
+ * import { connectRouter } from '@xmachines/play-dom-router';
16
21
  *
17
- * const { to, params } = routeMap.resolve('/settings/account');
18
- * if (to) {
19
- * actor.send({ type: 'play.route', to, params });
20
- * }
22
+ * const routeMap = createRouteMap(myMachine);
23
+ * const disconnect = connectRouter({ actor, router, routeMap });
21
24
  * ```
22
25
  */
23
- export function createRouteMap(routeTree) {
24
- // Build pattern matchers for routes with dynamic segments
25
- const patternRoutes = [];
26
- // Collect all routable routes with patterns
27
- function collectPatterns(node) {
28
- if (node.routable && node.pattern) {
29
- try {
30
- // URLPattern expects patterns in format: /settings/:section
31
- const pattern = new URLPattern({ pathname: node.pattern });
32
- patternRoutes.push({ pattern, node });
33
- }
34
- catch (err) {
35
- console.warn("[RouteMap] Invalid pattern:", node.pattern, err);
36
- }
37
- }
38
- for (const child of node.children) {
39
- collectPatterns(child);
40
- }
41
- }
42
- collectPatterns(routeTree.root);
43
- return {
44
- resolve(path) {
45
- // Try exact match first (O(1))
46
- const exactMatch = routeTree.byPath.get(path);
47
- if (exactMatch?.routable) {
48
- return {
49
- to: `#${exactMatch.id}`,
50
- params: {},
51
- };
52
- }
53
- // Try pattern matching (O(n))
54
- for (const { pattern, node } of patternRoutes) {
55
- const match = pattern.exec({ pathname: path });
56
- if (match) {
57
- // Extract pathname params (e.g., { section: "account" })
58
- const params = {};
59
- if (match.pathname.groups) {
60
- Object.assign(params, match.pathname.groups);
61
- }
62
- return {
63
- to: `#${node.id}`,
64
- params,
65
- };
66
- }
67
- }
68
- // No match found
69
- return { to: null, params: {} };
70
- },
71
- };
26
+ export function createRouteMap(machine, options) {
27
+ const routeTree = extractMachineRoutes(machine);
28
+ const routes = getRoutableRoutes(routeTree);
29
+ return new RouteMap(routes.map((node) => ({
30
+ stateId: node.stateId,
31
+ path: node.fullPath,
32
+ })), options);
72
33
  }
73
34
  //# sourceMappingURL=create-route-map.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-route-map.js","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAyBjD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,cAAc,CAAC,SAAoB;IAClD,0DAA0D;IAC1D,MAAM,aAAa,GAGd,EAAE,CAAC;IAER,4CAA4C;IAC5C,SAAS,eAAe,CAAC,IAA2B;QACnD,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACnC,IAAI,CAAC;gBACJ,4DAA4D;gBAC5D,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC3D,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACvC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,OAAO,CAAC,IAAI,CAAC,6BAA6B,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAChE,CAAC;QACF,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnC,eAAe,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;IACF,CAAC;IAED,eAAe,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAEhC,OAAO;QACN,OAAO,CAAC,IAAY;YACnB,+BAA+B;YAC/B,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,UAAU,EAAE,QAAQ,EAAE,CAAC;gBAC1B,OAAO;oBACN,EAAE,EAAE,IAAI,UAAU,CAAC,EAAE,EAAE;oBACvB,MAAM,EAAE,EAAE;iBACV,CAAC;YACH,CAAC;YAED,8BAA8B;YAC9B,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,aAAa,EAAE,CAAC;gBAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/C,IAAI,KAAK,EAAE,CAAC;oBACX,yDAAyD;oBACzD,MAAM,MAAM,GAA2B,EAAE,CAAC;oBAC1C,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;wBAC3B,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;oBAC9C,CAAC;oBAED,OAAO;wBACN,EAAE,EAAE,IAAI,IAAI,CAAC,EAAE,EAAE;wBACjB,MAAM;qBACN,CAAC;gBACH,CAAC;YACF,CAAC;YAED,iBAAiB;YACjB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACjC,CAAC;KACD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"create-route-map.js","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAmB/C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,cAAc,CAAC,OAAwB,EAAE,OAAyB;IACjF,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC5C,OAAO,IAAI,QAAQ,CAClB,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACrB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,IAAI,CAAC,QAAQ;KACnB,CAAC,CAAC,EACH,OAAO,CACP,CAAC;AACH,CAAC"}
@@ -0,0 +1,152 @@
1
+ import { PlayError } from "@xmachines/play";
2
+ /**
3
+ * Thrown when `RouterBridgeBase.syncActorFromRouter()` fails to process a router
4
+ * location change and send the corresponding `play.route` event to the actor.
5
+ *
6
+ * The `cause` property wraps the original error.
7
+ *
8
+ * **Error code:** `PLAY_ROUTER_SYNC_FAILED`
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { RouterSyncError } from "@xmachines/play-router/errors";
13
+ *
14
+ * try {
15
+ * bridge.connect();
16
+ * } catch (err) {
17
+ * if (err instanceof RouterSyncError) {
18
+ * console.error("Router sync failed:", err.message, err.cause);
19
+ * }
20
+ * }
21
+ * ```
22
+ */
23
+ export declare class RouterSyncError extends PlayError {
24
+ constructor(message: string, options?: ErrorOptions);
25
+ }
26
+ /**
27
+ * Thrown by `RouterBridgeBase.connect()` when a second bridge tries to connect
28
+ * to an actor that already has an active bridge connection.
29
+ *
30
+ * One actor may only be connected to one router bridge at a time. Two bridges
31
+ * sharing the same actor would both receive and process every router navigation
32
+ * event, sending duplicate `play.route` events to the actor and producing
33
+ * conflicting echo-suppression state via `lastSyncedPath`.
34
+ *
35
+ * **How to fix:** Call `bridge.disconnect()` on the first bridge before
36
+ * connecting a second one, or ensure only one `connectRouter()` call is active
37
+ * per actor at any given time.
38
+ *
39
+ * **Error code:** `PLAY_ROUTER_DUPLICATE_BRIDGE`
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * import { DuplicateBridgeError } from "@xmachines/play-router/errors";
44
+ *
45
+ * try {
46
+ * bridge.connect();
47
+ * } catch (err) {
48
+ * if (err instanceof DuplicateBridgeError) {
49
+ * console.error("Actor already has an active router bridge — disconnect first.");
50
+ * }
51
+ * }
52
+ * ```
53
+ */
54
+ export declare class DuplicateBridgeError extends PlayError {
55
+ constructor();
56
+ }
57
+ /**
58
+ * Thrown by the `RouteMap` constructor when a parameterized route is registered but
59
+ * the `URLPattern` API is not available in the current runtime environment and no
60
+ * polyfill has been loaded.
61
+ *
62
+ * **How to fix:** Load `urlpattern-polyfill` at your application entry point, before
63
+ * any import of `@xmachines/play-router`:
64
+ *
65
+ * ```typescript
66
+ * // app entry point — must run first
67
+ * import "urlpattern-polyfill";
68
+ * ```
69
+ *
70
+ * URLPattern is natively available on Node.js ≥ 24 and modern browsers
71
+ * (Chrome 95+, Firefox 117+, Safari 16.4+). Use the polyfill on older targets.
72
+ *
73
+ * **Error code:** `PLAY_ROUTE_MAP_URLPATTERN_UNAVAILABLE`
74
+ */
75
+ export declare class URLPatternUnavailableError extends PlayError {
76
+ constructor();
77
+ }
78
+ /**
79
+ * Thrown by `validateRouteFormat()` when a state declares `meta.route` with an
80
+ * empty string path.
81
+ *
82
+ * **Error code:** `PLAY_ROUTE_EMPTY_PATH`
83
+ */
84
+ export declare class EmptyRoutePathError extends PlayError {
85
+ /** The state ID whose route path is empty. */
86
+ readonly stateId: string;
87
+ constructor(stateId: string);
88
+ }
89
+ /**
90
+ * Thrown by `validateStateExists()` when a route references a state ID that does
91
+ * not exist in the machine graph.
92
+ *
93
+ * **Error code:** `PLAY_ROUTE_INVALID_STATE_ID`
94
+ */
95
+ export declare class InvalidStateIdError extends PlayError {
96
+ /** The state ID that was referenced but not found. */
97
+ readonly stateId: string;
98
+ constructor(stateId: string);
99
+ }
100
+ /**
101
+ * Thrown by `detectDuplicateRoutes()` when two or more states share the same URL path.
102
+ *
103
+ * **Error code:** `PLAY_ROUTE_DUPLICATE_PATH`
104
+ */
105
+ export declare class DuplicateRoutePathError extends PlayError {
106
+ /** Human-readable list of conflicting path → state-ID mappings. */
107
+ readonly duplicates: string[];
108
+ constructor(duplicates: string[]);
109
+ }
110
+ /**
111
+ * Thrown by `machineToGraph()` when an XState state node has an unrecognised
112
+ * `.type` value.
113
+ *
114
+ * **Error code:** `PLAY_ROUTE_UNKNOWN_STATE_TYPE`
115
+ */
116
+ export declare class UnknownStateTypeError extends PlayError {
117
+ /** The unrecognised state type string. */
118
+ readonly stateType: string;
119
+ /** The ID of the state node with the unknown type. */
120
+ readonly nodeId: string;
121
+ constructor(stateType: string, nodeId: string, validTypes: string[]);
122
+ }
123
+ /**
124
+ * Thrown by the `RouteMap` constructor when a route pattern string cannot be compiled
125
+ * by `URLPattern`. The `pattern` field contains the rejected string; `cause` wraps the
126
+ * original `URLPattern` constructor error.
127
+ *
128
+ * Common causes:
129
+ * - Unmatched parentheses or brackets in the pattern string
130
+ * - Characters that are invalid in a URL pathname pattern
131
+ *
132
+ * **Error code:** `PLAY_ROUTE_MAP_INVALID_PATTERN`
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * import { InvalidRoutePatternError } from "@xmachines/play-router/errors";
137
+ *
138
+ * try {
139
+ * const routeMap = createRouteMap(machine);
140
+ * } catch (err) {
141
+ * if (err instanceof InvalidRoutePatternError) {
142
+ * console.error(`Bad route pattern: "${err.pattern}"`, err.cause);
143
+ * }
144
+ * }
145
+ * ```
146
+ */
147
+ export declare class InvalidRoutePatternError extends PlayError {
148
+ /** The route pattern string that could not be compiled by URLPattern. */
149
+ readonly pattern: string;
150
+ constructor(pattern: string, options?: ErrorOptions);
151
+ }
152
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,eAAgB,SAAQ,SAAS;gBACjC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAInD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,oBAAqB,SAAQ,SAAS;;CAUlD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,0BAA2B,SAAQ,SAAS;;CASxD;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAS3B;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAS3B;AAED;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,SAAS;IACrD,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;gBAElB,UAAU,EAAE,MAAM,EAAE;CAWhC;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,SAAS;IACnD,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE;CAWnE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,wBAAyB,SAAQ,SAAS;IACtD,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAUnD"}
package/dist/errors.js ADDED
@@ -0,0 +1,186 @@
1
+ import { PlayError } from "@xmachines/play";
2
+ /**
3
+ * Thrown when `RouterBridgeBase.syncActorFromRouter()` fails to process a router
4
+ * location change and send the corresponding `play.route` event to the actor.
5
+ *
6
+ * The `cause` property wraps the original error.
7
+ *
8
+ * **Error code:** `PLAY_ROUTER_SYNC_FAILED`
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { RouterSyncError } from "@xmachines/play-router/errors";
13
+ *
14
+ * try {
15
+ * bridge.connect();
16
+ * } catch (err) {
17
+ * if (err instanceof RouterSyncError) {
18
+ * console.error("Router sync failed:", err.message, err.cause);
19
+ * }
20
+ * }
21
+ * ```
22
+ */
23
+ export class RouterSyncError extends PlayError {
24
+ constructor(message, options) {
25
+ super("RouterBridgeBase", "PLAY_ROUTER_SYNC_FAILED", message, options);
26
+ this.name = "RouterSyncError";
27
+ }
28
+ }
29
+ /**
30
+ * Thrown by `RouterBridgeBase.connect()` when a second bridge tries to connect
31
+ * to an actor that already has an active bridge connection.
32
+ *
33
+ * One actor may only be connected to one router bridge at a time. Two bridges
34
+ * sharing the same actor would both receive and process every router navigation
35
+ * event, sending duplicate `play.route` events to the actor and producing
36
+ * conflicting echo-suppression state via `lastSyncedPath`.
37
+ *
38
+ * **How to fix:** Call `bridge.disconnect()` on the first bridge before
39
+ * connecting a second one, or ensure only one `connectRouter()` call is active
40
+ * per actor at any given time.
41
+ *
42
+ * **Error code:** `PLAY_ROUTER_DUPLICATE_BRIDGE`
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * import { DuplicateBridgeError } from "@xmachines/play-router/errors";
47
+ *
48
+ * try {
49
+ * bridge.connect();
50
+ * } catch (err) {
51
+ * if (err instanceof DuplicateBridgeError) {
52
+ * console.error("Actor already has an active router bridge — disconnect first.");
53
+ * }
54
+ * }
55
+ * ```
56
+ */
57
+ export class DuplicateBridgeError extends PlayError {
58
+ constructor() {
59
+ super("RouterBridgeBase", "PLAY_ROUTER_DUPLICATE_BRIDGE", "An actor can only be connected to one router bridge at a time. " +
60
+ "Call disconnect() on the existing bridge before connecting a new one.");
61
+ this.name = "DuplicateBridgeError";
62
+ }
63
+ }
64
+ /**
65
+ * Thrown by the `RouteMap` constructor when a parameterized route is registered but
66
+ * the `URLPattern` API is not available in the current runtime environment and no
67
+ * polyfill has been loaded.
68
+ *
69
+ * **How to fix:** Load `urlpattern-polyfill` at your application entry point, before
70
+ * any import of `@xmachines/play-router`:
71
+ *
72
+ * ```typescript
73
+ * // app entry point — must run first
74
+ * import "urlpattern-polyfill";
75
+ * ```
76
+ *
77
+ * URLPattern is natively available on Node.js ≥ 24 and modern browsers
78
+ * (Chrome 95+, Firefox 117+, Safari 16.4+). Use the polyfill on older targets.
79
+ *
80
+ * **Error code:** `PLAY_ROUTE_MAP_URLPATTERN_UNAVAILABLE`
81
+ */
82
+ export class URLPatternUnavailableError extends PlayError {
83
+ constructor() {
84
+ super("RouteMap", "PLAY_ROUTE_MAP_URLPATTERN_UNAVAILABLE", "URLPattern is not available. Load a polyfill (e.g. urlpattern-polyfill) before using @xmachines/play-router on Node < 24 or older browsers.");
85
+ this.name = "URLPatternUnavailableError";
86
+ }
87
+ }
88
+ /**
89
+ * Thrown by `validateRouteFormat()` when a state declares `meta.route` with an
90
+ * empty string path.
91
+ *
92
+ * **Error code:** `PLAY_ROUTE_EMPTY_PATH`
93
+ */
94
+ export class EmptyRoutePathError extends PlayError {
95
+ /** The state ID whose route path is empty. */
96
+ stateId;
97
+ constructor(stateId) {
98
+ super("RouteMap", "PLAY_ROUTE_EMPTY_PATH", `Empty route path in state "${stateId}": routes must have a non-empty path`);
99
+ this.name = "EmptyRoutePathError";
100
+ this.stateId = stateId;
101
+ }
102
+ }
103
+ /**
104
+ * Thrown by `validateStateExists()` when a route references a state ID that does
105
+ * not exist in the machine graph.
106
+ *
107
+ * **Error code:** `PLAY_ROUTE_INVALID_STATE_ID`
108
+ */
109
+ export class InvalidStateIdError extends PlayError {
110
+ /** The state ID that was referenced but not found. */
111
+ stateId;
112
+ constructor(stateId) {
113
+ super("RouteMap", "PLAY_ROUTE_INVALID_STATE_ID", `Route references non-existent state ID: ${stateId}`);
114
+ this.name = "InvalidStateIdError";
115
+ this.stateId = stateId;
116
+ }
117
+ }
118
+ /**
119
+ * Thrown by `detectDuplicateRoutes()` when two or more states share the same URL path.
120
+ *
121
+ * **Error code:** `PLAY_ROUTE_DUPLICATE_PATH`
122
+ */
123
+ export class DuplicateRoutePathError extends PlayError {
124
+ /** Human-readable list of conflicting path → state-ID mappings. */
125
+ duplicates;
126
+ constructor(duplicates) {
127
+ super("RouteMap", "PLAY_ROUTE_DUPLICATE_PATH", `Duplicate route paths detected:\n${duplicates.join("\n")}\n\n` +
128
+ `Each route path must map to exactly one state.\n` +
129
+ `Use different paths (e.g., /dashboard vs /) or conditional rendering within a single state.`);
130
+ this.name = "DuplicateRoutePathError";
131
+ this.duplicates = duplicates;
132
+ }
133
+ }
134
+ /**
135
+ * Thrown by `machineToGraph()` when an XState state node has an unrecognised
136
+ * `.type` value.
137
+ *
138
+ * **Error code:** `PLAY_ROUTE_UNKNOWN_STATE_TYPE`
139
+ */
140
+ export class UnknownStateTypeError extends PlayError {
141
+ /** The unrecognised state type string. */
142
+ stateType;
143
+ /** The ID of the state node with the unknown type. */
144
+ nodeId;
145
+ constructor(stateType, nodeId, validTypes) {
146
+ super("machineToGraph", "PLAY_ROUTE_UNKNOWN_STATE_TYPE", `Unknown XState state type "${stateType}" on node "${nodeId}". ` +
147
+ `Expected one of: ${validTypes.join(", ")}.`);
148
+ this.name = "UnknownStateTypeError";
149
+ this.stateType = stateType;
150
+ this.nodeId = nodeId;
151
+ }
152
+ }
153
+ /**
154
+ * Thrown by the `RouteMap` constructor when a route pattern string cannot be compiled
155
+ * by `URLPattern`. The `pattern` field contains the rejected string; `cause` wraps the
156
+ * original `URLPattern` constructor error.
157
+ *
158
+ * Common causes:
159
+ * - Unmatched parentheses or brackets in the pattern string
160
+ * - Characters that are invalid in a URL pathname pattern
161
+ *
162
+ * **Error code:** `PLAY_ROUTE_MAP_INVALID_PATTERN`
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * import { InvalidRoutePatternError } from "@xmachines/play-router/errors";
167
+ *
168
+ * try {
169
+ * const routeMap = createRouteMap(machine);
170
+ * } catch (err) {
171
+ * if (err instanceof InvalidRoutePatternError) {
172
+ * console.error(`Bad route pattern: "${err.pattern}"`, err.cause);
173
+ * }
174
+ * }
175
+ * ```
176
+ */
177
+ export class InvalidRoutePatternError extends PlayError {
178
+ /** The route pattern string that could not be compiled by URLPattern. */
179
+ pattern;
180
+ constructor(pattern, options) {
181
+ super("RouteMap", "PLAY_ROUTE_MAP_INVALID_PATTERN", `Invalid route pattern: "${pattern}"`, options);
182
+ this.name = "InvalidRoutePatternError";
183
+ this.pattern = pattern;
184
+ }
185
+ }
186
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
@@ -3,39 +3,13 @@ import type { RouteTree } from "./types.js";
3
3
  /**
4
4
  * Extract complete route tree from state machine graph
5
5
  *
6
- * Crawls machine starting from root, visits all state nodes (including nested
7
- * and parallel), extracts meta.route metadata, validates route references,
8
- * and builds hierarchical tree.
6
+ * Converts the XState machine to a @statelyai/graph Graph via machineToGraph(),
7
+ * then walks graph nodes to extract route metadata, validate route references,
8
+ * and build a hierarchical RouteTree. The graph is attached to the returned
9
+ * RouteTree for downstream transition-aware queries.
9
10
  *
10
11
  * @param machine - XState v5 state machine
11
- * @returns Route tree with root, byStateId map, and byPath map
12
- * @throws {Error} If route references non-existent state
13
- * @throws {Error} If route path is malformed (doesn't start with /)
14
- * @throws {Error} If parallel states define conflicting routes
15
- *
16
- * @example
17
- * ```typescript
18
- * import { createMachine } from 'xstate';
19
- * import { extractMachineRoutes } from '@xmachines/play-router';
20
- *
21
- * const machine = createMachine({
22
- * initial: 'home',
23
- * states: {
24
- * home: { id: 'home', meta: { route: '/' } },
25
- * dashboard: {
26
- * id: 'dashboard',
27
- * meta: { route: '/dashboard' },
28
- * initial: 'overview',
29
- * states: {
30
- * overview: { id: 'overview', meta: { route: '/overview' } }
31
- * }
32
- * }
33
- * }
34
- * });
35
- *
36
- * const tree = extractMachineRoutes(machine);
37
- * console.log(tree.byPath.get('/dashboard/overview'));
38
- * ```
12
+ * @returns Route tree with root, byStateId map, byPath map, and graph
39
13
  */
40
14
  export declare const extractMachineRoutes: (machine: AnyStateMachine) => RouteTree;
41
15
  //# sourceMappingURL=extract-routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extract-routes.d.ts","sourceRoot":"","sources":["../src/extract-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAa,MAAM,QAAQ,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAM5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,eAAe,KAAG,SAsB/D,CAAC"}
1
+ {"version":3,"file":"extract-routes.d.ts","sourceRoot":"","sources":["../src/extract-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,KAAK,EAAE,SAAS,EAA6C,MAAM,YAAY,CAAC;AA2EvF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,eAAe,KAAG,SAmB/D,CAAC"}
@@ -1,61 +1,83 @@
1
- import { crawlMachine } from "./crawl-machine.js";
2
- import { extractRoute } from "./extract-route.js";
3
- import { detectDuplicateRoutes } from "./validate-routes.js";
1
+ import { machineToGraph } from "./machine-to-graph.js";
2
+ import { validateRouteFormat, validateStateExists } from "./validate-routes.js";
4
3
  import { buildRouteTree } from "./build-tree.js";
5
4
  /**
6
- * Extract complete route tree from state machine graph
7
- *
8
- * Crawls machine starting from root, visits all state nodes (including nested
9
- * and parallel), extracts meta.route metadata, validates route references,
10
- * and builds hierarchical tree.
5
+ * Build statePath for a graph node by walking the parentId chain.
11
6
  *
12
- * @param machine - XState v5 state machine
13
- * @returns Route tree with root, byStateId map, and byPath map
14
- * @throws {Error} If route references non-existent state
15
- * @throws {Error} If route path is malformed (doesn't start with /)
16
- * @throws {Error} If parallel states define conflicting routes
7
+ * Produces an array of stateId segments from root to this node (excluding root),
8
+ * matching the format expected by buildRouteTree's parent lookup.
17
9
  *
18
- * @example
19
- * ```typescript
20
- * import { createMachine } from 'xstate';
21
- * import { extractMachineRoutes } from '@xmachines/play-router';
10
+ * @param graphNode - The node to build a path for
11
+ * @param graphNodeMap - Map from node id to GraphNode for O(1) parent lookup
12
+ * @returns Array of stateId segments (e.g. ["dashboard", "overview"])
13
+ */
14
+ const buildStatePath = (graphNode, graphNodeMap) => {
15
+ const path = [];
16
+ let current = graphNode;
17
+ while (current && current.parentId !== undefined && current.parentId !== null) {
18
+ path.unshift(current.data.stateId);
19
+ current = graphNodeMap.get(current.parentId);
20
+ }
21
+ return path;
22
+ };
23
+ /**
24
+ * Extract route info from a graph node's MachineNodeData.
25
+ * Returns null if the node has no route (not routable).
26
+ */
27
+ const extractRouteFromNode = (graphNode, stateIds, graphNodeMap) => {
28
+ const { stateId, route, meta } = graphNode.data;
29
+ // Node has no route — not routable
30
+ if (!route)
31
+ return null;
32
+ // Validate format (must start with /)
33
+ validateRouteFormat(route, stateId);
34
+ // Validate state exists in graph (redundant but guards against corruption)
35
+ validateStateExists(stateId, stateIds);
36
+ // Build statePath from parentId chain (works for both explicit IDs and auto-generated)
37
+ const statePath = buildStatePath(graphNode, graphNodeMap);
38
+ const pattern = route.includes(":") ? route : undefined;
39
+ // Reconstruct original metadata shape from graph data
40
+ const metadata = meta && typeof meta === "object" && "route" in meta
41
+ ? meta["route"]
42
+ : route;
43
+ const result = {
44
+ stateId,
45
+ statePath,
46
+ routePath: route,
47
+ isAbsolute: route.startsWith("/"),
48
+ routable: true,
49
+ metadata,
50
+ };
51
+ if (pattern)
52
+ result.pattern = pattern;
53
+ return result;
54
+ };
55
+ /**
56
+ * Extract complete route tree from state machine graph
22
57
  *
23
- * const machine = createMachine({
24
- * initial: 'home',
25
- * states: {
26
- * home: { id: 'home', meta: { route: '/' } },
27
- * dashboard: {
28
- * id: 'dashboard',
29
- * meta: { route: '/dashboard' },
30
- * initial: 'overview',
31
- * states: {
32
- * overview: { id: 'overview', meta: { route: '/overview' } }
33
- * }
34
- * }
35
- * }
36
- * });
58
+ * Converts the XState machine to a @statelyai/graph Graph via machineToGraph(),
59
+ * then walks graph nodes to extract route metadata, validate route references,
60
+ * and build a hierarchical RouteTree. The graph is attached to the returned
61
+ * RouteTree for downstream transition-aware queries.
37
62
  *
38
- * const tree = extractMachineRoutes(machine);
39
- * console.log(tree.byPath.get('/dashboard/overview'));
40
- * ```
63
+ * @param machine - XState v5 state machine
64
+ * @returns Route tree with root, byStateId map, byPath map, and graph
41
65
  */
42
66
  export const extractMachineRoutes = (machine) => {
43
- // 1. Crawl machine graph to discover all states
44
- const visits = crawlMachine(machine);
45
- // 2. Build state map for validation
46
- const stateMap = new Map();
47
- for (const visit of visits) {
48
- if (visit.node.id) {
49
- stateMap.set(visit.node.id, visit.node);
50
- }
51
- }
52
- // 3. Extract routes with validation
53
- const routes = visits
54
- .map((visit) => extractRoute(visit.node, visit.path, stateMap))
55
- .filter((route) => route !== null);
56
- // 4. Detect duplicate routes
57
- detectDuplicateRoutes(routes);
58
- // 5. Build hierarchical tree
59
- return buildRouteTree(routes);
67
+ // 1. Convert machine to graph
68
+ const graph = machineToGraph(machine);
69
+ // 2. Build state ID set for validation and node map for O(1) parent lookup
70
+ const stateIds = new Set(graph.nodes.map((n) => n.data.stateId));
71
+ const graphNodeMap = new Map(graph.nodes.map((n) => [n.id, n]));
72
+ // 3. Extract routes from graph nodes
73
+ const routes = graph.nodes
74
+ .map((node) => extractRouteFromNode(node, stateIds, graphNodeMap))
75
+ .filter((r) => r !== null);
76
+ // 4. Build hierarchical tree and attach graph.
77
+ // Duplicate-route detection happens inside buildRouteTree, AFTER relative
78
+ // routes are resolved to full paths — raw route strings are not comparable
79
+ // (two relative "settings" routes under different parents are distinct).
80
+ const tree = buildRouteTree(routes);
81
+ return { ...tree, graph };
60
82
  };
61
83
  //# sourceMappingURL=extract-routes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"extract-routes.js","sourceRoot":"","sources":["../src/extract-routes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,OAAwB,EAAa,EAAE;IAC3E,gDAAgD;IAChD,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IAErC,oCAAoC;IACpC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC9C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACnB,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,CAAC;IACF,CAAC;IAED,oCAAoC;IACpC,MAAM,MAAM,GAAG,MAAM;SACnB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;SAC9D,MAAM,CAAC,CAAC,KAAK,EAAsC,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;IAExE,6BAA6B;IAC7B,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAE9B,6BAA6B;IAC7B,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC,CAAC"}
1
+ {"version":3,"file":"extract-routes.js","sourceRoot":"","sources":["../src/extract-routes.ts"],"names":[],"mappings":"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"}