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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +259 -335
  3. package/dist/base-route-map.d.ts +136 -0
  4. package/dist/base-route-map.d.ts.map +1 -0
  5. package/dist/base-route-map.js +182 -0
  6. package/dist/base-route-map.js.map +1 -0
  7. package/dist/build-tree.d.ts +7 -0
  8. package/dist/build-tree.d.ts.map +1 -1
  9. package/dist/build-tree.js +21 -6
  10. package/dist/build-tree.js.map +1 -1
  11. package/dist/create-route-map-from-tree.d.ts +33 -0
  12. package/dist/create-route-map-from-tree.d.ts.map +1 -0
  13. package/dist/create-route-map-from-tree.js +43 -0
  14. package/dist/create-route-map-from-tree.js.map +1 -0
  15. package/dist/create-route-map.d.ts +29 -33
  16. package/dist/create-route-map.d.ts.map +1 -1
  17. package/dist/create-route-map.js +25 -64
  18. package/dist/create-route-map.js.map +1 -1
  19. package/dist/errors.d.ts +152 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +186 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/extract-routes.d.ts +5 -31
  24. package/dist/extract-routes.d.ts.map +1 -1
  25. package/dist/extract-routes.js +73 -51
  26. package/dist/extract-routes.js.map +1 -1
  27. package/dist/find-route.d.ts +44 -0
  28. package/dist/find-route.d.ts.map +1 -0
  29. package/dist/find-route.js +103 -0
  30. package/dist/find-route.js.map +1 -0
  31. package/dist/index.d.ts +13 -51
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +13 -133
  34. package/dist/index.js.map +1 -1
  35. package/dist/machine-to-graph.d.ts +17 -0
  36. package/dist/machine-to-graph.d.ts.map +1 -0
  37. package/dist/machine-to-graph.js +115 -0
  38. package/dist/machine-to-graph.js.map +1 -0
  39. package/dist/query.d.ts +49 -1
  40. package/dist/query.d.ts.map +1 -1
  41. package/dist/query.js +98 -3
  42. package/dist/query.js.map +1 -1
  43. package/dist/router-bridge-base.d.ts +196 -32
  44. package/dist/router-bridge-base.d.ts.map +1 -1
  45. package/dist/router-bridge-base.js +319 -85
  46. package/dist/router-bridge-base.js.map +1 -1
  47. package/dist/router-sync.d.ts +95 -0
  48. package/dist/router-sync.d.ts.map +1 -0
  49. package/dist/router-sync.js +161 -0
  50. package/dist/router-sync.js.map +1 -0
  51. package/dist/types.d.ts +163 -22
  52. package/dist/types.d.ts.map +1 -1
  53. package/dist/url-pattern-utils.d.ts +117 -0
  54. package/dist/url-pattern-utils.d.ts.map +1 -0
  55. package/dist/url-pattern-utils.js +176 -0
  56. package/dist/url-pattern-utils.js.map +1 -0
  57. package/dist/validate-routes.d.ts +32 -14
  58. package/dist/validate-routes.d.ts.map +1 -1
  59. package/dist/validate-routes.js +28 -21
  60. package/dist/validate-routes.js.map +1 -1
  61. package/package.json +42 -14
  62. package/dist/connect-router.d.ts +0 -56
  63. package/dist/connect-router.d.ts.map +0 -1
  64. package/dist/connect-router.js +0 -119
  65. package/dist/connect-router.js.map +0 -1
  66. package/dist/crawl-machine.d.ts +0 -74
  67. package/dist/crawl-machine.d.ts.map +0 -1
  68. package/dist/crawl-machine.js +0 -95
  69. package/dist/crawl-machine.js.map +0 -1
  70. package/dist/create-browser-history.d.ts +0 -68
  71. package/dist/create-browser-history.d.ts.map +0 -1
  72. package/dist/create-browser-history.js +0 -94
  73. package/dist/create-browser-history.js.map +0 -1
  74. package/dist/create-router.d.ts +0 -73
  75. package/dist/create-router.d.ts.map +0 -1
  76. package/dist/create-router.js +0 -63
  77. package/dist/create-router.js.map +0 -1
  78. package/dist/extract-route.d.ts +0 -25
  79. package/dist/extract-route.d.ts.map +0 -1
  80. package/dist/extract-route.js +0 -63
  81. package/dist/extract-route.js.map +0 -1
@@ -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;AAqCvD;;;;;;;;;;;;;;;;;;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"}
@@ -0,0 +1,103 @@
1
+ import { getURLPatternCtor, getCompiledPattern } from "./url-pattern-utils.js";
2
+ import { URLPatternUnavailableError, InvalidRoutePatternError } from "./errors.js";
3
+ /**
4
+ * Check if a URL path matches a route pattern using URLPattern.
5
+ *
6
+ * For static patterns (no `:param`) falls back to a direct equality check so
7
+ * URLPattern is never required for polyfill-free environments that only use
8
+ * static routes.
9
+ *
10
+ * Uses the shared module-level compiled-pattern cache (`getCompiledPattern`)
11
+ * so repeated lookups never recompile the same pattern, and hyphenated param
12
+ * names (`:cat-id`) are normalized exactly as `RouteMap` does — the same single
13
+ * matching engine underneath.
14
+ *
15
+ * Throws `URLPatternUnavailableError` when a parameterised pattern is tested
16
+ * and URLPattern is not available on `globalThis`.
17
+ * Throws `InvalidRoutePatternError` when URLPattern cannot compile the pattern.
18
+ *
19
+ * Private — not exported from the package public API.
20
+ *
21
+ * @param path - URL path (e.g., '/settings', '/settings/billing')
22
+ * @param pattern - Route pattern (e.g., '/settings/:section?')
23
+ * @returns True if path matches pattern
24
+ */
25
+ function matchesPattern(path, pattern) {
26
+ if (!pattern.includes(":"))
27
+ return path === pattern;
28
+ const Ctor = getURLPatternCtor();
29
+ if (!Ctor)
30
+ throw new URLPatternUnavailableError();
31
+ try {
32
+ return getCompiledPattern(pattern, Ctor).test({ pathname: path });
33
+ }
34
+ catch (err) {
35
+ throw new InvalidRoutePatternError(pattern, { cause: err });
36
+ }
37
+ }
38
+ /**
39
+ * Find route node by state ID
40
+ *
41
+ * Looks up route node using the state's ID property. Used to get URL path
42
+ * from state ID for browser URL sync after play.route transitions.
43
+ *
44
+ * @param tree - Route tree from extractMachineRoutes
45
+ * @param id - State ID (e.g., 'dashboard', 'settings.profile')
46
+ * @returns Route node if found, undefined otherwise
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const tree = extractMachineRoutes(machine);
51
+ * const node = findRouteById(tree, 'dashboard');
52
+ * if (node) {
53
+ * console.log(node.fullPath); // '/dashboard'
54
+ * }
55
+ * ```
56
+ */
57
+ export const findRouteById = (tree, id) => {
58
+ return tree.byStateId.get(id);
59
+ };
60
+ /**
61
+ * Find route node by URL path
62
+ *
63
+ * Looks up route node using the URL path. Used to get state ID from browser
64
+ * URL for sending play.route events on navigation.
65
+ *
66
+ * When multiple states share the same path (e.g., root and a state both at "/"),
67
+ * prefers routable nodes (with meta.route) over non-routable nodes.
68
+ *
69
+ * Supports pattern matching for dynamic routes (e.g., '/settings/:section?').
70
+ *
71
+ * @param tree - Route tree from extractMachineRoutes
72
+ * @param path - URL path (e.g., '/dashboard', '/settings/profile')
73
+ * @returns Route node if found, undefined otherwise
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * const tree = extractMachineRoutes(machine);
78
+ * const node = findRouteByPath(tree, '/dashboard');
79
+ * ```
80
+ */
81
+ export const findRouteByPath = (tree, path) => {
82
+ // 1. Try exact match first (fast path for static routes)
83
+ const node = tree.byPath.get(path);
84
+ if (node?.routable) {
85
+ return node;
86
+ }
87
+ // 2. If exact match not found or non-routable, try pattern matching
88
+ // This handles:
89
+ // - Routes with parameters (e.g., '/settings/:section?')
90
+ // - Multiple states sharing same path (prefer routable)
91
+ for (const candidate of tree.byStateId.values()) {
92
+ if (!candidate.routable) {
93
+ continue; // Skip non-routable nodes
94
+ }
95
+ // Check if path matches this node's pattern
96
+ if (matchesPattern(path, candidate.fullPath)) {
97
+ return candidate;
98
+ }
99
+ }
100
+ // 3. Fallback: if we had a non-routable exact match, return it
101
+ return node;
102
+ };
103
+ //# sourceMappingURL=find-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-route.js","sourceRoot":"","sources":["../src/find-route.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAEnF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAS,cAAc,CAAC,IAAY,EAAE,OAAe;IACpD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,KAAK,OAAO,CAAC;IACpD,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAC;IACjC,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,0BAA0B,EAAE,CAAC;IAClD,IAAI,CAAC;QACJ,OAAO,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,IAAI,wBAAwB,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7D,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;;;;;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,18 @@
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, buildPlayRouteEvent, extractQuery, extractRouteParams, } from "./router-sync.js";
4
+ export type { RouteMatch, BuildPlayRouteEventOptions } from "./router-sync.js";
4
5
  export { validateRouteFormat, validateStateExists, detectDuplicateRoutes, } from "./validate-routes.js";
6
+ export type { ResolvedRoutePath } from "./validate-routes.js";
5
7
  export { buildRouteTree } from "./build-tree.js";
6
8
  export { extractMachineRoutes } from "./extract-routes.js";
7
- export { getNavigableRoutes, getRoutableRoutes, routeExists } from "./query.js";
8
- export type { StateVisit, RouteInfo, RouteNode, RouteTree, RouteObject, RouteMetadata, PlayRouteEvent, RouterBridge, } from "./types.js";
9
- export { createRouteMap, type RouteMap } from "./create-route-map.js";
10
- export { createBrowserHistory, type BrowserHistory } from "./create-browser-history.js";
11
- export { createRouter, type VanillaRouter } from "./create-router.js";
12
- 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;
9
+ export { getNavigableRoutes, getRoutableRoutes, routeExists, getTransitionReachableRoutes, isRouteReachable, } from "./query.js";
10
+ export { machineToGraph } from "./machine-to-graph.js";
11
+ export type { MachineGraph } from "./machine-to-graph.js";
12
+ export type { RouteInfo, RouteNode, RouteTree, RouteObject, RouteMetadata, PlayRouteEvent, RoutableActor, PlayActor, RouterBridge, MachineNodeData, MachineEdgeData, WindowLike, LocationLike, } from "./types.js";
13
+ export { RouteMap, type RouteMapping, type RouteMapping as BaseRouteMapping, } from "./base-route-map.js";
14
+ export { createRouteMap } from "./create-route-map.js";
15
+ export type { RouteMapOptions } from "./create-route-map.js";
16
+ export { createRouteMapFromTree } from "./create-route-map-from-tree.js";
17
+ export { findRouteById, findRouteByPath } from "./find-route.js";
56
18
  //# 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,WAAW,EACX,aAAa,EACb,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,EACN,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAG/E,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,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,aAAa,EACb,SAAS,EACT,YAAY,EACZ,eAAe,EACf,eAAe,EACf,UAAU,EACV,YAAY,GACZ,MAAM,YAAY,CAAC;AAIpB,OAAO,EACN,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,YAAY,IAAI,gBAAgB,GACrC,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAGzE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,141 +1,21 @@
1
1
  // RouterBridgeBase — public API for community adapters
2
2
  export { RouterBridgeBase } from "./router-bridge-base.js";
3
+ // Router sync utilities — used by play-dom-router and adapters that bypass syncActorFromRouter()
4
+ export { sanitizePathname, buildPlayRouteEvent, extractQuery, extractRouteParams, } 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
+ // Shared bidirectional route mapping base class.
13
+ // RouteMapping is exported directly; BaseRouteMapping kept as alias for compatibility.
14
+ export { RouteMap, } from "./base-route-map.js";
15
+ // Factories: create a RouteMap from a machine or a route tree.
16
+ // All RouterBridgeBase adapters use these instead of rolling their own factories.
11
17
  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)
15
- export { createRouter } from "./create-router.js";
16
- // New: Pure browser integration (low-level API)
17
- 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
- };
18
+ export { createRouteMapFromTree } from "./create-route-map-from-tree.js";
19
+ // Route lookup helpers — matchesPattern is private within find-route.ts
20
+ export { findRouteById, findRouteByPath } from "./find-route.js";
141
21
  //# 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,EACN,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,6BAA6B;AAC7B,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACrB,MAAM,sBAAsB,CAAC;AAE9B,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;AAmBvD,iDAAiD;AACjD,uFAAuF;AACvF,OAAO,EACN,QAAQ,GAGR,MAAM,qBAAqB,CAAC;AAE7B,+DAA+D;AAC/D,kFAAkF;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,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;AAGnE;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;AAqDnE,eAAO,MAAM,cAAc,GAAI,SAAS,eAAe,KAAG,YAwEzD,CAAC"}
@@ -0,0 +1,115 @@
1
+ import { toDirectedGraph } from "xstate/graph";
2
+ import { createGraph } from "@statelyai/graph";
3
+ import { UnknownStateTypeError } from "./errors.js";
4
+ /**
5
+ * Convert an XState v5 state machine to a typed @statelyai/graph Graph.
6
+ *
7
+ * The conversion pipeline is:
8
+ * `XState Machine → toDirectedGraph() → DirectedGraphNode hierarchy → createGraph() → Graph`
9
+ *
10
+ * The resulting graph captures:
11
+ * - **Hierarchy**: node parentId reflects XState compound/parallel nesting
12
+ * - **Edges**: all transitions with event types and optional guard types
13
+ * - **Metadata**: state type, meta object, and extracted route path
14
+ *
15
+ * @param machine - XState v5 state machine (any shape)
16
+ * @returns Typed directed graph with MachineNodeData on nodes and MachineEdgeData on edges
17
+ *
18
+ * @example
19
+ * Convert a simple machine and query its routes
20
+ * ```typescript
21
+ * import { createMachine } from "xstate";
22
+ * import { getChildren, getSuccessors } from "@statelyai/graph";
23
+ * import { machineToGraph } from "@xmachines/play-router";
24
+ *
25
+ * const machine = createMachine({
26
+ * id: "app",
27
+ * initial: "home",
28
+ * states: {
29
+ * home: { meta: { route: "/home" }, on: { GO_ABOUT: "about" } },
30
+ * about: { meta: { route: "/about" } },
31
+ * },
32
+ * });
33
+ *
34
+ * const graph = machineToGraph(machine);
35
+ * const children = getChildren(graph, "app"); // [home, about]
36
+ * const successors = getSuccessors(graph, "app.home"); // [about]
37
+ * ```
38
+ *
39
+ * @see {@link MachineNodeData} for node data shape
40
+ * @see {@link MachineEdgeData} for edge data shape
41
+ * @see https://github.com/statelyai/graph for @statelyai/graph API reference
42
+ */
43
+ /**
44
+ * Set of valid XState state types for runtime validation.
45
+ * Guards against future XState changes or unexpected machine shapes.
46
+ */
47
+ const VALID_STATE_TYPES = new Set([
48
+ "atomic",
49
+ "compound",
50
+ "parallel",
51
+ "final",
52
+ "history",
53
+ ]);
54
+ export const machineToGraph = (machine) => {
55
+ const dg = toDirectedGraph(machine);
56
+ // Flatten DirectedGraphNode hierarchy into flat node/edge arrays
57
+ const nodes = [];
58
+ const edges = [];
59
+ let edgeCounter = 0;
60
+ const walk = (dgNode, parentId) => {
61
+ const sn = dgNode.stateNode;
62
+ const meta = sn.meta;
63
+ const metaRoute = meta?.["route"];
64
+ const route = typeof metaRoute === "string"
65
+ ? metaRoute
66
+ : typeof metaRoute === "object" && metaRoute !== null && "path" in metaRoute
67
+ ? metaRoute.path
68
+ : undefined;
69
+ // Runtime assertion: validate state type before casting
70
+ if (!VALID_STATE_TYPES.has(sn.type)) {
71
+ throw new UnknownStateTypeError(sn.type, sn.id, [...VALID_STATE_TYPES]);
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
+ mode: "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,51 @@ 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
+ * Returned values are the RAW `meta.route` strings from the machine — relative
61
+ * routes (e.g. `"detail"`) are NOT resolved to full paths and cannot be used as
62
+ * `tree.byPath` keys. For resolved `RouteNode`s use `getNavigableRoutes`, which
63
+ * resolves reachable states through `tree.byStateId`.
64
+ *
65
+ * @param graph - Machine graph from RouteTree.graph
66
+ * @param stateId - Current state ID (e.g., "test.home")
67
+ * @returns Array of raw route strings reachable via transitions
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const tree = extractMachineRoutes(machine);
72
+ * if (tree.graph) {
73
+ * const reachable = getTransitionReachableRoutes(tree.graph, 'auth.loggedIn');
74
+ * // ['/dashboard', '/settings'] — routes reachable via transitions
75
+ * }
76
+ * ```
77
+ */
78
+ export declare const getTransitionReachableRoutes: (graph: Graph<MachineNodeData, MachineEdgeData>, stateId: string) => string[];
79
+ /**
80
+ * Check if a route is reachable from current state via transitions
81
+ *
82
+ * Uses @statelyai/graph path-finding to determine if there exists
83
+ * a chain of transition edges from the source state to the target state.
84
+ *
85
+ * @param graph - Machine graph from RouteTree.graph
86
+ * @param fromStateId - Source state ID
87
+ * @param toStateId - Target state ID
88
+ * @returns true if a transition path exists, false otherwise
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * const tree = extractMachineRoutes(machine);
93
+ * if (tree.graph) {
94
+ * const canReach = isRouteReachable(tree.graph, 'auth.login', 'auth.dashboard');
95
+ * // true if login → dashboard transition path exists
96
+ * }
97
+ * ```
98
+ */
99
+ export declare const isRouteReachable: (graph: Graph<MachineNodeData, MachineEdgeData>, fromStateId: string, toStateId: string) => boolean;
52
100
  //# 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,EAAa,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAqBzF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,SAAS,EAAE,SAAS,MAAM,KAAG,SAAS,EAyB9E,CAAC;AAgBF;;;;;;;;;;;;;;;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,4BAA4B,GACxC,OAAO,KAAK,CAAC,eAAe,EAAE,eAAe,CAAC,EAC9C,SAAS,MAAM,KACb,MAAM,EAER,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,gBAAgB,GAC5B,OAAO,KAAK,CAAC,eAAe,EAAE,eAAe,CAAC,EAC9C,aAAa,MAAM,EACnB,WAAW,MAAM,KACf,OAKF,CAAC"}