@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.
- package/README.md +169 -47
- package/dist/base-route-map.d.ts +116 -0
- package/dist/base-route-map.d.ts.map +1 -0
- package/dist/base-route-map.js +206 -0
- package/dist/base-route-map.js.map +1 -0
- package/dist/build-tree.d.ts.map +1 -1
- package/dist/build-tree.js +6 -5
- package/dist/build-tree.js.map +1 -1
- package/dist/connect-router.d.ts.map +1 -1
- package/dist/connect-router.js +35 -45
- package/dist/connect-router.js.map +1 -1
- package/dist/create-browser-history.d.ts +38 -5
- package/dist/create-browser-history.d.ts.map +1 -1
- package/dist/create-browser-history.js +43 -17
- package/dist/create-browser-history.js.map +1 -1
- package/dist/create-route-map.d.ts +21 -1
- package/dist/create-route-map.d.ts.map +1 -1
- package/dist/create-route-map.js +73 -22
- package/dist/create-route-map.js.map +1 -1
- package/dist/errors.d.ts +75 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +85 -0
- package/dist/errors.js.map +1 -0
- package/dist/extract-routes.d.ts +5 -31
- package/dist/extract-routes.d.ts.map +1 -1
- package/dist/extract-routes.js +70 -49
- package/dist/extract-routes.js.map +1 -1
- package/dist/find-route.d.ts +44 -0
- package/dist/find-route.d.ts.map +1 -0
- package/dist/find-route.js +126 -0
- package/dist/find-route.js.map +1 -0
- package/dist/index.d.ts +9 -48
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -131
- package/dist/index.js.map +1 -1
- package/dist/machine-to-graph.d.ts +17 -0
- package/dist/machine-to-graph.d.ts.map +1 -0
- package/dist/machine-to-graph.js +115 -0
- package/dist/machine-to-graph.js.map +1 -0
- package/dist/query.d.ts +44 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +80 -3
- package/dist/query.js.map +1 -1
- package/dist/router-bridge-base.d.ts +49 -19
- package/dist/router-bridge-base.d.ts.map +1 -1
- package/dist/router-bridge-base.js +120 -56
- package/dist/router-bridge-base.js.map +1 -1
- package/dist/router-sync.d.ts +62 -0
- package/dist/router-sync.d.ts.map +1 -0
- package/dist/router-sync.js +87 -0
- package/dist/router-sync.js.map +1 -0
- package/dist/types.d.ts +73 -14
- package/dist/types.d.ts.map +1 -1
- package/dist/validate-routes.d.ts +9 -9
- package/dist/validate-routes.d.ts.map +1 -1
- package/dist/validate-routes.js +12 -11
- package/dist/validate-routes.js.map +1 -1
- package/package.json +36 -18
- package/dist/crawl-machine.d.ts +0 -74
- package/dist/crawl-machine.d.ts.map +0 -1
- package/dist/crawl-machine.js +0 -95
- package/dist/crawl-machine.js.map +0 -1
- package/dist/extract-route.d.ts +0 -25
- package/dist/extract-route.d.ts.map +0 -1
- package/dist/extract-route.js +0 -63
- 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 {
|
|
3
|
-
export {
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,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
|
-
//
|
|
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
|
-
//
|
|
13
|
-
|
|
14
|
-
//
|
|
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
|
-
//
|
|
22
|
+
// Pure browser integration (low-level API)
|
|
17
23
|
export { connectRouter } from "./connect-router.js";
|
|
18
|
-
|
|
19
|
-
|
|
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":"AAAA,uDAAuD;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;
|
|
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 {
|
|
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
|
package/dist/query.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|