@xmachines/play-router 1.0.0-beta.1

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 (141) hide show
  1. package/.oxfmtrc.json +3 -0
  2. package/.oxlintrc.json +3 -0
  3. package/README.md +436 -0
  4. package/coverage/base.css +224 -0
  5. package/coverage/block-navigation.js +87 -0
  6. package/coverage/build-tree.ts.html +316 -0
  7. package/coverage/connect-router.ts.html +505 -0
  8. package/coverage/coverage-summary.json +15 -0
  9. package/coverage/crawl-machine.ts.html +385 -0
  10. package/coverage/create-browser-history.ts.html +556 -0
  11. package/coverage/create-route-map.ts.html +400 -0
  12. package/coverage/create-router.ts.html +328 -0
  13. package/coverage/extract-route.ts.html +322 -0
  14. package/coverage/extract-routes.ts.html +286 -0
  15. package/coverage/favicon.png +0 -0
  16. package/coverage/index.html +296 -0
  17. package/coverage/index.ts.html +610 -0
  18. package/coverage/prettify.css +1 -0
  19. package/coverage/prettify.js +2 -0
  20. package/coverage/query.ts.html +307 -0
  21. package/coverage/router-bridge-base.ts.html +919 -0
  22. package/coverage/sort-arrow-sprite.png +0 -0
  23. package/coverage/sorter.js +210 -0
  24. package/coverage/types.ts.html +787 -0
  25. package/coverage/validate-routes.ts.html +319 -0
  26. package/dist/build-tree.d.ts +13 -0
  27. package/dist/build-tree.d.ts.map +1 -0
  28. package/dist/build-tree.js +67 -0
  29. package/dist/build-tree.js.map +1 -0
  30. package/dist/connect-router.d.ts +56 -0
  31. package/dist/connect-router.d.ts.map +1 -0
  32. package/dist/connect-router.js +119 -0
  33. package/dist/connect-router.js.map +1 -0
  34. package/dist/crawl-machine.d.ts +74 -0
  35. package/dist/crawl-machine.d.ts.map +1 -0
  36. package/dist/crawl-machine.js +95 -0
  37. package/dist/crawl-machine.js.map +1 -0
  38. package/dist/create-browser-history.d.ts +68 -0
  39. package/dist/create-browser-history.d.ts.map +1 -0
  40. package/dist/create-browser-history.js +94 -0
  41. package/dist/create-browser-history.js.map +1 -0
  42. package/dist/create-route-map.d.ts +46 -0
  43. package/dist/create-route-map.d.ts.map +1 -0
  44. package/dist/create-route-map.js +73 -0
  45. package/dist/create-route-map.js.map +1 -0
  46. package/dist/create-router.d.ts +73 -0
  47. package/dist/create-router.d.ts.map +1 -0
  48. package/dist/create-router.js +63 -0
  49. package/dist/create-router.js.map +1 -0
  50. package/dist/extract-route.d.ts +25 -0
  51. package/dist/extract-route.d.ts.map +1 -0
  52. package/dist/extract-route.js +63 -0
  53. package/dist/extract-route.js.map +1 -0
  54. package/dist/extract-routes.d.ts +41 -0
  55. package/dist/extract-routes.d.ts.map +1 -0
  56. package/dist/extract-routes.js +61 -0
  57. package/dist/extract-routes.js.map +1 -0
  58. package/dist/index.d.ts +56 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +141 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/query.d.ts +52 -0
  63. package/dist/query.d.ts.map +1 -0
  64. package/dist/query.js +69 -0
  65. package/dist/query.js.map +1 -0
  66. package/dist/router-bridge-base.d.ts +150 -0
  67. package/dist/router-bridge-base.d.ts.map +1 -0
  68. package/dist/router-bridge-base.js +240 -0
  69. package/dist/router-bridge-base.js.map +1 -0
  70. package/dist/types.d.ts +228 -0
  71. package/dist/types.d.ts.map +1 -0
  72. package/dist/types.js +2 -0
  73. package/dist/types.js.map +1 -0
  74. package/dist/validate-routes.d.ts +39 -0
  75. package/dist/validate-routes.d.ts.map +1 -0
  76. package/dist/validate-routes.js +65 -0
  77. package/dist/validate-routes.js.map +1 -0
  78. package/examples/demo/README.md +127 -0
  79. package/examples/demo/index.html +41 -0
  80. package/examples/demo/package.json +27 -0
  81. package/examples/demo/src/main.ts +28 -0
  82. package/examples/demo/src/router.ts +37 -0
  83. package/examples/demo/src/shell.ts +316 -0
  84. package/examples/demo/test/browser/auth-flow.browser.test.ts +60 -0
  85. package/examples/demo/test/browser/startup.browser.test.ts +37 -0
  86. package/examples/demo/test/library-pattern.test.ts +51 -0
  87. package/examples/demo/tsconfig.json +17 -0
  88. package/examples/demo/vite.config.ts +7 -0
  89. package/examples/demo/vitest.browser.config.ts +20 -0
  90. package/examples/demo/vitest.config.ts +9 -0
  91. package/examples/shared/dist/auth-machine.d.ts +20 -0
  92. package/examples/shared/dist/auth-machine.d.ts.map +1 -0
  93. package/examples/shared/dist/auth-machine.js +212 -0
  94. package/examples/shared/dist/auth-machine.js.map +1 -0
  95. package/examples/shared/dist/catalog.d.ts +85 -0
  96. package/examples/shared/dist/catalog.d.ts.map +1 -0
  97. package/examples/shared/dist/catalog.js +86 -0
  98. package/examples/shared/dist/catalog.js.map +1 -0
  99. package/examples/shared/dist/index.d.ts +4 -0
  100. package/examples/shared/dist/index.d.ts.map +1 -0
  101. package/examples/shared/dist/index.js +3 -0
  102. package/examples/shared/dist/index.js.map +1 -0
  103. package/examples/shared/package.json +37 -0
  104. package/examples/shared/src/auth-machine.ts +234 -0
  105. package/examples/shared/src/catalog.ts +95 -0
  106. package/examples/shared/src/index.css +3 -0
  107. package/examples/shared/src/index.ts +3 -0
  108. package/examples/shared/src/styles/layout.css +413 -0
  109. package/examples/shared/src/styles/reset.css +42 -0
  110. package/examples/shared/src/styles/tokens.css +183 -0
  111. package/examples/shared/tsconfig.json +14 -0
  112. package/examples/shared/tsconfig.tsbuildinfo +1 -0
  113. package/package.json +44 -0
  114. package/src/build-tree.ts +77 -0
  115. package/src/connect-router.ts +142 -0
  116. package/src/crawl-machine.ts +100 -0
  117. package/src/create-browser-history.ts +157 -0
  118. package/src/create-route-map.ts +105 -0
  119. package/src/create-router.ts +87 -0
  120. package/src/extract-route.ts +79 -0
  121. package/src/extract-routes.ts +67 -0
  122. package/src/index.ts +175 -0
  123. package/src/query.ts +74 -0
  124. package/src/router-bridge-base.ts +279 -0
  125. package/src/types.ts +234 -0
  126. package/src/validate-routes.ts +76 -0
  127. package/test/connect-route-map.test.ts +320 -0
  128. package/test/crawl-extract.test.js +473 -0
  129. package/test/create-browser-history.test.ts +123 -0
  130. package/test/create-router.test.ts +23 -0
  131. package/test/extract-routes.test.ts +80 -0
  132. package/test/find-route-by-path-patterns.test.ts +69 -0
  133. package/test/integration.test.js +438 -0
  134. package/test/query.test.ts +56 -0
  135. package/test/router-bridge-base-edge.test.ts +165 -0
  136. package/test/router-bridge-base.test.ts +119 -0
  137. package/test/tree-query.test.js +692 -0
  138. package/test/validation.test.js +158 -0
  139. package/tsconfig.json +14 -0
  140. package/tsconfig.tsbuildinfo +1 -0
  141. package/vitest.config.ts +35 -0
@@ -0,0 +1,74 @@
1
+ import type { AnyStateMachine } from "xstate";
2
+ import type { StateVisit } from "./types.js";
3
+ /**
4
+ * Crawl state machine graph using breadth-first traversal
5
+ *
6
+ * Visits all state nodes in the machine, including deeply nested states, and returns
7
+ * complete list of visits with path and parent information. This enables systematic
8
+ * discovery of all states for route extraction and tree building.
9
+ *
10
+ * **Architectural Context:** Implements **Actor Authority (INV-01)** by extracting
11
+ * routing information from the state machine definition rather than external configuration.
12
+ * The BFS traversal ensures all nested states are discovered, enabling the Actor to
13
+ * define the complete navigation structure through its machine definition.
14
+ *
15
+ * @param machine - XState v5 state machine to crawl
16
+ * @returns Array of state visits in breadth-first order
17
+ *
18
+ * @example
19
+ * Basic machine crawl
20
+ * ```typescript
21
+ * import { createMachine } from "xstate";
22
+ * import { crawlMachine } from "@xmachines/play-router";
23
+ *
24
+ * const machine = createMachine({
25
+ * initial: 'home',
26
+ * states: {
27
+ * home: {},
28
+ * dashboard: {
29
+ * initial: 'overview',
30
+ * states: {
31
+ * overview: {},
32
+ * settings: {}
33
+ * }
34
+ * }
35
+ * }
36
+ * });
37
+ *
38
+ * const visits = crawlMachine(machine);
39
+ * // Returns visits for: root, home, dashboard, dashboard.overview, dashboard.settings
40
+ * console.log(visits.map(v => v.path.join('.')));
41
+ * // ['', 'home', 'dashboard', 'dashboard.overview', 'dashboard.settings']
42
+ * ```
43
+ *
44
+ * @example
45
+ * Extract state paths and parents
46
+ * ```typescript
47
+ * import { crawlMachine } from "@xmachines/play-router";
48
+ *
49
+ * const visits = crawlMachine(machine);
50
+ * visits.forEach(visit => {
51
+ * console.log({
52
+ * path: visit.path.join('.') || 'root',
53
+ * hasParent: !!visit.parent,
54
+ * hasChildren: Object.keys(visit.node.states).length > 0
55
+ * });
56
+ * });
57
+ * ```
58
+ *
59
+ * @see {@link https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md | RFC Play v1}
60
+ * @see {@link extractRoute} for extracting route from individual states
61
+ * @see {@link extractMachineRoutes} for complete route extraction
62
+ * @see {@link StateVisit} for visit structure
63
+ *
64
+ * @remarks
65
+ * **BFS Traversal:** Breadth-first search ensures systematic discovery of all states
66
+ * at each nesting level before descending deeper. This produces a predictable ordering
67
+ * useful for route tree construction and debugging.
68
+ *
69
+ * **Path Format:** Paths use array notation (`['dashboard', 'settings']`) which can be
70
+ * joined with dots for state IDs (`'dashboard.settings'`) matching XState's hierarchical
71
+ * state naming convention.
72
+ */
73
+ export declare const crawlMachine: (machine: AnyStateMachine) => StateVisit[];
74
+ //# sourceMappingURL=crawl-machine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crawl-machine.d.ts","sourceRoot":"","sources":["../src/crawl-machine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,eAAe,KAAG,UAAU,EA0BjE,CAAC"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Crawl state machine graph using breadth-first traversal
3
+ *
4
+ * Visits all state nodes in the machine, including deeply nested states, and returns
5
+ * complete list of visits with path and parent information. This enables systematic
6
+ * discovery of all states for route extraction and tree building.
7
+ *
8
+ * **Architectural Context:** Implements **Actor Authority (INV-01)** by extracting
9
+ * routing information from the state machine definition rather than external configuration.
10
+ * The BFS traversal ensures all nested states are discovered, enabling the Actor to
11
+ * define the complete navigation structure through its machine definition.
12
+ *
13
+ * @param machine - XState v5 state machine to crawl
14
+ * @returns Array of state visits in breadth-first order
15
+ *
16
+ * @example
17
+ * Basic machine crawl
18
+ * ```typescript
19
+ * import { createMachine } from "xstate";
20
+ * import { crawlMachine } from "@xmachines/play-router";
21
+ *
22
+ * const machine = createMachine({
23
+ * initial: 'home',
24
+ * states: {
25
+ * home: {},
26
+ * dashboard: {
27
+ * initial: 'overview',
28
+ * states: {
29
+ * overview: {},
30
+ * settings: {}
31
+ * }
32
+ * }
33
+ * }
34
+ * });
35
+ *
36
+ * const visits = crawlMachine(machine);
37
+ * // Returns visits for: root, home, dashboard, dashboard.overview, dashboard.settings
38
+ * console.log(visits.map(v => v.path.join('.')));
39
+ * // ['', 'home', 'dashboard', 'dashboard.overview', 'dashboard.settings']
40
+ * ```
41
+ *
42
+ * @example
43
+ * Extract state paths and parents
44
+ * ```typescript
45
+ * import { crawlMachine } from "@xmachines/play-router";
46
+ *
47
+ * const visits = crawlMachine(machine);
48
+ * visits.forEach(visit => {
49
+ * console.log({
50
+ * path: visit.path.join('.') || 'root',
51
+ * hasParent: !!visit.parent,
52
+ * hasChildren: Object.keys(visit.node.states).length > 0
53
+ * });
54
+ * });
55
+ * ```
56
+ *
57
+ * @see {@link https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md | RFC Play v1}
58
+ * @see {@link extractRoute} for extracting route from individual states
59
+ * @see {@link extractMachineRoutes} for complete route extraction
60
+ * @see {@link StateVisit} for visit structure
61
+ *
62
+ * @remarks
63
+ * **BFS Traversal:** Breadth-first search ensures systematic discovery of all states
64
+ * at each nesting level before descending deeper. This produces a predictable ordering
65
+ * useful for route tree construction and debugging.
66
+ *
67
+ * **Path Format:** Paths use array notation (`['dashboard', 'settings']`) which can be
68
+ * joined with dots for state IDs (`'dashboard.settings'`) matching XState's hierarchical
69
+ * state naming convention.
70
+ */
71
+ export const crawlMachine = (machine) => {
72
+ const visits = [];
73
+ const queue = [
74
+ {
75
+ node: machine.root,
76
+ path: [],
77
+ parent: null,
78
+ },
79
+ ];
80
+ while (queue.length > 0) {
81
+ const visit = queue.shift();
82
+ visits.push(visit);
83
+ // Traverse child states
84
+ const childStates = Object.entries(visit.node.states);
85
+ for (const [key, childNode] of childStates) {
86
+ queue.push({
87
+ node: childNode,
88
+ path: [...visit.path, key],
89
+ parent: visit.node,
90
+ });
91
+ }
92
+ }
93
+ return visits;
94
+ };
95
+ //# sourceMappingURL=crawl-machine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crawl-machine.js","sourceRoot":"","sources":["../src/crawl-machine.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAwB,EAAgB,EAAE;IACtE,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,MAAM,KAAK,GAAiB;QAC3B;YACC,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,IAAI,EAAE,EAAE;YACR,MAAM,EAAE,IAAI;SACZ;KACD,CAAC;IAEF,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEnB,wBAAwB;QACxB,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtD,KAAK,MAAM,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,WAAW,EAAE,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC;gBAC1B,MAAM,EAAE,KAAK,CAAC,IAAI;aAClB,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC,CAAC"}
@@ -0,0 +1,68 @@
1
+ export interface BrowserHistory {
2
+ /**
3
+ * Get current location state
4
+ */
5
+ readonly location: {
6
+ pathname: string;
7
+ search: string;
8
+ hash: string;
9
+ state: any;
10
+ };
11
+ /**
12
+ * Push new URL to history
13
+ */
14
+ push(path: string, state?: any): void;
15
+ /**
16
+ * Replace current URL in history
17
+ */
18
+ replace(path: string, state?: any): void;
19
+ /**
20
+ * Go back/forward
21
+ */
22
+ go(delta: number): void;
23
+ back(): void;
24
+ forward(): void;
25
+ /**
26
+ * Subscribe to history changes
27
+ * Returns unsubscribe function
28
+ */
29
+ subscribe(listener: (location: BrowserHistory["location"]) => void): () => void;
30
+ /**
31
+ * Create href from path
32
+ */
33
+ createHref(path: string): string;
34
+ /**
35
+ * Cleanup
36
+ */
37
+ destroy(): void;
38
+ }
39
+ /**
40
+ * Create browser history that wraps window.history
41
+ *
42
+ * Aligned with TanStack Router's history interface for API parallelism.
43
+ *
44
+ * Architecture:
45
+ * - Patches window.history.pushState/replaceState to detect changes
46
+ * - Listens to popstate for browser back/forward
47
+ * - Provides subscribe() for listeners (like PlayRouterProvider)
48
+ * - Testable (accepts window object)
49
+ *
50
+ * Usage:
51
+ * ```typescript
52
+ * const history = createBrowserHistory({ window });
53
+ *
54
+ * const unsubscribe = history.subscribe((location) => {
55
+ * console.log('URL changed:', location.pathname);
56
+ * });
57
+ *
58
+ * history.push('/new-path');
59
+ *
60
+ * // Later:
61
+ * unsubscribe();
62
+ * history.destroy();
63
+ * ```
64
+ */
65
+ export declare function createBrowserHistory(options: {
66
+ window: any;
67
+ }): BrowserHistory;
68
+ //# sourceMappingURL=create-browser-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-browser-history.d.ts","sourceRoot":"","sources":["../src/create-browser-history.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,GAAG,CAAC;KACX,CAAC;IAEF;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IAEtC;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IAEzC;;OAEG;IACH,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,IAAI,IAAI,CAAC;IACb,OAAO,IAAI,IAAI,CAAC;IAEhB;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAEhF;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAEjC;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC7C,MAAM,EAAE,GAAG,CAAC;CACZ,GAAG,cAAc,CAmFjB"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Create browser history that wraps window.history
3
+ *
4
+ * Aligned with TanStack Router's history interface for API parallelism.
5
+ *
6
+ * Architecture:
7
+ * - Patches window.history.pushState/replaceState to detect changes
8
+ * - Listens to popstate for browser back/forward
9
+ * - Provides subscribe() for listeners (like PlayRouterProvider)
10
+ * - Testable (accepts window object)
11
+ *
12
+ * Usage:
13
+ * ```typescript
14
+ * const history = createBrowserHistory({ window });
15
+ *
16
+ * const unsubscribe = history.subscribe((location) => {
17
+ * console.log('URL changed:', location.pathname);
18
+ * });
19
+ *
20
+ * history.push('/new-path');
21
+ *
22
+ * // Later:
23
+ * unsubscribe();
24
+ * history.destroy();
25
+ * ```
26
+ */
27
+ export function createBrowserHistory(options) {
28
+ const win = options.window;
29
+ const listeners = new Set();
30
+ // Save originals
31
+ const originalPushState = win.history.pushState.bind(win.history);
32
+ const originalReplaceState = win.history.replaceState.bind(win.history);
33
+ function getLocation() {
34
+ return {
35
+ pathname: win.location.pathname,
36
+ search: win.location.search,
37
+ hash: win.location.hash,
38
+ state: win.history.state,
39
+ };
40
+ }
41
+ function notify() {
42
+ const location = getLocation();
43
+ listeners.forEach((listener) => listener(location));
44
+ }
45
+ // Patch history methods
46
+ win.history.pushState = function (state, title, url) {
47
+ originalPushState(state, title, url);
48
+ notify();
49
+ };
50
+ win.history.replaceState = function (state, title, url) {
51
+ originalReplaceState(state, title, url);
52
+ notify();
53
+ };
54
+ // Listen to popstate
55
+ const popstateHandler = () => notify();
56
+ win.addEventListener("popstate", popstateHandler);
57
+ return {
58
+ get location() {
59
+ return getLocation();
60
+ },
61
+ push(path, state) {
62
+ win.history.pushState(state, "", path);
63
+ },
64
+ replace(path, state) {
65
+ win.history.replaceState(state, "", path);
66
+ },
67
+ go(delta) {
68
+ win.history.go(delta);
69
+ },
70
+ back() {
71
+ win.history.back();
72
+ },
73
+ forward() {
74
+ win.history.forward();
75
+ },
76
+ subscribe(listener) {
77
+ listeners.add(listener);
78
+ return () => listeners.delete(listener);
79
+ },
80
+ createHref(path) {
81
+ return path;
82
+ },
83
+ destroy() {
84
+ // Restore originals
85
+ win.history.pushState = originalPushState;
86
+ win.history.replaceState = originalReplaceState;
87
+ // Remove listener
88
+ win.removeEventListener("popstate", popstateHandler);
89
+ // Clear subscribers
90
+ listeners.clear();
91
+ },
92
+ };
93
+ }
94
+ //# sourceMappingURL=create-browser-history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-browser-history.js","sourceRoot":"","sources":["../src/create-browser-history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { RouteTree } from "./types.js";
2
+ export interface RouteMap {
3
+ /**
4
+ * Resolve a URL path to a state ID and params.
5
+ *
6
+ * @param path - URL pathname (e.g., "/settings/account")
7
+ * @returns Object with `to` (state ID with # prefix) and `params`
8
+ *
9
+ * Example:
10
+ * ```typescript
11
+ * const { to, params } = routeMap.resolve('/settings/account');
12
+ * // { to: "#settings", params: { section: "account" } }
13
+ *
14
+ * if (to) {
15
+ * actor.send({ type: 'play.route', to, params });
16
+ * }
17
+ * ```
18
+ */
19
+ resolve(path: string): {
20
+ to: string | null;
21
+ params: Record<string, string>;
22
+ };
23
+ }
24
+ /**
25
+ * Create a RouteMap from a RouteTree for efficient path lookups.
26
+ *
27
+ * Architecture:
28
+ * - Pure function - no side effects
29
+ * - O(1) lookups for exact matches via Map
30
+ * - O(n) fallback for pattern matching with URLPattern
31
+ * - Returns formatted state IDs (with # prefix)
32
+ * - Extracts params from matched patterns
33
+ *
34
+ * Usage:
35
+ * ```typescript
36
+ * const routeTree = extractMachineRoutes(machine);
37
+ * const routeMap = createRouteMap(routeTree);
38
+ *
39
+ * const { to, params } = routeMap.resolve('/settings/account');
40
+ * if (to) {
41
+ * actor.send({ type: 'play.route', to, params });
42
+ * }
43
+ * ```
44
+ */
45
+ export declare function createRouteMap(routeTree: RouteTree): RouteMap;
46
+ //# sourceMappingURL=create-route-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-route-map.d.ts","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,MAAM,WAAW,QAAQ;IACxB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG;QACtB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,QAAQ,CAyD7D"}
@@ -0,0 +1,73 @@
1
+ import { URLPattern } from "urlpattern-polyfill";
2
+ /**
3
+ * Create a RouteMap from a RouteTree for efficient path lookups.
4
+ *
5
+ * Architecture:
6
+ * - Pure function - no side effects
7
+ * - O(1) lookups for exact matches via Map
8
+ * - O(n) fallback for pattern matching with URLPattern
9
+ * - Returns formatted state IDs (with # prefix)
10
+ * - Extracts params from matched patterns
11
+ *
12
+ * Usage:
13
+ * ```typescript
14
+ * const routeTree = extractMachineRoutes(machine);
15
+ * const routeMap = createRouteMap(routeTree);
16
+ *
17
+ * const { to, params } = routeMap.resolve('/settings/account');
18
+ * if (to) {
19
+ * actor.send({ type: 'play.route', to, params });
20
+ * }
21
+ * ```
22
+ */
23
+ export function createRouteMap(routeTree) {
24
+ // Build pattern matchers for routes with dynamic segments
25
+ const patternRoutes = [];
26
+ // Collect all routable routes with patterns
27
+ function collectPatterns(node) {
28
+ if (node.routable && node.pattern) {
29
+ try {
30
+ // URLPattern expects patterns in format: /settings/:section
31
+ const pattern = new URLPattern({ pathname: node.pattern });
32
+ patternRoutes.push({ pattern, node });
33
+ }
34
+ catch (err) {
35
+ console.warn("[RouteMap] Invalid pattern:", node.pattern, err);
36
+ }
37
+ }
38
+ for (const child of node.children) {
39
+ collectPatterns(child);
40
+ }
41
+ }
42
+ collectPatterns(routeTree.root);
43
+ return {
44
+ resolve(path) {
45
+ // Try exact match first (O(1))
46
+ const exactMatch = routeTree.byPath.get(path);
47
+ if (exactMatch?.routable) {
48
+ return {
49
+ to: `#${exactMatch.id}`,
50
+ params: {},
51
+ };
52
+ }
53
+ // Try pattern matching (O(n))
54
+ for (const { pattern, node } of patternRoutes) {
55
+ const match = pattern.exec({ pathname: path });
56
+ if (match) {
57
+ // Extract pathname params (e.g., { section: "account" })
58
+ const params = {};
59
+ if (match.pathname.groups) {
60
+ Object.assign(params, match.pathname.groups);
61
+ }
62
+ return {
63
+ to: `#${node.id}`,
64
+ params,
65
+ };
66
+ }
67
+ }
68
+ // No match found
69
+ return { to: null, params: {} };
70
+ },
71
+ };
72
+ }
73
+ //# sourceMappingURL=create-route-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-route-map.js","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAyBjD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,cAAc,CAAC,SAAoB;IAClD,0DAA0D;IAC1D,MAAM,aAAa,GAGd,EAAE,CAAC;IAER,4CAA4C;IAC5C,SAAS,eAAe,CAAC,IAA2B;QACnD,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACnC,IAAI,CAAC;gBACJ,4DAA4D;gBAC5D,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC3D,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACvC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,OAAO,CAAC,IAAI,CAAC,6BAA6B,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAChE,CAAC;QACF,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnC,eAAe,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;IACF,CAAC;IAED,eAAe,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAEhC,OAAO;QACN,OAAO,CAAC,IAAY;YACnB,+BAA+B;YAC/B,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,UAAU,EAAE,QAAQ,EAAE,CAAC;gBAC1B,OAAO;oBACN,EAAE,EAAE,IAAI,UAAU,CAAC,EAAE,EAAE;oBACvB,MAAM,EAAE,EAAE;iBACV,CAAC;YACH,CAAC;YAED,8BAA8B;YAC9B,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,aAAa,EAAE,CAAC;gBAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/C,IAAI,KAAK,EAAE,CAAC;oBACX,yDAAyD;oBACzD,MAAM,MAAM,GAA2B,EAAE,CAAC;oBAC1C,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;wBAC3B,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;oBAC9C,CAAC;oBAED,OAAO;wBACN,EAAE,EAAE,IAAI,IAAI,CAAC,EAAE,EAAE;wBACjB,MAAM;qBACN,CAAC;gBACH,CAAC;YACF,CAAC;YAED,iBAAiB;YACjB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACjC,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,73 @@
1
+ import type { RouteTree } from "./types.js";
2
+ import type { BrowserHistory } from "./create-browser-history.js";
3
+ export interface VanillaRouter {
4
+ /**
5
+ * History instance
6
+ */
7
+ readonly history: BrowserHistory;
8
+ /**
9
+ * Route tree (for structure reference)
10
+ */
11
+ readonly routeTree: RouteTree;
12
+ /**
13
+ * Cleanup
14
+ */
15
+ destroy(): void;
16
+ }
17
+ /**
18
+ * Create vanilla router for framework-agnostic routing.
19
+ *
20
+ * Architecture:
21
+ * - Just wraps history and routeTree
22
+ * - Does NOT embed routeMap (providers need it as prop)
23
+ * - Framework-agnostic history management only
24
+ *
25
+ * Why no routeMap:
26
+ * - Router doesn't know about state IDs (that's Play-specific)
27
+ * - RouteMap is the bridge between router and actor
28
+ * - Provider receives routeMap as prop to do path → state ID resolution
29
+ *
30
+ * Usage (parallel to TanStack mode):
31
+ * ```typescript
32
+ * // Both modes: identical setup
33
+ * const routeTree = extractMachineRoutes(machine);
34
+ * const routeMap = createRouteMap(routeTree);
35
+ * const history = createBrowserHistory({ window });
36
+ *
37
+ * // Vanilla router
38
+ * const router = createRouter({ routeTree, history });
39
+ *
40
+ * // Provider needs routeMap as prop (router doesn't have it)
41
+ * <PlayRouterProvider
42
+ * actor={actor}
43
+ * router={router}
44
+ * routeMap={routeMap} // ← Must pass separately
45
+ * renderer={(currentActor, currentRouter) => {
46
+ * void currentRouter;
47
+ * return <Renderer actor={currentActor} components={components} />;
48
+ * }}
49
+ * />
50
+ * ```
51
+ *
52
+ * Compare to TanStack mode:
53
+ * ```typescript
54
+ * // TanStack router (external library)
55
+ * const router = TanStackCreateRouter({ routeTree, history });
56
+ *
57
+ * // Provider needs routeMap as prop (same as vanilla!)
58
+ * <PlayRouterProvider
59
+ * actor={actor}
60
+ * router={router}
61
+ * routeMap={routeMap} // ← Must pass separately
62
+ * renderer={(currentActor, currentRouter) => {
63
+ * void currentRouter;
64
+ * return <Renderer actor={currentActor} components={components} />;
65
+ * }}
66
+ * />
67
+ * ```
68
+ */
69
+ export declare function createRouter(options: {
70
+ routeTree: RouteTree;
71
+ history: BrowserHistory;
72
+ }): VanillaRouter;
73
+ //# sourceMappingURL=create-router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-router.d.ts","sourceRoot":"","sources":["../src/create-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,MAAM,WAAW,aAAa;IAC7B;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAE9B;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE;IACrC,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,cAAc,CAAC;CACxB,GAAG,aAAa,CAWhB"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Create vanilla router for framework-agnostic routing.
3
+ *
4
+ * Architecture:
5
+ * - Just wraps history and routeTree
6
+ * - Does NOT embed routeMap (providers need it as prop)
7
+ * - Framework-agnostic history management only
8
+ *
9
+ * Why no routeMap:
10
+ * - Router doesn't know about state IDs (that's Play-specific)
11
+ * - RouteMap is the bridge between router and actor
12
+ * - Provider receives routeMap as prop to do path → state ID resolution
13
+ *
14
+ * Usage (parallel to TanStack mode):
15
+ * ```typescript
16
+ * // Both modes: identical setup
17
+ * const routeTree = extractMachineRoutes(machine);
18
+ * const routeMap = createRouteMap(routeTree);
19
+ * const history = createBrowserHistory({ window });
20
+ *
21
+ * // Vanilla router
22
+ * const router = createRouter({ routeTree, history });
23
+ *
24
+ * // Provider needs routeMap as prop (router doesn't have it)
25
+ * <PlayRouterProvider
26
+ * actor={actor}
27
+ * router={router}
28
+ * routeMap={routeMap} // ← Must pass separately
29
+ * renderer={(currentActor, currentRouter) => {
30
+ * void currentRouter;
31
+ * return <Renderer actor={currentActor} components={components} />;
32
+ * }}
33
+ * />
34
+ * ```
35
+ *
36
+ * Compare to TanStack mode:
37
+ * ```typescript
38
+ * // TanStack router (external library)
39
+ * const router = TanStackCreateRouter({ routeTree, history });
40
+ *
41
+ * // Provider needs routeMap as prop (same as vanilla!)
42
+ * <PlayRouterProvider
43
+ * actor={actor}
44
+ * router={router}
45
+ * routeMap={routeMap} // ← Must pass separately
46
+ * renderer={(currentActor, currentRouter) => {
47
+ * void currentRouter;
48
+ * return <Renderer actor={currentActor} components={components} />;
49
+ * }}
50
+ * />
51
+ * ```
52
+ */
53
+ export function createRouter(options) {
54
+ const { routeTree, history } = options;
55
+ return {
56
+ routeTree,
57
+ history,
58
+ destroy() {
59
+ history.destroy();
60
+ },
61
+ };
62
+ }
63
+ //# sourceMappingURL=create-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-router.js","sourceRoot":"","sources":["../src/create-router.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,MAAM,UAAU,YAAY,CAAC,OAG5B;IACA,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAEvC,OAAO;QACN,SAAS;QACT,OAAO;QAEP,OAAO;YACN,OAAO,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { StateNode } from "xstate";
2
+ import type { RouteInfo } from "./types.js";
3
+ /**
4
+ * Extract route information from state node with validation
5
+ *
6
+ * States with meta.route are "routable" - they can receive play.route events
7
+ * (when machine is wrapped with formatPlayRouteTransitions).
8
+ *
9
+ * @param node - XState StateNode to extract route from
10
+ * @param path - State path segments from root
11
+ * @param stateMap - Map of all state IDs to StateNodes for validation
12
+ * @returns RouteInfo if state has meta.route, null otherwise
13
+ * @throws {Error} If route path is malformed (missing leading /)
14
+ * @throws {Error} If route references non-existent state
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const node = {
19
+ * id: 'dashboard',
20
+ * meta: { route: '/dashboard' }
21
+ * };
22
+ * ```
23
+ */
24
+ export declare const extractRoute: (node: StateNode, path: string[], stateMap: Map<string, StateNode>) => RouteInfo | null;
25
+ //# sourceMappingURL=extract-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-route.d.ts","sourceRoot":"","sources":["../src/extract-route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,YAAY,GACxB,MAAM,SAAS,EACf,MAAM,MAAM,EAAE,EACd,UAAU,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,KAC9B,SAAS,GAAG,IAiDd,CAAC"}