@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
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Check if a URL path matches a route pattern
3
+ *
4
+ * Handles parameter patterns including optional parameters (:param?).
5
+ * Used to match incoming URLs against route patterns with dynamic segments.
6
+ *
7
+ * Private — not exported from the package public API.
8
+ *
9
+ * @param path - URL path (e.g., '/settings', '/settings/billing')
10
+ * @param pattern - Route pattern (e.g., '/settings/:section?')
11
+ * @returns True if path matches pattern
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * matchesPattern('/settings', '/settings/:section?') // true
16
+ * matchesPattern('/settings/billing', '/settings/:section?') // true
17
+ * matchesPattern('/profile/123', '/profile/:userId') // true
18
+ * matchesPattern('/profile', '/profile/:userId') // false (required param missing)
19
+ * ```
20
+ */
21
+ const matchesPattern = (path, pattern) => {
22
+ // No parameters - exact match required
23
+ if (!pattern.includes(":")) {
24
+ return path === pattern;
25
+ }
26
+ const patternSegments = pattern.split("/").filter(Boolean);
27
+ const pathSegments = path.split("/").filter(Boolean);
28
+ // Check each segment
29
+ for (let i = 0; i < patternSegments.length; i++) {
30
+ const patternSegment = patternSegments[i];
31
+ const pathSegment = pathSegments[i];
32
+ // Static segment - must match exactly
33
+ if (!patternSegment.startsWith(":")) {
34
+ if (patternSegment !== pathSegment) {
35
+ return false;
36
+ }
37
+ continue;
38
+ }
39
+ // Parameter segment
40
+ const isOptional = patternSegment.endsWith("?");
41
+ // Optional parameter - can be missing
42
+ if (isOptional && pathSegment === undefined) {
43
+ // Check if there are more path segments after this - if so, no match
44
+ if (i < pathSegments.length) {
45
+ return false;
46
+ }
47
+ continue;
48
+ }
49
+ // Required parameter - must exist
50
+ if (!isOptional && pathSegment === undefined) {
51
+ return false;
52
+ }
53
+ }
54
+ // If path has more segments than pattern, no match
55
+ // (unless all remaining pattern segments are optional - but we don't support that)
56
+ if (pathSegments.length > patternSegments.length) {
57
+ return false;
58
+ }
59
+ return true;
60
+ };
61
+ /**
62
+ * Find route node by state ID
63
+ *
64
+ * Looks up route node using the state's ID property. Used to get URL path
65
+ * from state ID for browser URL sync after play.route transitions.
66
+ *
67
+ * @param tree - Route tree from extractMachineRoutes
68
+ * @param id - State ID (e.g., 'dashboard', 'settings.profile')
69
+ * @returns Route node if found, undefined otherwise
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * const tree = extractMachineRoutes(machine);
74
+ * const node = findRouteById(tree, 'dashboard');
75
+ * if (node) {
76
+ * console.log(node.fullPath); // '/dashboard'
77
+ * }
78
+ * ```
79
+ */
80
+ export const findRouteById = (tree, id) => {
81
+ return tree.byStateId.get(id);
82
+ };
83
+ /**
84
+ * Find route node by URL path
85
+ *
86
+ * Looks up route node using the URL path. Used to get state ID from browser
87
+ * URL for sending play.route events on navigation.
88
+ *
89
+ * When multiple states share the same path (e.g., root and a state both at "/"),
90
+ * prefers routable nodes (with meta.route) over non-routable nodes.
91
+ *
92
+ * Supports pattern matching for dynamic routes (e.g., '/settings/:section?').
93
+ *
94
+ * @param tree - Route tree from extractMachineRoutes
95
+ * @param path - URL path (e.g., '/dashboard', '/settings/profile')
96
+ * @returns Route node if found, undefined otherwise
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * const tree = extractMachineRoutes(machine);
101
+ * const node = findRouteByPath(tree, '/dashboard');
102
+ * ```
103
+ */
104
+ export const findRouteByPath = (tree, path) => {
105
+ // 1. Try exact match first (fast path for static routes)
106
+ const node = tree.byPath.get(path);
107
+ if (node?.routable) {
108
+ return node;
109
+ }
110
+ // 2. If exact match not found or non-routable, try pattern matching
111
+ // This handles:
112
+ // - Routes with parameters (e.g., '/settings/:section?')
113
+ // - Multiple states sharing same path (prefer routable)
114
+ for (const candidate of tree.byStateId.values()) {
115
+ if (!candidate.routable) {
116
+ continue; // Skip non-routable nodes
117
+ }
118
+ // Check if path matches this node's pattern
119
+ if (matchesPattern(path, candidate.fullPath)) {
120
+ return candidate;
121
+ }
122
+ }
123
+ // 3. Fallback: if we had a non-routable exact match, return it
124
+ return node;
125
+ };
126
+ //# sourceMappingURL=find-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-route.js","sourceRoot":"","sources":["../src/find-route.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,OAAe,EAAW,EAAE;IACjE,uCAAuC;IACvC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,KAAK,OAAO,CAAC;IACzB,CAAC;IAED,MAAM,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3D,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAErD,qBAAqB;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACjD,MAAM,cAAc,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;QAEpC,sCAAsC;QACtC,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrC,IAAI,cAAc,KAAK,WAAW,EAAE,CAAC;gBACpC,OAAO,KAAK,CAAC;YACd,CAAC;YACD,SAAS;QACV,CAAC;QAED,oBAAoB;QACpB,MAAM,UAAU,GAAG,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAEhD,sCAAsC;QACtC,IAAI,UAAU,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC7C,qEAAqE;YACrE,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC;gBAC7B,OAAO,KAAK,CAAC;YACd,CAAC;YACD,SAAS;QACV,CAAC;QAED,kCAAkC;QAClC,IAAI,CAAC,UAAU,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9C,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;IAED,mDAAmD;IACnD,mFAAmF;IACnF,IAAI,YAAY,CAAC,MAAM,GAAG,eAAe,CAAC,MAAM,EAAE,CAAC;QAClD,OAAO,KAAK,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC;AACb,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAe,EAAE,EAAU,EAAyB,EAAE;IACnF,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC/B,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,IAAe,EAAE,IAAY,EAAyB,EAAE;IACvF,yDAAyD;IACzD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAEnC,IAAI,IAAI,EAAE,QAAQ,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACb,CAAC;IAED,oEAAoE;IACpE,gBAAgB;IAChB,yDAAyD;IACzD,wDAAwD;IACxD,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;QACjD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;YACzB,SAAS,CAAC,0BAA0B;QACrC,CAAC;QAED,4CAA4C;QAC5C,IAAI,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9C,OAAO,SAAS,CAAC;QAClB,CAAC;IACF,CAAC;IAED,+DAA+D;IAC/D,OAAO,IAAI,CAAC;AACb,CAAC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,56 +1,17 @@
1
1
  export { RouterBridgeBase } from "./router-bridge-base.js";
2
- export { crawlMachine } from "./crawl-machine.js";
3
- export { extractRoute } from "./extract-route.js";
2
+ export type { RouteWatcherHandle } from "./router-bridge-base.js";
3
+ export { sanitizePathname } from "./router-sync.js";
4
4
  export { validateRouteFormat, validateStateExists, detectDuplicateRoutes, } from "./validate-routes.js";
5
5
  export { buildRouteTree } from "./build-tree.js";
6
6
  export { extractMachineRoutes } from "./extract-routes.js";
7
- export { getNavigableRoutes, getRoutableRoutes, routeExists } from "./query.js";
8
- export type { StateVisit, RouteInfo, RouteNode, RouteTree, PlayRouteEvent, RouterBridge, } from "./types.js";
7
+ export { getNavigableRoutes, getRoutableRoutes, routeExists, getTransitionReachableRoutes, isRouteReachable, } from "./query.js";
8
+ export { machineToGraph } from "./machine-to-graph.js";
9
+ export type { MachineGraph } from "./machine-to-graph.js";
10
+ export type { RouteInfo, RouteNode, RouteTree, RouteObject, RouteMetadata, PlayRouteEvent, RouterBridge, MachineNodeData, MachineEdgeData, } from "./types.js";
9
11
  export { createRouteMap, type RouteMap } from "./create-route-map.js";
10
- export { createBrowserHistory, type BrowserHistory } from "./create-browser-history.js";
12
+ export { BaseRouteMap, type RouteMapping as BaseRouteMapping } from "./base-route-map.js";
13
+ export { createBrowserHistory, type BrowserHistory, type BrowserWindow, } from "./create-browser-history.js";
11
14
  export { createRouter, type VanillaRouter } from "./create-router.js";
12
15
  export { connectRouter, type ConnectRouterOptions } from "./connect-router.js";
13
- import type { RouteTree, RouteNode } from "./types.js";
14
- /**
15
- * Find route node by state ID
16
- *
17
- * Looks up route node using the state's ID property. Used to get URL path
18
- * from state ID for browser URL sync after play.route transitions.
19
- *
20
- * @param tree - Route tree from extractMachineRoutes
21
- * @param id - State ID (e.g., 'dashboard', 'settings.profile')
22
- * @returns Route node if found, undefined otherwise
23
- *
24
- * @example
25
- * ```typescript
26
- * const tree = extractMachineRoutes(machine);
27
- * const node = findRouteById(tree, 'dashboard');
28
- * if (node) {
29
- * console.log(node.fullPath); // '/dashboard'
30
- * }
31
- * ```
32
- */
33
- export declare const findRouteById: (tree: RouteTree, id: string) => RouteNode | undefined;
34
- /**
35
- * Find route node by URL path
36
- *
37
- * Looks up route node using the URL path. Used to get state ID from browser
38
- * URL for sending play.route events on navigation.
39
- *
40
- * When multiple states share the same path (e.g., root and a state both at "/"),
41
- * prefers routable nodes (with meta.route) over non-routable nodes.
42
- *
43
- * Supports pattern matching for dynamic routes (e.g., '/settings/:section?').
44
- *
45
- * @param tree - Route tree from extractMachineRoutes
46
- * @param path - URL path (e.g., '/dashboard', '/settings/profile')
47
- * @returns Route node if found, undefined otherwise
48
- *
49
- * @example
50
- * ```typescript
51
- * const tree = extractMachineRoutes(machine);
52
- * const node = findRouteByPath(tree, '/dashboard');
53
- * ```
54
- */
55
- export declare const findRouteByPath: (tree: RouteTree, path: string) => RouteNode | undefined;
16
+ export { findRouteById, findRouteByPath } from "./find-route.js";
56
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG3D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEhF,YAAY,EACX,UAAU,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,cAAc,EACd,YAAY,GACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtE,OAAO,EAAE,oBAAoB,EAAE,KAAK,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAGxF,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGtE,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,EAAE,IAAI,MAAM,KAAG,SAAS,GAAG,SAEvE,CAAC;AAqEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,SAAS,EAAE,MAAM,MAAM,KAAG,SAAS,GAAG,SAyB3E,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,YAAY,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAGlE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,4BAA4B,EAC5B,gBAAgB,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,YAAY,EACX,SAAS,EACT,SAAS,EACT,SAAS,EACT,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,EACZ,eAAe,EACf,eAAe,GACf,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAKtE,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,IAAI,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG1F,OAAO,EACN,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,GAClB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGtE,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAG/E,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,141 +1,26 @@
1
1
  // RouterBridgeBase — public API for community adapters
2
2
  export { RouterBridgeBase } from "./router-bridge-base.js";
3
+ // Router sync utilities — sanitizePathname is used by adapters that bypass syncActorFromRouter()
4
+ export { sanitizePathname } from "./router-sync.js";
3
5
  // Route utilities (existing)
4
- export { crawlMachine } from "./crawl-machine.js";
5
- export { extractRoute } from "./extract-route.js";
6
6
  export { validateRouteFormat, validateStateExists, detectDuplicateRoutes, } from "./validate-routes.js";
7
7
  export { buildRouteTree } from "./build-tree.js";
8
8
  export { extractMachineRoutes } from "./extract-routes.js";
9
- export { getNavigableRoutes, getRoutableRoutes, routeExists } from "./query.js";
10
- // New: Route map for path state ID resolution
9
+ export { getNavigableRoutes, getRoutableRoutes, routeExists, getTransitionReachableRoutes, isRouteReachable, } from "./query.js";
10
+ // Graph adapter converts XState machines to @statelyai/graph Graph
11
+ export { machineToGraph } from "./machine-to-graph.js";
12
+ // Route map for path → state ID resolution (createRouteMap / RouteMap interface)
11
13
  export { createRouteMap } from "./create-route-map.js";
12
- // New: Browser history abstraction (aligned with TanStack Router)
13
- export { createBrowserHistory } from "./create-browser-history.js";
14
- // New: Vanilla router (framework-agnostic)
14
+ // Shared bidirectional route mapping base class.
15
+ // Re-exported as `BaseRouteMapping` (not `RouteMapping`) so adapter packages can
16
+ // define their own local `RouteMapping` type without a name collision.
17
+ export { BaseRouteMap } from "./base-route-map.js";
18
+ // Browser history abstraction (aligned with TanStack Router)
19
+ export { createBrowserHistory, } from "./create-browser-history.js";
20
+ // Vanilla router (framework-agnostic)
15
21
  export { createRouter } from "./create-router.js";
16
- // New: Pure browser integration (low-level API)
22
+ // Pure browser integration (low-level API)
17
23
  export { connectRouter } from "./connect-router.js";
18
- /**
19
- * Find route node by state ID
20
- *
21
- * Looks up route node using the state's ID property. Used to get URL path
22
- * from state ID for browser URL sync after play.route transitions.
23
- *
24
- * @param tree - Route tree from extractMachineRoutes
25
- * @param id - State ID (e.g., 'dashboard', 'settings.profile')
26
- * @returns Route node if found, undefined otherwise
27
- *
28
- * @example
29
- * ```typescript
30
- * const tree = extractMachineRoutes(machine);
31
- * const node = findRouteById(tree, 'dashboard');
32
- * if (node) {
33
- * console.log(node.fullPath); // '/dashboard'
34
- * }
35
- * ```
36
- */
37
- export const findRouteById = (tree, id) => {
38
- return tree.byStateId.get(id);
39
- };
40
- /**
41
- * Check if a URL path matches a route pattern
42
- *
43
- * Handles parameter patterns including optional parameters (:param?).
44
- * Used to match incoming URLs against route patterns with dynamic segments.
45
- *
46
- * @param path - URL path (e.g., '/settings', '/settings/billing')
47
- * @param pattern - Route pattern (e.g., '/settings/:section?')
48
- * @returns True if path matches pattern
49
- *
50
- * @example
51
- * ```typescript
52
- * matchesPattern('/settings', '/settings/:section?') // true
53
- * matchesPattern('/settings/billing', '/settings/:section?') // true
54
- * matchesPattern('/profile/123', '/profile/:userId') // true
55
- * matchesPattern('/profile', '/profile/:userId') // false (required param missing)
56
- * ```
57
- */
58
- const matchesPattern = (path, pattern) => {
59
- // No parameters - exact match required
60
- if (!pattern.includes(":")) {
61
- return path === pattern;
62
- }
63
- const patternSegments = pattern.split("/").filter(Boolean);
64
- const pathSegments = path.split("/").filter(Boolean);
65
- // Check each segment
66
- for (let i = 0; i < patternSegments.length; i++) {
67
- const patternSegment = patternSegments[i];
68
- const pathSegment = pathSegments[i];
69
- // Static segment - must match exactly
70
- if (!patternSegment.startsWith(":")) {
71
- if (patternSegment !== pathSegment) {
72
- return false;
73
- }
74
- continue;
75
- }
76
- // Parameter segment
77
- const isOptional = patternSegment.endsWith("?");
78
- // Optional parameter - can be missing
79
- if (isOptional && pathSegment === undefined) {
80
- // Check if there are more path segments after this - if so, no match
81
- if (i < pathSegments.length) {
82
- return false;
83
- }
84
- continue;
85
- }
86
- // Required parameter - must exist
87
- if (!isOptional && pathSegment === undefined) {
88
- return false;
89
- }
90
- }
91
- // If path has more segments than pattern, no match
92
- // (unless all remaining pattern segments are optional - but we don't support that)
93
- if (pathSegments.length > patternSegments.length) {
94
- return false;
95
- }
96
- return true;
97
- };
98
- /**
99
- * Find route node by URL path
100
- *
101
- * Looks up route node using the URL path. Used to get state ID from browser
102
- * URL for sending play.route events on navigation.
103
- *
104
- * When multiple states share the same path (e.g., root and a state both at "/"),
105
- * prefers routable nodes (with meta.route) over non-routable nodes.
106
- *
107
- * Supports pattern matching for dynamic routes (e.g., '/settings/:section?').
108
- *
109
- * @param tree - Route tree from extractMachineRoutes
110
- * @param path - URL path (e.g., '/dashboard', '/settings/profile')
111
- * @returns Route node if found, undefined otherwise
112
- *
113
- * @example
114
- * ```typescript
115
- * const tree = extractMachineRoutes(machine);
116
- * const node = findRouteByPath(tree, '/dashboard');
117
- * ```
118
- */
119
- export const findRouteByPath = (tree, path) => {
120
- // 1. Try exact match first (fast path for static routes)
121
- const node = tree.byPath.get(path);
122
- if (node?.routable) {
123
- return node;
124
- }
125
- // 2. If exact match not found or non-routable, try pattern matching
126
- // This handles:
127
- // - Routes with parameters (e.g., '/settings/:section?')
128
- // - Multiple states sharing same path (prefer routable)
129
- for (const candidate of tree.byStateId.values()) {
130
- if (!candidate.routable) {
131
- continue; // Skip non-routable nodes
132
- }
133
- // Check if path matches this node's pattern
134
- if (matchesPattern(path, candidate.fullPath)) {
135
- return candidate;
136
- }
137
- }
138
- // 3. Fallback: if we had a non-routable exact match, return it
139
- return node;
140
- };
24
+ // Route lookup helpers — matchesPattern is private within find-route.ts
25
+ export { findRouteById, findRouteByPath } from "./find-route.js";
141
26
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG3D,iGAAiG;AACjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,6BAA6B;AAC7B,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,4BAA4B,EAC5B,gBAAgB,GAChB,MAAM,YAAY,CAAC;AAEpB,qEAAqE;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAevD,iFAAiF;AACjF,OAAO,EAAE,cAAc,EAAiB,MAAM,uBAAuB,CAAC;AAEtE,iDAAiD;AACjD,iFAAiF;AACjF,uEAAuE;AACvE,OAAO,EAAE,YAAY,EAAyC,MAAM,qBAAqB,CAAC;AAE1F,6DAA6D;AAC7D,OAAO,EACN,oBAAoB,GAGpB,MAAM,6BAA6B,CAAC;AAErC,sCAAsC;AACtC,OAAO,EAAE,YAAY,EAAsB,MAAM,oBAAoB,CAAC;AAEtE,2CAA2C;AAC3C,OAAO,EAAE,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAE/E,wEAAwE;AACxE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { AnyStateMachine } from "xstate";
2
+ import { type Graph } from "@statelyai/graph";
3
+ import type { MachineNodeData, MachineEdgeData } from "./types.js";
4
+ /**
5
+ * Typed @statelyai/graph Graph representing a state machine.
6
+ * Provides hierarchy queries, traversal algorithms, and reachability checks.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { getChildren, getSuccessors, hasPath } from "@statelyai/graph";
11
+ * const graph: MachineGraph = machineToGraph(machine);
12
+ * const children = getChildren(graph, "myMachine");
13
+ * ```
14
+ */
15
+ export type MachineGraph = Graph<MachineNodeData, MachineEdgeData>;
16
+ export declare const machineToGraph: (machine: AnyStateMachine) => MachineGraph;
17
+ //# sourceMappingURL=machine-to-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-to-graph.d.ts","sourceRoot":"","sources":["../src/machine-to-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAe,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEnE;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;AAqDnE,eAAO,MAAM,cAAc,GAAI,SAAS,eAAe,KAAG,YA2EzD,CAAC"}
@@ -0,0 +1,115 @@
1
+ import { toDirectedGraph } from "xstate/graph";
2
+ import { createGraph } from "@statelyai/graph";
3
+ /**
4
+ * Convert an XState v5 state machine to a typed @statelyai/graph Graph.
5
+ *
6
+ * The conversion pipeline is:
7
+ * `XState Machine → toDirectedGraph() → DirectedGraphNode hierarchy → createGraph() → Graph`
8
+ *
9
+ * The resulting graph captures:
10
+ * - **Hierarchy**: node parentId reflects XState compound/parallel nesting
11
+ * - **Edges**: all transitions with event types and optional guard types
12
+ * - **Metadata**: state type, meta object, and extracted route path
13
+ *
14
+ * @param machine - XState v5 state machine (any shape)
15
+ * @returns Typed directed graph with MachineNodeData on nodes and MachineEdgeData on edges
16
+ *
17
+ * @example
18
+ * Convert a simple machine and query its routes
19
+ * ```typescript
20
+ * import { createMachine } from "xstate";
21
+ * import { getChildren, getSuccessors } from "@statelyai/graph";
22
+ * import { machineToGraph } from "@xmachines/play-router";
23
+ *
24
+ * const machine = createMachine({
25
+ * id: "app",
26
+ * initial: "home",
27
+ * states: {
28
+ * home: { meta: { route: "/home" }, on: { GO_ABOUT: "about" } },
29
+ * about: { meta: { route: "/about" } },
30
+ * },
31
+ * });
32
+ *
33
+ * const graph = machineToGraph(machine);
34
+ * const children = getChildren(graph, "app"); // [home, about]
35
+ * const successors = getSuccessors(graph, "app.home"); // [about]
36
+ * ```
37
+ *
38
+ * @see {@link MachineNodeData} for node data shape
39
+ * @see {@link MachineEdgeData} for edge data shape
40
+ * @see https://github.com/statelyai/graph for @statelyai/graph API reference
41
+ */
42
+ /**
43
+ * Set of valid XState state types for runtime validation.
44
+ * Guards against future XState changes or unexpected machine shapes.
45
+ */
46
+ const VALID_STATE_TYPES = new Set([
47
+ "atomic",
48
+ "compound",
49
+ "parallel",
50
+ "final",
51
+ "history",
52
+ ]);
53
+ export const machineToGraph = (machine) => {
54
+ const dg = toDirectedGraph(machine);
55
+ // Flatten DirectedGraphNode hierarchy into flat node/edge arrays
56
+ const nodes = [];
57
+ const edges = [];
58
+ let edgeCounter = 0;
59
+ const walk = (dgNode, parentId) => {
60
+ const sn = dgNode.stateNode;
61
+ const meta = sn.meta;
62
+ const metaRoute = meta?.["route"];
63
+ const route = typeof metaRoute === "string"
64
+ ? metaRoute
65
+ : typeof metaRoute === "object" && metaRoute !== null && "path" in metaRoute
66
+ ? metaRoute.path
67
+ : undefined;
68
+ // Runtime assertion: validate state type before casting
69
+ if (!VALID_STATE_TYPES.has(sn.type)) {
70
+ throw new Error(`Unknown XState state type "${sn.type}" on node "${sn.id}". ` +
71
+ `Expected one of: ${[...VALID_STATE_TYPES].join(", ")}.`);
72
+ }
73
+ const nodeData = {
74
+ stateId: sn.id,
75
+ type: sn.type,
76
+ };
77
+ if (meta !== undefined)
78
+ nodeData.meta = meta;
79
+ if (route !== undefined)
80
+ nodeData.route = route;
81
+ if (parentId !== undefined) {
82
+ nodes.push({ id: dgNode.id, parentId, data: nodeData });
83
+ }
84
+ else {
85
+ nodes.push({ id: dgNode.id, data: nodeData });
86
+ }
87
+ // Collect edges from THIS node (edges are on source node, not graph root)
88
+ for (const edge of dgNode.edges) {
89
+ const edgeData = {
90
+ eventType: edge.label.text,
91
+ };
92
+ if (edge.transition.guard) {
93
+ edgeData.guardType = String(edge.transition.guard);
94
+ }
95
+ edges.push({
96
+ id: `e${edgeCounter++}`,
97
+ sourceId: edge.source.id,
98
+ targetId: edge.target.id,
99
+ data: edgeData,
100
+ });
101
+ }
102
+ // Recurse into children with current node as parent
103
+ for (const child of dgNode.children) {
104
+ walk(child, dgNode.id);
105
+ }
106
+ };
107
+ walk(dg);
108
+ return createGraph({
109
+ type: "directed",
110
+ initialNodeId: dg.id,
111
+ nodes,
112
+ edges,
113
+ });
114
+ };
115
+ //# sourceMappingURL=machine-to-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-to-graph.js","sourceRoot":"","sources":["../src/machine-to-graph.ts"],"names":[],"mappings":"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"}
package/dist/query.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { RouteTree, RouteNode } from "./types.js";
1
+ import type { Graph } from "@statelyai/graph";
2
+ import type { RouteTree, RouteNode, MachineNodeData, MachineEdgeData } from "./types.js";
2
3
  /**
3
4
  * Get all routes navigable from given state
4
5
  *
@@ -49,4 +50,46 @@ export declare const getRoutableRoutes: (tree: RouteTree) => RouteNode[];
49
50
  * @returns true if path exists, false otherwise
50
51
  */
51
52
  export declare const routeExists: (tree: RouteTree, path: string) => boolean;
53
+ /**
54
+ * Get routes reachable via transitions from current state
55
+ *
56
+ * Uses the @statelyai/graph successor algorithm to find all states
57
+ * directly reachable via transition edges from the given state,
58
+ * then filters to those with defined routes.
59
+ *
60
+ * @param graph - Machine graph from RouteTree.graph
61
+ * @param stateId - Current state ID (e.g., "test.home")
62
+ * @returns Array of route paths reachable via transitions
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const tree = extractMachineRoutes(machine);
67
+ * if (tree.graph) {
68
+ * const reachable = getTransitionReachableRoutes(tree.graph, 'auth.loggedIn');
69
+ * // ['/dashboard', '/settings'] — routes reachable via transitions
70
+ * }
71
+ * ```
72
+ */
73
+ export declare const getTransitionReachableRoutes: (graph: Graph<MachineNodeData, MachineEdgeData>, stateId: string) => string[];
74
+ /**
75
+ * Check if a route is reachable from current state via transitions
76
+ *
77
+ * Uses @statelyai/graph path-finding to determine if there exists
78
+ * a chain of transition edges from the source state to the target state.
79
+ *
80
+ * @param graph - Machine graph from RouteTree.graph
81
+ * @param fromStateId - Source state ID
82
+ * @param toStateId - Target state ID
83
+ * @returns true if a transition path exists, false otherwise
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const tree = extractMachineRoutes(machine);
88
+ * if (tree.graph) {
89
+ * const canReach = isRouteReachable(tree.graph, 'auth.login', 'auth.dashboard');
90
+ * // true if login → dashboard transition path exists
91
+ * }
92
+ * ```
93
+ */
94
+ export declare const isRouteReachable: (graph: Graph<MachineNodeData, MachineEdgeData>, fromStateId: string, toStateId: string) => boolean;
52
95
  //# sourceMappingURL=query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,SAAS,EAAE,SAAS,MAAM,KAAG,SAAS,EAO9E,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,SAAS,KAAG,SAAS,EAW5D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,EAAE,MAAM,MAAM,KAAG,OAE3D,CAAC"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEzF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,SAAS,EAAE,SAAS,MAAM,KAAG,SAAS,EAsB9E,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,SAAS,KAAG,SAAS,EAW5D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,EAAE,MAAM,MAAM,KAAG,OAE3D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,4BAA4B,GACxC,OAAO,KAAK,CAAC,eAAe,EAAE,eAAe,CAAC,EAC9C,SAAS,MAAM,KACb,MAAM,EAWR,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,gBAAgB,GAC5B,OAAO,KAAK,CAAC,eAAe,EAAE,eAAe,CAAC,EAC9C,aAAa,MAAM,EACnB,WAAW,MAAM,KACf,OASF,CAAC"}