@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.
- package/.oxfmtrc.json +3 -0
- package/.oxlintrc.json +3 -0
- package/README.md +436 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/build-tree.ts.html +316 -0
- package/coverage/connect-router.ts.html +505 -0
- package/coverage/coverage-summary.json +15 -0
- package/coverage/crawl-machine.ts.html +385 -0
- package/coverage/create-browser-history.ts.html +556 -0
- package/coverage/create-route-map.ts.html +400 -0
- package/coverage/create-router.ts.html +328 -0
- package/coverage/extract-route.ts.html +322 -0
- package/coverage/extract-routes.ts.html +286 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +296 -0
- package/coverage/index.ts.html +610 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/query.ts.html +307 -0
- package/coverage/router-bridge-base.ts.html +919 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/coverage/types.ts.html +787 -0
- package/coverage/validate-routes.ts.html +319 -0
- package/dist/build-tree.d.ts +13 -0
- package/dist/build-tree.d.ts.map +1 -0
- package/dist/build-tree.js +67 -0
- package/dist/build-tree.js.map +1 -0
- package/dist/connect-router.d.ts +56 -0
- package/dist/connect-router.d.ts.map +1 -0
- package/dist/connect-router.js +119 -0
- package/dist/connect-router.js.map +1 -0
- package/dist/crawl-machine.d.ts +74 -0
- package/dist/crawl-machine.d.ts.map +1 -0
- package/dist/crawl-machine.js +95 -0
- package/dist/crawl-machine.js.map +1 -0
- package/dist/create-browser-history.d.ts +68 -0
- package/dist/create-browser-history.d.ts.map +1 -0
- package/dist/create-browser-history.js +94 -0
- package/dist/create-browser-history.js.map +1 -0
- package/dist/create-route-map.d.ts +46 -0
- package/dist/create-route-map.d.ts.map +1 -0
- package/dist/create-route-map.js +73 -0
- package/dist/create-route-map.js.map +1 -0
- package/dist/create-router.d.ts +73 -0
- package/dist/create-router.d.ts.map +1 -0
- package/dist/create-router.js +63 -0
- package/dist/create-router.js.map +1 -0
- package/dist/extract-route.d.ts +25 -0
- package/dist/extract-route.d.ts.map +1 -0
- package/dist/extract-route.js +63 -0
- package/dist/extract-route.js.map +1 -0
- package/dist/extract-routes.d.ts +41 -0
- package/dist/extract-routes.d.ts.map +1 -0
- package/dist/extract-routes.js +61 -0
- package/dist/extract-routes.js.map +1 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +141 -0
- package/dist/index.js.map +1 -0
- package/dist/query.d.ts +52 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +69 -0
- package/dist/query.js.map +1 -0
- package/dist/router-bridge-base.d.ts +150 -0
- package/dist/router-bridge-base.d.ts.map +1 -0
- package/dist/router-bridge-base.js +240 -0
- package/dist/router-bridge-base.js.map +1 -0
- package/dist/types.d.ts +228 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validate-routes.d.ts +39 -0
- package/dist/validate-routes.d.ts.map +1 -0
- package/dist/validate-routes.js +65 -0
- package/dist/validate-routes.js.map +1 -0
- package/examples/demo/README.md +127 -0
- package/examples/demo/index.html +41 -0
- package/examples/demo/package.json +27 -0
- package/examples/demo/src/main.ts +28 -0
- package/examples/demo/src/router.ts +37 -0
- package/examples/demo/src/shell.ts +316 -0
- package/examples/demo/test/browser/auth-flow.browser.test.ts +60 -0
- package/examples/demo/test/browser/startup.browser.test.ts +37 -0
- package/examples/demo/test/library-pattern.test.ts +51 -0
- package/examples/demo/tsconfig.json +17 -0
- package/examples/demo/vite.config.ts +7 -0
- package/examples/demo/vitest.browser.config.ts +20 -0
- package/examples/demo/vitest.config.ts +9 -0
- package/examples/shared/dist/auth-machine.d.ts +20 -0
- package/examples/shared/dist/auth-machine.d.ts.map +1 -0
- package/examples/shared/dist/auth-machine.js +212 -0
- package/examples/shared/dist/auth-machine.js.map +1 -0
- package/examples/shared/dist/catalog.d.ts +85 -0
- package/examples/shared/dist/catalog.d.ts.map +1 -0
- package/examples/shared/dist/catalog.js +86 -0
- package/examples/shared/dist/catalog.js.map +1 -0
- package/examples/shared/dist/index.d.ts +4 -0
- package/examples/shared/dist/index.d.ts.map +1 -0
- package/examples/shared/dist/index.js +3 -0
- package/examples/shared/dist/index.js.map +1 -0
- package/examples/shared/package.json +37 -0
- package/examples/shared/src/auth-machine.ts +234 -0
- package/examples/shared/src/catalog.ts +95 -0
- package/examples/shared/src/index.css +3 -0
- package/examples/shared/src/index.ts +3 -0
- package/examples/shared/src/styles/layout.css +413 -0
- package/examples/shared/src/styles/reset.css +42 -0
- package/examples/shared/src/styles/tokens.css +183 -0
- package/examples/shared/tsconfig.json +14 -0
- package/examples/shared/tsconfig.tsbuildinfo +1 -0
- package/package.json +44 -0
- package/src/build-tree.ts +77 -0
- package/src/connect-router.ts +142 -0
- package/src/crawl-machine.ts +100 -0
- package/src/create-browser-history.ts +157 -0
- package/src/create-route-map.ts +105 -0
- package/src/create-router.ts +87 -0
- package/src/extract-route.ts +79 -0
- package/src/extract-routes.ts +67 -0
- package/src/index.ts +175 -0
- package/src/query.ts +74 -0
- package/src/router-bridge-base.ts +279 -0
- package/src/types.ts +234 -0
- package/src/validate-routes.ts +76 -0
- package/test/connect-route-map.test.ts +320 -0
- package/test/crawl-extract.test.js +473 -0
- package/test/create-browser-history.test.ts +123 -0
- package/test/create-router.test.ts +23 -0
- package/test/extract-routes.test.ts +80 -0
- package/test/find-route-by-path-patterns.test.ts +69 -0
- package/test/integration.test.js +438 -0
- package/test/query.test.ts +56 -0
- package/test/router-bridge-base-edge.test.ts +165 -0
- package/test/router-bridge-base.test.ts +119 -0
- package/test/tree-query.test.js +692 -0
- package/test/validation.test.js +158 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
- 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"}
|