@xmachines/play-router 1.0.0-beta.2 → 1.0.0-beta.21

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 (66) hide show
  1. package/README.md +169 -47
  2. package/dist/base-route-map.d.ts +116 -0
  3. package/dist/base-route-map.d.ts.map +1 -0
  4. package/dist/base-route-map.js +206 -0
  5. package/dist/base-route-map.js.map +1 -0
  6. package/dist/build-tree.d.ts.map +1 -1
  7. package/dist/build-tree.js +6 -5
  8. package/dist/build-tree.js.map +1 -1
  9. package/dist/connect-router.d.ts.map +1 -1
  10. package/dist/connect-router.js +35 -45
  11. package/dist/connect-router.js.map +1 -1
  12. package/dist/create-browser-history.d.ts +38 -5
  13. package/dist/create-browser-history.d.ts.map +1 -1
  14. package/dist/create-browser-history.js +43 -17
  15. package/dist/create-browser-history.js.map +1 -1
  16. package/dist/create-route-map.d.ts +21 -1
  17. package/dist/create-route-map.d.ts.map +1 -1
  18. package/dist/create-route-map.js +73 -22
  19. package/dist/create-route-map.js.map +1 -1
  20. package/dist/errors.d.ts +75 -0
  21. package/dist/errors.d.ts.map +1 -0
  22. package/dist/errors.js +85 -0
  23. package/dist/errors.js.map +1 -0
  24. package/dist/extract-routes.d.ts +5 -31
  25. package/dist/extract-routes.d.ts.map +1 -1
  26. package/dist/extract-routes.js +70 -49
  27. package/dist/extract-routes.js.map +1 -1
  28. package/dist/find-route.d.ts +44 -0
  29. package/dist/find-route.d.ts.map +1 -0
  30. package/dist/find-route.js +126 -0
  31. package/dist/find-route.js.map +1 -0
  32. package/dist/index.d.ts +9 -48
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +16 -131
  35. package/dist/index.js.map +1 -1
  36. package/dist/machine-to-graph.d.ts +17 -0
  37. package/dist/machine-to-graph.d.ts.map +1 -0
  38. package/dist/machine-to-graph.js +115 -0
  39. package/dist/machine-to-graph.js.map +1 -0
  40. package/dist/query.d.ts +44 -1
  41. package/dist/query.d.ts.map +1 -1
  42. package/dist/query.js +80 -3
  43. package/dist/query.js.map +1 -1
  44. package/dist/router-bridge-base.d.ts +49 -19
  45. package/dist/router-bridge-base.d.ts.map +1 -1
  46. package/dist/router-bridge-base.js +120 -56
  47. package/dist/router-bridge-base.js.map +1 -1
  48. package/dist/router-sync.d.ts +62 -0
  49. package/dist/router-sync.d.ts.map +1 -0
  50. package/dist/router-sync.js +87 -0
  51. package/dist/router-sync.js.map +1 -0
  52. package/dist/types.d.ts +73 -14
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/validate-routes.d.ts +9 -9
  55. package/dist/validate-routes.d.ts.map +1 -1
  56. package/dist/validate-routes.js +12 -11
  57. package/dist/validate-routes.js.map +1 -1
  58. package/package.json +36 -18
  59. package/dist/crawl-machine.d.ts +0 -74
  60. package/dist/crawl-machine.d.ts.map +0 -1
  61. package/dist/crawl-machine.js +0 -95
  62. package/dist/crawl-machine.js.map +0 -1
  63. package/dist/extract-route.d.ts +0 -25
  64. package/dist/extract-route.d.ts.map +0 -1
  65. package/dist/extract-route.js +0 -63
  66. package/dist/extract-route.js.map +0 -1
@@ -1,14 +1,66 @@
1
- import { URLPattern } from "urlpattern-polyfill";
1
+ import { URLPatternUnavailableError, InvalidRoutePatternError } from "./errors.js";
2
+ function getURLPatternCtor() {
3
+ return globalThis["URLPattern"];
4
+ }
5
+ function getIndexKey(path) {
6
+ const trimmed = path.startsWith("/") ? path.slice(1) : path;
7
+ if (trimmed.length === 0)
8
+ return "/";
9
+ const segment = trimmed.split("/")[0];
10
+ if (segment === undefined || segment.startsWith(":"))
11
+ return "*";
12
+ return segment;
13
+ }
14
+ function getCandidates(patternBuckets, indexKey) {
15
+ const bucket = patternBuckets.get(indexKey) ?? [];
16
+ const wildcardBucket = patternBuckets.get("*") ?? [];
17
+ if (bucket.length === 0)
18
+ return wildcardBucket;
19
+ if (wildcardBucket.length === 0)
20
+ return bucket;
21
+ const merged = [];
22
+ let b = 0;
23
+ let w = 0;
24
+ while (b < bucket.length && w < wildcardBucket.length) {
25
+ if (bucket[b].order < wildcardBucket[w].order) {
26
+ merged.push(bucket[b]);
27
+ b += 1;
28
+ }
29
+ else {
30
+ merged.push(wildcardBucket[w]);
31
+ w += 1;
32
+ }
33
+ }
34
+ while (b < bucket.length) {
35
+ merged.push(bucket[b]);
36
+ b += 1;
37
+ }
38
+ while (w < wildcardBucket.length) {
39
+ merged.push(wildcardBucket[w]);
40
+ w += 1;
41
+ }
42
+ return merged;
43
+ }
2
44
  /**
3
45
  * Create a RouteMap from a RouteTree for efficient path lookups.
4
46
  *
5
47
  * Architecture:
6
48
  * - Pure function - no side effects
7
49
  * - O(1) lookups for exact matches via Map
8
- * - O(n) fallback for pattern matching with URLPattern
50
+ * - O(k) bucket-indexed pattern matching with URLPattern, where k = routes in the
51
+ * first-segment bucket (typically << total routes)
9
52
  * - Returns formatted state IDs (with # prefix)
10
53
  * - Extracts params from matched patterns
11
54
  *
55
+ * **URLPattern requirement:** Dynamic route pattern matching requires `URLPattern`
56
+ * to be available on `globalThis`. On Node.js < 24 and older browsers, load a polyfill
57
+ * before calling this function:
58
+ * ```typescript
59
+ * import "urlpattern-polyfill"; // before importing @xmachines/play-router
60
+ * ```
61
+ * If `URLPattern` is unavailable, a `URLPatternUnavailableError` is thrown.
62
+ * Static (exact) routes are unaffected by URLPattern availability.
63
+ *
12
64
  * Usage:
13
65
  * ```typescript
14
66
  * const routeTree = extractMachineRoutes(machine);
@@ -21,18 +73,24 @@ import { URLPattern } from "urlpattern-polyfill";
21
73
  * ```
22
74
  */
23
75
  export function createRouteMap(routeTree) {
24
- // Build pattern matchers for routes with dynamic segments
25
- const patternRoutes = [];
26
- // Collect all routable routes with patterns
76
+ // Build bucket-indexed pattern matchers for routes with dynamic segments
77
+ const patternBuckets = new Map();
78
+ let patternOrder = 0;
79
+ const URLPatternCtor = getURLPatternCtor();
27
80
  function collectPatterns(node) {
28
81
  if (node.routable && node.pattern) {
82
+ if (!URLPatternCtor) {
83
+ throw new URLPatternUnavailableError();
84
+ }
29
85
  try {
30
- // URLPattern expects patterns in format: /settings/:section
31
- const pattern = new URLPattern({ pathname: node.pattern });
32
- patternRoutes.push({ pattern, node });
86
+ const urlPattern = new URLPatternCtor({ pathname: node.pattern });
87
+ const bucketKey = getIndexKey(node.pattern);
88
+ const bucket = patternBuckets.get(bucketKey) ?? [];
89
+ bucket.push({ pattern: urlPattern, node, order: patternOrder++ });
90
+ patternBuckets.set(bucketKey, bucket);
33
91
  }
34
92
  catch (err) {
35
- console.warn("[RouteMap] Invalid pattern:", node.pattern, err);
93
+ throw new InvalidRoutePatternError(node.pattern, { cause: err });
36
94
  }
37
95
  }
38
96
  for (const child of node.children) {
@@ -42,30 +100,23 @@ export function createRouteMap(routeTree) {
42
100
  collectPatterns(routeTree.root);
43
101
  return {
44
102
  resolve(path) {
45
- // Try exact match first (O(1))
103
+ // O(1) exact match via routeTree.byPath Map (unchanged)
46
104
  const exactMatch = routeTree.byPath.get(path);
47
105
  if (exactMatch?.routable) {
48
- return {
49
- to: `#${exactMatch.id}`,
50
- params: {},
51
- };
106
+ return { to: `#${exactMatch.id}`, params: {} };
52
107
  }
53
- // Try pattern matching (O(n))
54
- for (const { pattern, node } of patternRoutes) {
108
+ // Bucket-indexed pattern match: only scan candidates for this path's first segment
109
+ const candidates = getCandidates(patternBuckets, getIndexKey(path));
110
+ for (const { pattern, node } of candidates) {
55
111
  const match = pattern.exec({ pathname: path });
56
112
  if (match) {
57
- // Extract pathname params (e.g., { section: "account" })
58
113
  const params = {};
59
114
  if (match.pathname.groups) {
60
115
  Object.assign(params, match.pathname.groups);
61
116
  }
62
- return {
63
- to: `#${node.id}`,
64
- params,
65
- };
117
+ return { to: `#${node.id}`, params };
66
118
  }
67
119
  }
68
- // No match found
69
120
  return { to: null, params: {} };
70
121
  },
71
122
  };
@@ -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":"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"}
@@ -0,0 +1,75 @@
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 `createRouteMap()` when the `URLPattern` API is not available in the
28
+ * current runtime environment and no polyfill has been loaded.
29
+ *
30
+ * **How to fix:** Load `urlpattern-polyfill` at your application entry point, before
31
+ * any import of `@xmachines/play-router`:
32
+ *
33
+ * ```typescript
34
+ * // app entry point — must run first
35
+ * import "urlpattern-polyfill";
36
+ * ```
37
+ *
38
+ * URLPattern is natively available on Node.js ≥ 24 and modern browsers
39
+ * (Chrome 95+, Firefox 117+, Safari 16.4+). Use the polyfill on older targets.
40
+ *
41
+ * **Error code:** `PLAY_ROUTE_MAP_URLPATTERN_UNAVAILABLE`
42
+ */
43
+ export declare class URLPatternUnavailableError extends PlayError {
44
+ constructor();
45
+ }
46
+ /**
47
+ * Thrown by `createRouteMap()` when a route pattern string cannot be compiled by
48
+ * `URLPattern`. The `pattern` field contains the rejected string; `cause` wraps the
49
+ * original `URLPattern` constructor error.
50
+ *
51
+ * Common causes:
52
+ * - Unmatched parentheses or brackets in the pattern string
53
+ * - Characters that are invalid in a URL pathname pattern
54
+ *
55
+ * **Error code:** `PLAY_ROUTE_MAP_INVALID_PATTERN`
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * import { InvalidRoutePatternError } from "@xmachines/play-router/errors";
60
+ *
61
+ * try {
62
+ * const routeMap = createRouteMap(tree);
63
+ * } catch (err) {
64
+ * if (err instanceof InvalidRoutePatternError) {
65
+ * console.error(`Bad route pattern: "${err.pattern}"`, err.cause);
66
+ * }
67
+ * }
68
+ * ```
69
+ */
70
+ export declare class InvalidRoutePatternError extends PlayError {
71
+ /** The route pattern string that could not be compiled by URLPattern. */
72
+ readonly pattern: string;
73
+ constructor(pattern: string, options?: ErrorOptions);
74
+ }
75
+ //# 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;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,0BAA2B,SAAQ,SAAS;;CASxD;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,85 @@
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 `createRouteMap()` when the `URLPattern` API is not available in the
31
+ * current runtime environment and no polyfill has been loaded.
32
+ *
33
+ * **How to fix:** Load `urlpattern-polyfill` at your application entry point, before
34
+ * any import of `@xmachines/play-router`:
35
+ *
36
+ * ```typescript
37
+ * // app entry point — must run first
38
+ * import "urlpattern-polyfill";
39
+ * ```
40
+ *
41
+ * URLPattern is natively available on Node.js ≥ 24 and modern browsers
42
+ * (Chrome 95+, Firefox 117+, Safari 16.4+). Use the polyfill on older targets.
43
+ *
44
+ * **Error code:** `PLAY_ROUTE_MAP_URLPATTERN_UNAVAILABLE`
45
+ */
46
+ export class URLPatternUnavailableError extends PlayError {
47
+ constructor() {
48
+ 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.");
49
+ this.name = "URLPatternUnavailableError";
50
+ }
51
+ }
52
+ /**
53
+ * Thrown by `createRouteMap()` when a route pattern string cannot be compiled by
54
+ * `URLPattern`. The `pattern` field contains the rejected string; `cause` wraps the
55
+ * original `URLPattern` constructor error.
56
+ *
57
+ * Common causes:
58
+ * - Unmatched parentheses or brackets in the pattern string
59
+ * - Characters that are invalid in a URL pathname pattern
60
+ *
61
+ * **Error code:** `PLAY_ROUTE_MAP_INVALID_PATTERN`
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * import { InvalidRoutePatternError } from "@xmachines/play-router/errors";
66
+ *
67
+ * try {
68
+ * const routeMap = createRouteMap(tree);
69
+ * } catch (err) {
70
+ * if (err instanceof InvalidRoutePatternError) {
71
+ * console.error(`Bad route pattern: "${err.pattern}"`, err.cause);
72
+ * }
73
+ * }
74
+ * ```
75
+ */
76
+ export class InvalidRoutePatternError extends PlayError {
77
+ /** The route pattern string that could not be compiled by URLPattern. */
78
+ pattern;
79
+ constructor(pattern, options) {
80
+ super("RouteMap", "PLAY_ROUTE_MAP_INVALID_PATTERN", `Invalid route pattern: "${pattern}"`, options);
81
+ this.name = "InvalidRoutePatternError";
82
+ this.pattern = pattern;
83
+ }
84
+ }
85
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,OAAO,eAAgB,SAAQ,SAAS;IAC7C,YAAY,OAAe,EAAE,OAAsB;QAClD,KAAK,CAAC,kBAAkB,EAAE,yBAAyB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAC/B,CAAC;CACD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,0BAA2B,SAAQ,SAAS;IACxD;QACC,KAAK,CACJ,UAAU,EACV,uCAAuC,EACvC,6IAA6I,CAC7I,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC1C,CAAC;CACD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,wBAAyB,SAAQ,SAAS;IACtD,yEAAyE;IAChE,OAAO,CAAS;IAEzB,YAAY,OAAe,EAAE,OAAsB;QAClD,KAAK,CACJ,UAAU,EACV,gCAAgC,EAChC,2BAA2B,OAAO,GAAG,EACrC,OAAO,CACP,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC;CACD"}
@@ -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;AA+EvF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,eAAe,KAAG,SAmB/D,CAAC"}
@@ -1,61 +1,82 @@
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, detectDuplicateRoutes, } 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);
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);
56
76
  // 4. Detect duplicate routes
57
77
  detectDuplicateRoutes(routes);
58
- // 5. Build hierarchical tree
59
- return buildRouteTree(routes);
78
+ // 5. Build hierarchical tree and attach graph
79
+ const tree = buildRouteTree(routes);
80
+ return { ...tree, graph };
60
81
  };
61
82
  //# 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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"}
@@ -0,0 +1,44 @@
1
+ import type { RouteTree, RouteNode } from "./types.js";
2
+ /**
3
+ * Find route node by state ID
4
+ *
5
+ * Looks up route node using the state's ID property. Used to get URL path
6
+ * from state ID for browser URL sync after play.route transitions.
7
+ *
8
+ * @param tree - Route tree from extractMachineRoutes
9
+ * @param id - State ID (e.g., 'dashboard', 'settings.profile')
10
+ * @returns Route node if found, undefined otherwise
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const tree = extractMachineRoutes(machine);
15
+ * const node = findRouteById(tree, 'dashboard');
16
+ * if (node) {
17
+ * console.log(node.fullPath); // '/dashboard'
18
+ * }
19
+ * ```
20
+ */
21
+ export declare const findRouteById: (tree: RouteTree, id: string) => RouteNode | undefined;
22
+ /**
23
+ * Find route node by URL path
24
+ *
25
+ * Looks up route node using the URL path. Used to get state ID from browser
26
+ * URL for sending play.route events on navigation.
27
+ *
28
+ * When multiple states share the same path (e.g., root and a state both at "/"),
29
+ * prefers routable nodes (with meta.route) over non-routable nodes.
30
+ *
31
+ * Supports pattern matching for dynamic routes (e.g., '/settings/:section?').
32
+ *
33
+ * @param tree - Route tree from extractMachineRoutes
34
+ * @param path - URL path (e.g., '/dashboard', '/settings/profile')
35
+ * @returns Route node if found, undefined otherwise
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const tree = extractMachineRoutes(machine);
40
+ * const node = findRouteByPath(tree, '/dashboard');
41
+ * ```
42
+ */
43
+ export declare const findRouteByPath: (tree: RouteTree, path: string) => RouteNode | undefined;
44
+ //# sourceMappingURL=find-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-route.d.ts","sourceRoot":"","sources":["../src/find-route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAuEvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,EAAE,IAAI,MAAM,KAAG,SAAS,GAAG,SAEvE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,SAAS,EAAE,MAAM,MAAM,KAAG,SAAS,GAAG,SAyB3E,CAAC"}