@xmachines/play-router 1.0.0-beta.43 → 1.0.0-beta.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/base-route-map.d.ts +1 -1
- package/dist/base-route-map.js +1 -1
- package/dist/create-route-map-from-tree.d.ts +8 -6
- package/dist/create-route-map-from-tree.d.ts.map +1 -1
- package/dist/create-route-map-from-tree.js +8 -7
- package/dist/create-route-map-from-tree.js.map +1 -1
- package/dist/create-route-map.d.ts +26 -50
- package/dist/create-route-map.d.ts.map +1 -1
- package/dist/create-route-map.js +24 -73
- package/dist/create-route-map.js.map +1 -1
- package/dist/create-route-matcher.d.ts +66 -0
- package/dist/create-route-matcher.d.ts.map +1 -0
- package/dist/create-route-matcher.js +83 -0
- package/dist/create-route-matcher.js.map +1 -0
- package/dist/errors.d.ts +34 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +38 -3
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/router-bridge-base.d.ts +22 -7
- package/dist/router-bridge-base.d.ts.map +1 -1
- package/dist/router-bridge-base.js +58 -15
- package/dist/router-bridge-base.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +6 -6
- package/dist/create-route-map-from-machine.d.ts +0 -24
- package/dist/create-route-map-from-machine.d.ts.map +0 -1
- package/dist/create-route-map-from-machine.js +0 -32
- package/dist/create-route-map-from-machine.js.map +0 -1
package/README.md
CHANGED
|
@@ -124,7 +124,7 @@ Open http://localhost:5174/ and explore:
|
|
|
124
124
|
```typescript
|
|
125
125
|
// Extract routes from machine
|
|
126
126
|
const routeTree = extractMachineRoutes(authMachine);
|
|
127
|
-
const routeMatcher =
|
|
127
|
+
const routeMatcher = createRouteMatcher(routeTree);
|
|
128
128
|
|
|
129
129
|
// Actor → URL sync
|
|
130
130
|
const watcher = new Signal.subtle.Watcher(() => {
|
|
@@ -195,7 +195,7 @@ That final step distinguishes:
|
|
|
195
195
|
- **deep-link:** browser URL differs from the machine's initial route, so router wins and the actor receives `play.route`
|
|
196
196
|
- **restore:** browser URL is still at the machine's initial route while the actor was restored elsewhere, so actor wins and the bridge pushes the actor route back into the router
|
|
197
197
|
|
|
198
|
-
Actor-originated router sync
|
|
198
|
+
Actor-originated router sync suppresses router echoes via `lastSyncedPath`. `syncRouterFromActor` resolves the concrete path and stores it before calling the adapter's navigation method; when the router callback fires with that same path, `syncActorFromRouter` short-circuits at `sanitized === lastSyncedPath` and sends no event.
|
|
199
199
|
|
|
200
200
|
## Diagnostics
|
|
201
201
|
|
|
@@ -291,14 +291,14 @@ const successors = getSuccessors(tree.graph!, "myMachine.home");
|
|
|
291
291
|
- `eventType: string` — event type triggering this transition
|
|
292
292
|
- `guardType?: string` — guard name/description (if transition is guarded)
|
|
293
293
|
|
|
294
|
-
###
|
|
294
|
+
### createRouteMap() / createRouteMapFromTree()
|
|
295
295
|
|
|
296
296
|
Both build a `BaseRouteMap` using `node.fullPath` (absolute resolved paths) for browser URL matching.
|
|
297
297
|
|
|
298
298
|
```typescript
|
|
299
299
|
// Single-call form — preferred for XState machines:
|
|
300
|
-
import {
|
|
301
|
-
const routeMap =
|
|
300
|
+
import { createRouteMap } from "@xmachines/play-router";
|
|
301
|
+
const routeMap = createRouteMap(machine);
|
|
302
302
|
|
|
303
303
|
// Two-step form — used by framework adapter packages:
|
|
304
304
|
import { extractMachineRoutes, createRouteMapFromTree } from "@xmachines/play-router";
|
|
@@ -306,7 +306,7 @@ const routeTree = extractMachineRoutes(machine);
|
|
|
306
306
|
const routeMap = createRouteMapFromTree(routeTree);
|
|
307
307
|
```
|
|
308
308
|
|
|
309
|
-
Both forms produce identical results. `createRouteMapFromTree` is exposed for framework adapters (React Router, TanStack Router) that
|
|
309
|
+
Both forms produce identical `RouteMap` results. `createRouteMapFromTree` is exposed for framework adapters (React Router, TanStack Router) that already hold the `RouteTree` separately for other purposes (e.g. graph queries). `createRouteMatcher(routeTree)` is the distinct API for URL-pattern matching that returns a `RouteMatcher`, not a `RouteMap`.
|
|
310
310
|
|
|
311
311
|
**`node.path` vs `node.fullPath`:** Every `RouteNode` carries both fields. `path` is the raw `meta.route` segment as declared in the machine — it may be relative (e.g. `"overview"`). `fullPath` is the resolved absolute path (e.g. `"/dashboard/overview"`). Route maps always use `fullPath`. Only use `path` if you specifically need the declared segment.
|
|
312
312
|
|
package/dist/base-route-map.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Algorithm: O(1) exact match via Map, then bucket-based O(k) pattern match
|
|
8
8
|
* where k = routes in the first-segment bucket (typically << total routes).
|
|
9
|
-
* Uses URLPattern for parameterized route matching (same engine as
|
|
9
|
+
* Uses URLPattern for parameterized route matching (same engine as createRouteMatcher).
|
|
10
10
|
*/
|
|
11
11
|
/**
|
|
12
12
|
* A single state ID ↔ path mapping entry.
|
package/dist/base-route-map.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Algorithm: O(1) exact match via Map, then bucket-based O(k) pattern match
|
|
8
8
|
* where k = routes in the first-segment bucket (typically << total routes).
|
|
9
|
-
* Uses URLPattern for parameterized route matching (same engine as
|
|
9
|
+
* Uses URLPattern for parameterized route matching (same engine as createRouteMatcher).
|
|
10
10
|
*/
|
|
11
11
|
import QuickLRU from "quick-lru";
|
|
12
12
|
import { getURLPatternCtor, getIndexKey, getCandidates, } from "./url-pattern-utils.js";
|
|
@@ -1,31 +1,33 @@
|
|
|
1
1
|
import type { RouteTree } from "./types.js";
|
|
2
2
|
import { RouteMap } from "./base-route-map.js";
|
|
3
|
+
import type { RouteMapOptions } from "./create-route-map.js";
|
|
3
4
|
/**
|
|
4
5
|
* Create a `RouteMap` from a `RouteTree` node structure.
|
|
5
6
|
*
|
|
6
7
|
* Used by framework-router adapters (React Router, TanStack Router) that pass a
|
|
7
8
|
* `RouteTree` produced by `extractMachineRoutes()` rather than calling
|
|
8
|
-
* `
|
|
9
|
+
* `createRouteMap()` directly.
|
|
9
10
|
*
|
|
10
11
|
* Traverses all nodes collecting `{ stateId: node.id, path: node.fullPath }` pairs.
|
|
11
12
|
* `node.fullPath` is always the absolute resolved path (e.g. `"/dashboard/overview"`),
|
|
12
13
|
* which is what `RouteMap` needs for browser URL matching. This matches the
|
|
13
|
-
* behaviour of `
|
|
14
|
+
* behaviour of `createRouteMap(machine)`, which also uses `node.fullPath`.
|
|
14
15
|
*
|
|
15
16
|
* @param routeTree - A `RouteTree` as returned by `extractMachineRoutes()`.
|
|
17
|
+
* @param options - Optional configuration (e.g. `{ cacheSize }` to override the LRU cache size).
|
|
16
18
|
* @returns A `RouteMap` for use with any `RouterBridgeBase`-based adapter.
|
|
17
19
|
*
|
|
18
20
|
* @example
|
|
19
21
|
* ```typescript
|
|
20
22
|
* // Preferred — single call for XState machines:
|
|
21
|
-
* import {
|
|
22
|
-
* const routeMap =
|
|
23
|
+
* import { createRouteMap } from '@xmachines/play-router';
|
|
24
|
+
* const routeMap = createRouteMap(machine); // takes AnyStateMachine
|
|
23
25
|
*
|
|
24
|
-
* //
|
|
26
|
+
* // Two-step form used by framework adapters that work with route trees:
|
|
25
27
|
* import { extractMachineRoutes, createRouteMapFromTree } from '@xmachines/play-router';
|
|
26
28
|
* const routeTree = extractMachineRoutes(machine);
|
|
27
29
|
* const routeMap = createRouteMapFromTree(routeTree); // uses node.fullPath (absolute)
|
|
28
30
|
* ```
|
|
29
31
|
*/
|
|
30
|
-
export declare function createRouteMapFromTree(routeTree: RouteTree): RouteMap;
|
|
32
|
+
export declare function createRouteMapFromTree(routeTree: RouteTree, options?: RouteMapOptions): RouteMap;
|
|
31
33
|
//# sourceMappingURL=create-route-map-from-tree.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-route-map-from-tree.d.ts","sourceRoot":"","sources":["../src/create-route-map-from-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"create-route-map-from-tree.d.ts","sourceRoot":"","sources":["../src/create-route-map-from-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,QAAQ,CAchG"}
|
|
@@ -4,29 +4,30 @@ import { RouteMap } from "./base-route-map.js";
|
|
|
4
4
|
*
|
|
5
5
|
* Used by framework-router adapters (React Router, TanStack Router) that pass a
|
|
6
6
|
* `RouteTree` produced by `extractMachineRoutes()` rather than calling
|
|
7
|
-
* `
|
|
7
|
+
* `createRouteMap()` directly.
|
|
8
8
|
*
|
|
9
9
|
* Traverses all nodes collecting `{ stateId: node.id, path: node.fullPath }` pairs.
|
|
10
10
|
* `node.fullPath` is always the absolute resolved path (e.g. `"/dashboard/overview"`),
|
|
11
11
|
* which is what `RouteMap` needs for browser URL matching. This matches the
|
|
12
|
-
* behaviour of `
|
|
12
|
+
* behaviour of `createRouteMap(machine)`, which also uses `node.fullPath`.
|
|
13
13
|
*
|
|
14
14
|
* @param routeTree - A `RouteTree` as returned by `extractMachineRoutes()`.
|
|
15
|
+
* @param options - Optional configuration (e.g. `{ cacheSize }` to override the LRU cache size).
|
|
15
16
|
* @returns A `RouteMap` for use with any `RouterBridgeBase`-based adapter.
|
|
16
17
|
*
|
|
17
18
|
* @example
|
|
18
19
|
* ```typescript
|
|
19
20
|
* // Preferred — single call for XState machines:
|
|
20
|
-
* import {
|
|
21
|
-
* const routeMap =
|
|
21
|
+
* import { createRouteMap } from '@xmachines/play-router';
|
|
22
|
+
* const routeMap = createRouteMap(machine); // takes AnyStateMachine
|
|
22
23
|
*
|
|
23
|
-
* //
|
|
24
|
+
* // Two-step form used by framework adapters that work with route trees:
|
|
24
25
|
* import { extractMachineRoutes, createRouteMapFromTree } from '@xmachines/play-router';
|
|
25
26
|
* const routeTree = extractMachineRoutes(machine);
|
|
26
27
|
* const routeMap = createRouteMapFromTree(routeTree); // uses node.fullPath (absolute)
|
|
27
28
|
* ```
|
|
28
29
|
*/
|
|
29
|
-
export function createRouteMapFromTree(routeTree) {
|
|
30
|
+
export function createRouteMapFromTree(routeTree, options) {
|
|
30
31
|
const routes = [];
|
|
31
32
|
function traverse(node) {
|
|
32
33
|
if (node.id && node.fullPath) {
|
|
@@ -37,6 +38,6 @@ export function createRouteMapFromTree(routeTree) {
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
traverse(routeTree.root);
|
|
40
|
-
return new RouteMap(routes);
|
|
41
|
+
return new RouteMap(routes, options);
|
|
41
42
|
}
|
|
42
43
|
//# sourceMappingURL=create-route-map-from-tree.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-route-map-from-tree.js","sourceRoot":"","sources":["../src/create-route-map-from-tree.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"create-route-map-from-tree.js","sourceRoot":"","sources":["../src/create-route-map-from-tree.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAG/C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,sBAAsB,CAAC,SAAoB,EAAE,OAAyB;IACrF,MAAM,MAAM,GAA6C,EAAE,CAAC;IAE5D,SAAS,QAAQ,CAAC,IAAe;QAChC,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;IACF,CAAC;IAED,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -1,66 +1,42 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AnyStateMachine } from "xstate";
|
|
2
|
+
import { RouteMap } from "./base-route-map.js";
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Matches incoming URL paths to state IDs (with `#` prefix) and extracts
|
|
6
|
-
* route parameters. Used by `RouterBridgeBase` to translate router navigation
|
|
7
|
-
* events into `play.route` actor events.
|
|
8
|
-
*
|
|
9
|
-
* @see {@link createRouteMap} for the factory function
|
|
10
|
-
* @see {@link RouteMap} for direct state ID ↔ path lookups without params extraction
|
|
4
|
+
* Options for `createRouteMap` and `createRouteMapFromTree`.
|
|
11
5
|
*/
|
|
12
|
-
export interface
|
|
6
|
+
export interface RouteMapOptions {
|
|
13
7
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* @param path - URL pathname (e.g., "/settings/account")
|
|
17
|
-
* @returns Object with `to` (state ID with # prefix) and `params`
|
|
8
|
+
* Maximum number of resolved parameterized path lookups to cache.
|
|
18
9
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* // { to: "#settings", params: { section: "account" } }
|
|
10
|
+
* `RouteMap.getStateIdByPath()` resolves parameterized patterns (e.g.
|
|
11
|
+
* `/profile/:userId`) via URLPattern on every call. Frequently visited
|
|
12
|
+
* paths are cached in an LRU so subsequent lookups are O(1).
|
|
23
13
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* }
|
|
27
|
-
* ```
|
|
14
|
+
* Increase this value for applications with large parameterized route sets
|
|
15
|
+
* or high navigation frequency. Default: `500`.
|
|
28
16
|
*/
|
|
29
|
-
|
|
30
|
-
to: string | null;
|
|
31
|
-
params: Record<string, string>;
|
|
32
|
-
};
|
|
17
|
+
cacheSize?: number;
|
|
33
18
|
}
|
|
34
19
|
/**
|
|
35
|
-
* Create a
|
|
20
|
+
* Create a `RouteMap` from an XState state machine.
|
|
36
21
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* -
|
|
41
|
-
* first-segment bucket (typically << total routes)
|
|
42
|
-
* - Returns formatted state IDs (with # prefix)
|
|
43
|
-
* - Extracts params from matched patterns
|
|
22
|
+
* Extracts all routable states (those with `meta.route`) and builds a bidirectional
|
|
23
|
+
* path ↔ stateId lookup structure. The returned map is used by `RouterBridgeBase`
|
|
24
|
+
* subclasses to translate browser URL changes into `play.route` actor events and
|
|
25
|
+
* vice-versa.
|
|
44
26
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* import "urlpattern-polyfill"; // before importing @xmachines/play-router
|
|
50
|
-
* ```
|
|
51
|
-
* If `URLPattern` is unavailable, a `URLPatternUnavailableError` is thrown.
|
|
52
|
-
* Static (exact) routes are unaffected by URLPattern availability.
|
|
27
|
+
* @param machine - XState v5 state machine with `meta.route` annotations on states.
|
|
28
|
+
* @param options - Optional configuration. Pass `{ cacheSize }` to override the
|
|
29
|
+
* default LRU cache size for parameterized path lookups.
|
|
30
|
+
* @returns A `RouteMap` for passing to any `RouterBridgeBase`-based adapter.
|
|
53
31
|
*
|
|
54
|
-
*
|
|
32
|
+
* @example
|
|
55
33
|
* ```typescript
|
|
56
|
-
*
|
|
57
|
-
*
|
|
34
|
+
* import { createRouteMap } from '@xmachines/play-router';
|
|
35
|
+
* import { connectRouter } from '@xmachines/play-dom-router';
|
|
58
36
|
*
|
|
59
|
-
* const
|
|
60
|
-
*
|
|
61
|
-
* actor.send({ type: 'play.route', to, params });
|
|
62
|
-
* }
|
|
37
|
+
* const routeMap = createRouteMap(myMachine);
|
|
38
|
+
* const disconnect = connectRouter({ actor, router, routeMap });
|
|
63
39
|
* ```
|
|
64
40
|
*/
|
|
65
|
-
export declare function createRouteMap(
|
|
41
|
+
export declare function createRouteMap(machine: AnyStateMachine, options?: RouteMapOptions): RouteMap;
|
|
66
42
|
//# sourceMappingURL=create-route-map.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-route-map.d.ts","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"create-route-map.d.ts","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAG9C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,QAAQ,CAU5F"}
|
package/dist/create-route-map.js
CHANGED
|
@@ -1,83 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { extractMachineRoutes } from "./extract-routes.js";
|
|
2
|
+
import { getRoutableRoutes } from "./query.js";
|
|
3
|
+
import { RouteMap } from "./base-route-map.js";
|
|
3
4
|
/**
|
|
4
|
-
* Create a
|
|
5
|
+
* Create a `RouteMap` from an XState state machine.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
* first-segment bucket (typically << total routes)
|
|
11
|
-
* - Returns formatted state IDs (with # prefix)
|
|
12
|
-
* - Extracts params from matched patterns
|
|
7
|
+
* Extracts all routable states (those with `meta.route`) and builds a bidirectional
|
|
8
|
+
* path ↔ stateId lookup structure. The returned map is used by `RouterBridgeBase`
|
|
9
|
+
* subclasses to translate browser URL changes into `play.route` actor events and
|
|
10
|
+
* vice-versa.
|
|
13
11
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* import "urlpattern-polyfill"; // before importing @xmachines/play-router
|
|
19
|
-
* ```
|
|
20
|
-
* If `URLPattern` is unavailable, a `URLPatternUnavailableError` is thrown.
|
|
21
|
-
* Static (exact) routes are unaffected by URLPattern availability.
|
|
12
|
+
* @param machine - XState v5 state machine with `meta.route` annotations on states.
|
|
13
|
+
* @param options - Optional configuration. Pass `{ cacheSize }` to override the
|
|
14
|
+
* default LRU cache size for parameterized path lookups.
|
|
15
|
+
* @returns A `RouteMap` for passing to any `RouterBridgeBase`-based adapter.
|
|
22
16
|
*
|
|
23
|
-
*
|
|
17
|
+
* @example
|
|
24
18
|
* ```typescript
|
|
25
|
-
*
|
|
26
|
-
*
|
|
19
|
+
* import { createRouteMap } from '@xmachines/play-router';
|
|
20
|
+
* import { connectRouter } from '@xmachines/play-dom-router';
|
|
27
21
|
*
|
|
28
|
-
* const
|
|
29
|
-
*
|
|
30
|
-
* actor.send({ type: 'play.route', to, params });
|
|
31
|
-
* }
|
|
22
|
+
* const routeMap = createRouteMap(myMachine);
|
|
23
|
+
* const disconnect = connectRouter({ actor, router, routeMap });
|
|
32
24
|
* ```
|
|
33
25
|
*/
|
|
34
|
-
export function createRouteMap(
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (!URLPatternCtor) {
|
|
42
|
-
throw new URLPatternUnavailableError();
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
const urlPattern = new URLPatternCtor({ pathname: node.pattern });
|
|
46
|
-
const bucketKey = getIndexKey(node.pattern);
|
|
47
|
-
const bucket = patternBuckets.get(bucketKey) ?? [];
|
|
48
|
-
bucket.push({ pattern: urlPattern, node, order: patternOrder++ });
|
|
49
|
-
patternBuckets.set(bucketKey, bucket);
|
|
50
|
-
}
|
|
51
|
-
catch (err) {
|
|
52
|
-
throw new InvalidRoutePatternError(node.pattern, { cause: err });
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
for (const child of node.children) {
|
|
56
|
-
collectPatterns(child);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
collectPatterns(routeTree.root);
|
|
60
|
-
return {
|
|
61
|
-
match(path) {
|
|
62
|
-
// O(1) exact match via routeTree.byPath Map (unchanged)
|
|
63
|
-
const exactMatch = routeTree.byPath.get(path);
|
|
64
|
-
if (exactMatch?.routable) {
|
|
65
|
-
return { to: `#${exactMatch.id}`, params: {} };
|
|
66
|
-
}
|
|
67
|
-
// Bucket-indexed pattern match: only scan candidates for this path's first segment
|
|
68
|
-
const candidates = getCandidates(patternBuckets, getIndexKey(path));
|
|
69
|
-
for (const { pattern, node } of candidates) {
|
|
70
|
-
const match = pattern.exec({ pathname: path });
|
|
71
|
-
if (match) {
|
|
72
|
-
const params = {};
|
|
73
|
-
if (match.pathname.groups) {
|
|
74
|
-
Object.assign(params, match.pathname.groups);
|
|
75
|
-
}
|
|
76
|
-
return { to: `#${node.id}`, params };
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return { to: null, params: {} };
|
|
80
|
-
},
|
|
81
|
-
};
|
|
26
|
+
export function createRouteMap(machine, options) {
|
|
27
|
+
const routeTree = extractMachineRoutes(machine);
|
|
28
|
+
const routes = getRoutableRoutes(routeTree);
|
|
29
|
+
return new RouteMap(routes.map((node) => ({
|
|
30
|
+
stateId: node.stateId,
|
|
31
|
+
path: node.fullPath,
|
|
32
|
+
})), options);
|
|
82
33
|
}
|
|
83
34
|
//# sourceMappingURL=create-route-map.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-route-map.js","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"create-route-map.js","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAmB/C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,cAAc,CAAC,OAAwB,EAAE,OAAyB;IACjF,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC5C,OAAO,IAAI,QAAQ,CAClB,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACrB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,IAAI,CAAC,QAAQ;KACnB,CAAC,CAAC,EACH,OAAO,CACP,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { RouteTree } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* URL route matcher interface returned by `createRouteMatcher()`.
|
|
4
|
+
*
|
|
5
|
+
* Matches incoming URL paths to state IDs (with `#` prefix) and extracts
|
|
6
|
+
* route parameters. Used by `RouterBridgeBase` to translate router navigation
|
|
7
|
+
* events into `play.route` actor events.
|
|
8
|
+
*
|
|
9
|
+
* @see {@link createRouteMatcher} for the factory function
|
|
10
|
+
* @see {@link RouteMap} for direct state ID ↔ path lookups without params extraction
|
|
11
|
+
*/
|
|
12
|
+
export interface RouteMatcher {
|
|
13
|
+
/**
|
|
14
|
+
* Match a URL path to a state ID and params.
|
|
15
|
+
*
|
|
16
|
+
* @param path - URL pathname (e.g., "/settings/account")
|
|
17
|
+
* @returns Object with `to` (state ID with # prefix) and `params`
|
|
18
|
+
*
|
|
19
|
+
* Example:
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const { to, params } = routeMatcher.match('/settings/account');
|
|
22
|
+
* // { to: "#settings", params: { section: "account" } }
|
|
23
|
+
*
|
|
24
|
+
* if (to) {
|
|
25
|
+
* actor.send({ type: 'play.route', to, params });
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
match(path: string): {
|
|
30
|
+
to: string | null;
|
|
31
|
+
params: Record<string, string>;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a URL pattern matcher from a RouteTree for efficient path lookups.
|
|
36
|
+
*
|
|
37
|
+
* Architecture:
|
|
38
|
+
* - Pure function - no side effects
|
|
39
|
+
* - O(1) lookups for exact matches via Map
|
|
40
|
+
* - O(k) bucket-indexed pattern matching with URLPattern, where k = routes in the
|
|
41
|
+
* first-segment bucket (typically << total routes)
|
|
42
|
+
* - Returns formatted state IDs (with # prefix)
|
|
43
|
+
* - Extracts params from matched patterns
|
|
44
|
+
*
|
|
45
|
+
* **URLPattern requirement:** Dynamic route pattern matching requires `URLPattern`
|
|
46
|
+
* to be available on `globalThis`. On Node.js < 24 and older browsers, load a polyfill
|
|
47
|
+
* before calling this function:
|
|
48
|
+
* ```typescript
|
|
49
|
+
* import "urlpattern-polyfill"; // before importing @xmachines/play-router
|
|
50
|
+
* ```
|
|
51
|
+
* If `URLPattern` is unavailable, a `URLPatternUnavailableError` is thrown.
|
|
52
|
+
* Static (exact) routes are unaffected by URLPattern availability.
|
|
53
|
+
*
|
|
54
|
+
* Usage:
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const routeTree = extractMachineRoutes(machine);
|
|
57
|
+
* const routeMatcher = createRouteMatcher(routeTree);
|
|
58
|
+
*
|
|
59
|
+
* const { to, params } = routeMatcher.match('/settings/account');
|
|
60
|
+
* if (to) {
|
|
61
|
+
* actor.send({ type: 'play.route', to, params });
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function createRouteMatcher(routeTree: RouteTree): RouteMatcher;
|
|
66
|
+
//# sourceMappingURL=create-route-matcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-route-matcher.d.ts","sourceRoot":"","sources":["../src/create-route-matcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AASvD;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC5B;;;;;;;;;;;;;;;OAeG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG;QACpB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,YAAY,CAuDrE"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { URLPatternUnavailableError, InvalidRoutePatternError } from "./errors.js";
|
|
2
|
+
import { getURLPatternCtor, getIndexKey, getCandidates, } from "./url-pattern-utils.js";
|
|
3
|
+
/**
|
|
4
|
+
* Create a URL pattern matcher from a RouteTree for efficient path lookups.
|
|
5
|
+
*
|
|
6
|
+
* Architecture:
|
|
7
|
+
* - Pure function - no side effects
|
|
8
|
+
* - O(1) lookups for exact matches via Map
|
|
9
|
+
* - O(k) bucket-indexed pattern matching with URLPattern, where k = routes in the
|
|
10
|
+
* first-segment bucket (typically << total routes)
|
|
11
|
+
* - Returns formatted state IDs (with # prefix)
|
|
12
|
+
* - Extracts params from matched patterns
|
|
13
|
+
*
|
|
14
|
+
* **URLPattern requirement:** Dynamic route pattern matching requires `URLPattern`
|
|
15
|
+
* to be available on `globalThis`. On Node.js < 24 and older browsers, load a polyfill
|
|
16
|
+
* before calling this function:
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import "urlpattern-polyfill"; // before importing @xmachines/play-router
|
|
19
|
+
* ```
|
|
20
|
+
* If `URLPattern` is unavailable, a `URLPatternUnavailableError` is thrown.
|
|
21
|
+
* Static (exact) routes are unaffected by URLPattern availability.
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const routeTree = extractMachineRoutes(machine);
|
|
26
|
+
* const routeMatcher = createRouteMatcher(routeTree);
|
|
27
|
+
*
|
|
28
|
+
* const { to, params } = routeMatcher.match('/settings/account');
|
|
29
|
+
* if (to) {
|
|
30
|
+
* actor.send({ type: 'play.route', to, params });
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function createRouteMatcher(routeTree) {
|
|
35
|
+
// Build bucket-indexed pattern matchers for routes with dynamic segments
|
|
36
|
+
const patternBuckets = new Map();
|
|
37
|
+
let patternOrder = 0;
|
|
38
|
+
const URLPatternCtor = getURLPatternCtor();
|
|
39
|
+
function collectPatterns(node) {
|
|
40
|
+
if (node.routable && node.pattern) {
|
|
41
|
+
if (!URLPatternCtor) {
|
|
42
|
+
throw new URLPatternUnavailableError();
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const urlPattern = new URLPatternCtor({ pathname: node.pattern });
|
|
46
|
+
const bucketKey = getIndexKey(node.pattern);
|
|
47
|
+
const bucket = patternBuckets.get(bucketKey) ?? [];
|
|
48
|
+
bucket.push({ pattern: urlPattern, node, order: patternOrder++ });
|
|
49
|
+
patternBuckets.set(bucketKey, bucket);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
throw new InvalidRoutePatternError(node.pattern, { cause: err });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (const child of node.children) {
|
|
56
|
+
collectPatterns(child);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
collectPatterns(routeTree.root);
|
|
60
|
+
return {
|
|
61
|
+
match(path) {
|
|
62
|
+
// O(1) exact match via routeTree.byPath Map (unchanged)
|
|
63
|
+
const exactMatch = routeTree.byPath.get(path);
|
|
64
|
+
if (exactMatch?.routable) {
|
|
65
|
+
return { to: `#${exactMatch.id}`, params: {} };
|
|
66
|
+
}
|
|
67
|
+
// Bucket-indexed pattern match: only scan candidates for this path's first segment
|
|
68
|
+
const candidates = getCandidates(patternBuckets, getIndexKey(path));
|
|
69
|
+
for (const { pattern, node } of candidates) {
|
|
70
|
+
const match = pattern.exec({ pathname: path });
|
|
71
|
+
if (match) {
|
|
72
|
+
const params = {};
|
|
73
|
+
if (match.pathname.groups) {
|
|
74
|
+
Object.assign(params, match.pathname.groups);
|
|
75
|
+
}
|
|
76
|
+
return { to: `#${node.id}`, params };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { to: null, params: {} };
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=create-route-matcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-route-matcher.js","sourceRoot":"","sources":["../src/create-route-matcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAEN,iBAAiB,EACjB,WAAW,EACX,aAAa,GACb,MAAM,wBAAwB,CAAC;AAmChC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAoB;IACtD,yEAAyE;IACzE,MAAM,cAAc,GAAG,IAAI,GAAG,EAG3B,CAAC;IACJ,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAC;IAE3C,SAAS,eAAe,CAAC,IAAe;QACvC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACnC,IAAI,CAAC,cAAc,EAAE,CAAC;gBACrB,MAAM,IAAI,0BAA0B,EAAE,CAAC;YACxC,CAAC;YACD,IAAI,CAAC;gBACJ,MAAM,UAAU,GAAG,IAAI,cAAc,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;gBAClE,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAC5C,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;gBACnD,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;gBAClE,cAAc,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACvC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,IAAI,wBAAwB,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAClE,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,KAAK,CAAC,IAAY;YACjB,wDAAwD;YACxD,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,UAAU,EAAE,QAAQ,EAAE,CAAC;gBAC1B,OAAO,EAAE,EAAE,EAAE,IAAI,UAAU,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;YAChD,CAAC;YAED,mFAAmF;YACnF,MAAM,UAAU,GAAG,aAAa,CAAC,cAAc,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;YACpE,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,UAAU,EAAE,CAAC;gBAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/C,IAAI,KAAK,EAAE,CAAC;oBACX,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;oBACD,OAAO,EAAE,EAAE,EAAE,IAAI,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;gBACtC,CAAC;YACF,CAAC;YAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACjC,CAAC;KACD,CAAC;AACH,CAAC"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -24,7 +24,38 @@ export declare class RouterSyncError extends PlayError {
|
|
|
24
24
|
constructor(message: string, options?: ErrorOptions);
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
* Thrown by `
|
|
27
|
+
* Thrown by `RouterBridgeBase.connect()` when a second bridge tries to connect
|
|
28
|
+
* to an actor that already has an active bridge connection.
|
|
29
|
+
*
|
|
30
|
+
* One actor may only be connected to one router bridge at a time. Two bridges
|
|
31
|
+
* sharing the same actor would both receive and process every router navigation
|
|
32
|
+
* event, sending duplicate `play.route` events to the actor and producing
|
|
33
|
+
* conflicting echo-suppression state via `lastSyncedPath`.
|
|
34
|
+
*
|
|
35
|
+
* **How to fix:** Call `bridge.disconnect()` on the first bridge before
|
|
36
|
+
* connecting a second one, or ensure only one `connectRouter()` call is active
|
|
37
|
+
* per actor at any given time.
|
|
38
|
+
*
|
|
39
|
+
* **Error code:** `PLAY_ROUTER_DUPLICATE_BRIDGE`
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* import { DuplicateBridgeError } from "@xmachines/play-router/errors";
|
|
44
|
+
*
|
|
45
|
+
* try {
|
|
46
|
+
* bridge.connect();
|
|
47
|
+
* } catch (err) {
|
|
48
|
+
* if (err instanceof DuplicateBridgeError) {
|
|
49
|
+
* console.error("Actor already has an active router bridge — disconnect first.");
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare class DuplicateBridgeError extends PlayError {
|
|
55
|
+
constructor();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Thrown by `createRouteMatcher()` when the `URLPattern` API is not available in the
|
|
28
59
|
* current runtime environment and no polyfill has been loaded.
|
|
29
60
|
*
|
|
30
61
|
* **How to fix:** Load `urlpattern-polyfill` at your application entry point, before
|
|
@@ -44,7 +75,7 @@ export declare class URLPatternUnavailableError extends PlayError {
|
|
|
44
75
|
constructor();
|
|
45
76
|
}
|
|
46
77
|
/**
|
|
47
|
-
* Thrown by `
|
|
78
|
+
* Thrown by `createRouteMatcher()` when a route pattern string cannot be compiled by
|
|
48
79
|
* `URLPattern`. The `pattern` field contains the rejected string; `cause` wraps the
|
|
49
80
|
* original `URLPattern` constructor error.
|
|
50
81
|
*
|
|
@@ -59,7 +90,7 @@ export declare class URLPatternUnavailableError extends PlayError {
|
|
|
59
90
|
* import { InvalidRoutePatternError } from "@xmachines/play-router/errors";
|
|
60
91
|
*
|
|
61
92
|
* try {
|
|
62
|
-
* const routeMatcher =
|
|
93
|
+
* const routeMatcher = createRouteMatcher(tree);
|
|
63
94
|
* } catch (err) {
|
|
64
95
|
* if (err instanceof InvalidRoutePatternError) {
|
|
65
96
|
* console.error(`Bad route pattern: "${err.pattern}"`, err.cause);
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,eAAgB,SAAQ,SAAS;gBACjC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAInD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,0BAA2B,SAAQ,SAAS;;CASxD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAS3B;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAS3B;AAED;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,SAAS;IACrD,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;gBAElB,UAAU,EAAE,MAAM,EAAE;CAWhC;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,SAAS;IACnD,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE;CAWnE;AAED,qBAAa,wBAAyB,SAAQ,SAAS;IACtD,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAUnD"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,eAAgB,SAAQ,SAAS;gBACjC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAInD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,oBAAqB,SAAQ,SAAS;;CAUlD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,0BAA2B,SAAQ,SAAS;;CASxD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAS3B;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IACjD,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAS3B;AAED;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,SAAS;IACrD,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;gBAElB,UAAU,EAAE,MAAM,EAAE;CAWhC;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,SAAS;IACnD,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE;CAWnE;AAED,qBAAa,wBAAyB,SAAQ,SAAS;IACtD,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAUnD"}
|
package/dist/errors.js
CHANGED
|
@@ -27,7 +27,42 @@ export class RouterSyncError extends PlayError {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
|
-
* Thrown by `
|
|
30
|
+
* Thrown by `RouterBridgeBase.connect()` when a second bridge tries to connect
|
|
31
|
+
* to an actor that already has an active bridge connection.
|
|
32
|
+
*
|
|
33
|
+
* One actor may only be connected to one router bridge at a time. Two bridges
|
|
34
|
+
* sharing the same actor would both receive and process every router navigation
|
|
35
|
+
* event, sending duplicate `play.route` events to the actor and producing
|
|
36
|
+
* conflicting echo-suppression state via `lastSyncedPath`.
|
|
37
|
+
*
|
|
38
|
+
* **How to fix:** Call `bridge.disconnect()` on the first bridge before
|
|
39
|
+
* connecting a second one, or ensure only one `connectRouter()` call is active
|
|
40
|
+
* per actor at any given time.
|
|
41
|
+
*
|
|
42
|
+
* **Error code:** `PLAY_ROUTER_DUPLICATE_BRIDGE`
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* import { DuplicateBridgeError } from "@xmachines/play-router/errors";
|
|
47
|
+
*
|
|
48
|
+
* try {
|
|
49
|
+
* bridge.connect();
|
|
50
|
+
* } catch (err) {
|
|
51
|
+
* if (err instanceof DuplicateBridgeError) {
|
|
52
|
+
* console.error("Actor already has an active router bridge — disconnect first.");
|
|
53
|
+
* }
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export class DuplicateBridgeError extends PlayError {
|
|
58
|
+
constructor() {
|
|
59
|
+
super("RouterBridgeBase", "PLAY_ROUTER_DUPLICATE_BRIDGE", "An actor can only be connected to one router bridge at a time. " +
|
|
60
|
+
"Call disconnect() on the existing bridge before connecting a new one.");
|
|
61
|
+
this.name = "DuplicateBridgeError";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Thrown by `createRouteMatcher()` when the `URLPattern` API is not available in the
|
|
31
66
|
* current runtime environment and no polyfill has been loaded.
|
|
32
67
|
*
|
|
33
68
|
* **How to fix:** Load `urlpattern-polyfill` at your application entry point, before
|
|
@@ -50,7 +85,7 @@ export class URLPatternUnavailableError extends PlayError {
|
|
|
50
85
|
}
|
|
51
86
|
}
|
|
52
87
|
/**
|
|
53
|
-
* Thrown by `
|
|
88
|
+
* Thrown by `createRouteMatcher()` when a route pattern string cannot be compiled by
|
|
54
89
|
* `URLPattern`. The `pattern` field contains the rejected string; `cause` wraps the
|
|
55
90
|
* original `URLPattern` constructor error.
|
|
56
91
|
*
|
|
@@ -65,7 +100,7 @@ export class URLPatternUnavailableError extends PlayError {
|
|
|
65
100
|
* import { InvalidRoutePatternError } from "@xmachines/play-router/errors";
|
|
66
101
|
*
|
|
67
102
|
* try {
|
|
68
|
-
* const routeMatcher =
|
|
103
|
+
* const routeMatcher = createRouteMatcher(tree);
|
|
69
104
|
* } catch (err) {
|
|
70
105
|
* if (err instanceof InvalidRoutePatternError) {
|
|
71
106
|
* console.error(`Bad route pattern: "${err.pattern}"`, err.cause);
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,OAAO,eAAgB,SAAQ,SAAS;IAC7C,YAAY,OAAe,EAAE,OAAsB;QAClD,KAAK,CAAC,kBAAkB,EAAE,yBAAyB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAC/B,CAAC;CACD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,0BAA2B,SAAQ,SAAS;IACxD;QACC,KAAK,CACJ,UAAU,EACV,uCAAuC,EACvC,6IAA6I,CAC7I,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC1C,CAAC;CACD;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH;;;;;GAKG;AACH,MAAM,OAAO,mBAAoB,SAAQ,SAAS;IACjD,8CAA8C;IACrC,OAAO,CAAS;IAEzB,YAAY,OAAe;QAC1B,KAAK,CACJ,UAAU,EACV,uBAAuB,EACvB,8BAA8B,OAAO,sCAAsC,CAC3E,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC;CACD;AAED;;;;;GAKG;AACH,MAAM,OAAO,mBAAoB,SAAQ,SAAS;IACjD,sDAAsD;IAC7C,OAAO,CAAS;IAEzB,YAAY,OAAe;QAC1B,KAAK,CACJ,UAAU,EACV,6BAA6B,EAC7B,2CAA2C,OAAO,EAAE,CACpD,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC;CACD;AAED;;;;GAIG;AACH,MAAM,OAAO,uBAAwB,SAAQ,SAAS;IACrD,mEAAmE;IAC1D,UAAU,CAAW;IAE9B,YAAY,UAAoB;QAC/B,KAAK,CACJ,UAAU,EACV,2BAA2B,EAC3B,oCAAoC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM;YAC9D,kDAAkD;YAClD,6FAA6F,CAC9F,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC9B,CAAC;CACD;AAED;;;;;GAKG;AACH,MAAM,OAAO,qBAAsB,SAAQ,SAAS;IACnD,0CAA0C;IACjC,SAAS,CAAS;IAC3B,sDAAsD;IAC7C,MAAM,CAAS;IAExB,YAAY,SAAiB,EAAE,MAAc,EAAE,UAAoB;QAClE,KAAK,CACJ,gBAAgB,EAChB,+BAA+B,EAC/B,8BAA8B,SAAS,cAAc,MAAM,KAAK;YAC/D,oBAAoB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC7C,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;CACD;AAED,MAAM,OAAO,wBAAyB,SAAQ,SAAS;IACtD,yEAAyE;IAChE,OAAO,CAAS;IAEzB,YAAY,OAAe,EAAE,OAAsB;QAClD,KAAK,CACJ,UAAU,EACV,gCAAgC,EAChC,2BAA2B,OAAO,GAAG,EACrC,OAAO,CACP,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC;CACD"}
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,9 +9,10 @@ export { getNavigableRoutes, getRoutableRoutes, routeExists, getTransitionReacha
|
|
|
9
9
|
export { machineToGraph } from "./machine-to-graph.js";
|
|
10
10
|
export type { MachineGraph } from "./machine-to-graph.js";
|
|
11
11
|
export type { RouteInfo, RouteNode, RouteTree, RouteObject, RouteMetadata, PlayRouteEvent, RouterBridge, MachineNodeData, MachineEdgeData, WindowLike, LocationLike, } from "./types.js";
|
|
12
|
-
export {
|
|
12
|
+
export { createRouteMatcher, type RouteMatcher } from "./create-route-matcher.js";
|
|
13
13
|
export { RouteMap, type RouteMapping, type RouteMapping as BaseRouteMapping, } from "./base-route-map.js";
|
|
14
|
-
export {
|
|
14
|
+
export { createRouteMap } from "./create-route-map.js";
|
|
15
|
+
export type { RouteMapOptions } from "./create-route-map.js";
|
|
15
16
|
export { createRouteMapFromTree } from "./create-route-map-from-tree.js";
|
|
16
17
|
export { findRouteById, findRouteByPath } from "./find-route.js";
|
|
17
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;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,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,4BAA4B,EAC5B,gBAAgB,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,YAAY,EACX,SAAS,EACT,SAAS,EACT,SAAS,EACT,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,EACZ,eAAe,EACf,eAAe,EACf,UAAU,EACV,YAAY,GACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,
|
|
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,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,4BAA4B,EAC5B,gBAAgB,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,YAAY,EACX,SAAS,EACT,SAAS,EACT,SAAS,EACT,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,EACZ,eAAe,EACf,eAAe,EACf,UAAU,EACV,YAAY,GACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAIlF,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
|
@@ -9,14 +9,14 @@ export { extractMachineRoutes } from "./extract-routes.js";
|
|
|
9
9
|
export { getNavigableRoutes, getRoutableRoutes, routeExists, getTransitionReachableRoutes, isRouteReachable, } from "./query.js";
|
|
10
10
|
// Graph adapter — converts XState machines to @statelyai/graph Graph
|
|
11
11
|
export { machineToGraph } from "./machine-to-graph.js";
|
|
12
|
-
// URL-pattern-based matcher (
|
|
13
|
-
export {
|
|
12
|
+
// URL-pattern-based matcher (createRouteMatcher / RouteMatcher interface)
|
|
13
|
+
export { createRouteMatcher } from "./create-route-matcher.js";
|
|
14
14
|
// Shared bidirectional route mapping base class.
|
|
15
15
|
// RouteMapping is exported directly; BaseRouteMapping kept as alias for compatibility.
|
|
16
16
|
export { RouteMap, } from "./base-route-map.js";
|
|
17
17
|
// Factories: create a RouteMap from a machine or a route tree.
|
|
18
18
|
// All RouterBridgeBase adapters use these instead of rolling their own factories.
|
|
19
|
-
export {
|
|
19
|
+
export { createRouteMap } from "./create-route-map.js";
|
|
20
20
|
export { createRouteMapFromTree } from "./create-route-map-from-tree.js";
|
|
21
21
|
// Route lookup helpers — matchesPattern is private within find-route.ts
|
|
22
22
|
export { findRouteById, findRouteByPath } from "./find-route.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;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;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,4BAA4B,EAC5B,gBAAgB,GAChB,MAAM,YAAY,CAAC;AAEpB,qEAAqE;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAiBvD,
|
|
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;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,EACX,4BAA4B,EAC5B,gBAAgB,GAChB,MAAM,YAAY,CAAC;AAEpB,qEAAqE;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAiBvD,0EAA0E;AAC1E,OAAO,EAAE,kBAAkB,EAAqB,MAAM,2BAA2B,CAAC;AAElF,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"}
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Captures the 90% identical logic from all existing router bridges:
|
|
5
5
|
* - TC39 Signal watcher for actor → router direction
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
6
|
+
* - `lastSyncedPath` for echo suppression in the actor→router direction
|
|
7
|
+
* - `isProcessingNavigation` flag for guard-redirect loop prevention in `syncActorFromRouter` only
|
|
8
|
+
* - `syncRouterFromActor` / `syncActorFromRouter` with URL parameter extraction
|
|
8
9
|
* - connect / disconnect lifecycle matching the RouterBridge protocol
|
|
9
10
|
*
|
|
10
11
|
* Subclasses implement only the 3 framework-specific abstract methods:
|
|
@@ -78,13 +79,23 @@ export declare abstract class RouterBridgeBase implements RouterBridge {
|
|
|
78
79
|
protected isConnected: boolean;
|
|
79
80
|
protected hasConnectedOnce: boolean;
|
|
80
81
|
protected lastSyncedPath: string | null;
|
|
82
|
+
/**
|
|
83
|
+
* Guards `syncActorFromRouter` against re-entrant calls triggered by the
|
|
84
|
+
* actor's own guard redirects (router→actor send → signal fires → actor→router
|
|
85
|
+
* push → another syncActorFromRouter before the first one returns).
|
|
86
|
+
*
|
|
87
|
+
* NOT used for actor→router echo suppression — that is handled exclusively by
|
|
88
|
+
* `lastSyncedPath`, which is updated before `navigateRouter()` is called so
|
|
89
|
+
* any router callback for the same path short-circuits at the
|
|
90
|
+
* `sanitized === lastSyncedPath` check in `syncActorFromRouter`.
|
|
91
|
+
*/
|
|
81
92
|
protected isProcessingNavigation: boolean;
|
|
82
93
|
protected routeWatcher: RouteWatcherHandle | null;
|
|
83
94
|
/**
|
|
84
95
|
* @param actor - A `Routable` actor exposing `currentRoute` and `send`.
|
|
85
96
|
* @param routeMap - Bidirectional route map for `stateId ↔ path` resolution.
|
|
86
97
|
* Provide `getStateIdByPath` and `getPathByStateId`. Framework adapters
|
|
87
|
-
* typically wrap the result of `createRouteMap()` or an equivalent.
|
|
98
|
+
* typically wrap the result of `createRouteMap(machine)` or an equivalent.
|
|
88
99
|
*/
|
|
89
100
|
constructor(actor: AbstractActor<AnyActorLogic> & Routable, routeMap: {
|
|
90
101
|
getStateIdByPath(path: string): string | null | undefined;
|
|
@@ -115,11 +126,15 @@ export declare abstract class RouterBridgeBase implements RouterBridge {
|
|
|
115
126
|
* Sync router location when actor route signal changes.
|
|
116
127
|
*
|
|
117
128
|
* Calls navigateRouter() for framework-specific navigation.
|
|
118
|
-
* Prevents circular updates via isProcessingNavigation flag.
|
|
119
129
|
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
130
|
+
* Echo suppression — preventing the router's own callback from re-driving the
|
|
131
|
+
* actor — is handled entirely by `lastSyncedPath`: it is set to `route` before
|
|
132
|
+
* `navigateRouter()` is called, so any `syncActorFromRouter` invocation for the
|
|
133
|
+
* same path short-circuits at the `sanitized === lastSyncedPath` check and sends
|
|
134
|
+
* no event regardless of whether the callback fires synchronously or asynchronously.
|
|
135
|
+
*
|
|
136
|
+
* `isProcessingNavigation` is NOT set here — it is only used inside
|
|
137
|
+
* `syncActorFromRouter` to guard against re-entrant guard-redirect loops.
|
|
123
138
|
*/
|
|
124
139
|
protected syncRouterFromActor(route: string | null | unknown): void;
|
|
125
140
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router-bridge-base.d.ts","sourceRoot":"","sources":["../src/router-bridge-base.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"router-bridge-base.d.ts","sourceRoot":"","sources":["../src/router-bridge-base.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Captures the 90% identical logic from all existing router bridges:
|
|
5
5
|
* - TC39 Signal watcher for actor → router direction
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
6
|
+
* - `lastSyncedPath` for echo suppression in the actor→router direction
|
|
7
|
+
* - `isProcessingNavigation` flag for guard-redirect loop prevention in `syncActorFromRouter` only
|
|
8
|
+
* - `syncRouterFromActor` / `syncActorFromRouter` with URL parameter extraction
|
|
8
9
|
* - connect / disconnect lifecycle matching the RouterBridge protocol
|
|
9
10
|
*
|
|
10
11
|
* Subclasses implement only the 3 framework-specific abstract methods:
|
|
@@ -41,9 +42,19 @@
|
|
|
41
42
|
* @see [Play RFC](../../docs/rfc/play.md) - Invariant INV-04
|
|
42
43
|
*/
|
|
43
44
|
import { Signal, watchSignal } from "@xmachines/play-signals";
|
|
44
|
-
import { RouterSyncError } from "./errors.js";
|
|
45
|
+
import { DuplicateBridgeError, RouterSyncError } from "./errors.js";
|
|
45
46
|
import { buildPlayRouteEvent, extractQuery, extractRouteParams, matchRouteMap, sanitizePathname, } from "./router-sync.js";
|
|
46
47
|
import { URLPatternUnavailableError } from "./errors.js";
|
|
48
|
+
/**
|
|
49
|
+
* Module-level registry of actors that currently have an active bridge connection.
|
|
50
|
+
*
|
|
51
|
+
* Keyed on the actor instance (WeakMap — no reference retention). Used to detect and
|
|
52
|
+
* reject duplicate `connect()` calls for the same actor, which would produce duplicate
|
|
53
|
+
* `play.route` events and conflicting `lastSyncedPath` echo-suppression state.
|
|
54
|
+
*
|
|
55
|
+
* @internal
|
|
56
|
+
*/
|
|
57
|
+
const activeBridges = new WeakMap();
|
|
47
58
|
/** Strip leading '#' for normalized stateId comparison. */
|
|
48
59
|
function normalizeId(id) {
|
|
49
60
|
return id.startsWith("#") ? id.slice(1) : id;
|
|
@@ -62,13 +73,23 @@ export class RouterBridgeBase {
|
|
|
62
73
|
isConnected = false;
|
|
63
74
|
hasConnectedOnce = false;
|
|
64
75
|
lastSyncedPath = null;
|
|
76
|
+
/**
|
|
77
|
+
* Guards `syncActorFromRouter` against re-entrant calls triggered by the
|
|
78
|
+
* actor's own guard redirects (router→actor send → signal fires → actor→router
|
|
79
|
+
* push → another syncActorFromRouter before the first one returns).
|
|
80
|
+
*
|
|
81
|
+
* NOT used for actor→router echo suppression — that is handled exclusively by
|
|
82
|
+
* `lastSyncedPath`, which is updated before `navigateRouter()` is called so
|
|
83
|
+
* any router callback for the same path short-circuits at the
|
|
84
|
+
* `sanitized === lastSyncedPath` check in `syncActorFromRouter`.
|
|
85
|
+
*/
|
|
65
86
|
isProcessingNavigation = false;
|
|
66
87
|
routeWatcher = null;
|
|
67
88
|
/**
|
|
68
89
|
* @param actor - A `Routable` actor exposing `currentRoute` and `send`.
|
|
69
90
|
* @param routeMap - Bidirectional route map for `stateId ↔ path` resolution.
|
|
70
91
|
* Provide `getStateIdByPath` and `getPathByStateId`. Framework adapters
|
|
71
|
-
* typically wrap the result of `createRouteMap()` or an equivalent.
|
|
92
|
+
* typically wrap the result of `createRouteMap(machine)` or an equivalent.
|
|
72
93
|
*/
|
|
73
94
|
constructor(actor, routeMap) {
|
|
74
95
|
this.actor = actor;
|
|
@@ -96,6 +117,14 @@ export class RouterBridgeBase {
|
|
|
96
117
|
if (this.isConnected) {
|
|
97
118
|
return;
|
|
98
119
|
}
|
|
120
|
+
// Enforce single-bridge-per-actor: two connected bridges for the same actor
|
|
121
|
+
// would both receive every router navigation event, sending duplicate play.route
|
|
122
|
+
// events and producing conflicting lastSyncedPath echo-suppression state.
|
|
123
|
+
const existingBridge = activeBridges.get(this.actor);
|
|
124
|
+
if (existingBridge !== undefined && existingBridge !== this) {
|
|
125
|
+
throw new DuplicateBridgeError();
|
|
126
|
+
}
|
|
127
|
+
activeBridges.set(this.actor, this);
|
|
99
128
|
this.isConnected = true;
|
|
100
129
|
this.hasConnectedOnce = true;
|
|
101
130
|
// Set up TC39 Signal watcher for actor → router direction
|
|
@@ -186,17 +215,27 @@ export class RouterBridgeBase {
|
|
|
186
215
|
}
|
|
187
216
|
this.isProcessingNavigation = false;
|
|
188
217
|
this.isConnected = false;
|
|
218
|
+
// Release the actor slot so a new bridge can connect to the same actor.
|
|
219
|
+
// Only clear if this bridge is the currently registered one — a second bridge
|
|
220
|
+
// that was rejected at connect() time must not evict the legitimate bridge.
|
|
221
|
+
if (activeBridges.get(this.actor) === this) {
|
|
222
|
+
activeBridges.delete(this.actor);
|
|
223
|
+
}
|
|
189
224
|
}
|
|
190
225
|
// ── Sync methods (protected, overridable if subclass needs custom behavior) ──
|
|
191
226
|
/**
|
|
192
227
|
* Sync router location when actor route signal changes.
|
|
193
228
|
*
|
|
194
229
|
* Calls navigateRouter() for framework-specific navigation.
|
|
195
|
-
* Prevents circular updates via isProcessingNavigation flag.
|
|
196
230
|
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
231
|
+
* Echo suppression — preventing the router's own callback from re-driving the
|
|
232
|
+
* actor — is handled entirely by `lastSyncedPath`: it is set to `route` before
|
|
233
|
+
* `navigateRouter()` is called, so any `syncActorFromRouter` invocation for the
|
|
234
|
+
* same path short-circuits at the `sanitized === lastSyncedPath` check and sends
|
|
235
|
+
* no event regardless of whether the callback fires synchronously or asynchronously.
|
|
236
|
+
*
|
|
237
|
+
* `isProcessingNavigation` is NOT set here — it is only used inside
|
|
238
|
+
* `syncActorFromRouter` to guard against re-entrant guard-redirect loops.
|
|
200
239
|
*/
|
|
201
240
|
syncRouterFromActor(route) {
|
|
202
241
|
if (this.hasConnectedOnce && !this.isConnected)
|
|
@@ -205,14 +244,18 @@ export class RouterBridgeBase {
|
|
|
205
244
|
return;
|
|
206
245
|
if (route === this.lastSyncedPath)
|
|
207
246
|
return;
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
247
|
+
// Resolve the raw actor route (stateId or concrete path) to the concrete URL
|
|
248
|
+
// path that will actually be pushed. lastSyncedPath must store the concrete
|
|
249
|
+
// path so that the router callback (which fires with the concrete path) matches
|
|
250
|
+
// and short-circuits in syncActorFromRouter. Storing the raw stateId (e.g.
|
|
251
|
+
// "#home") would cause a mismatch against the sanitized path ("/") in the watcher.
|
|
252
|
+
//
|
|
253
|
+
// When resolveNavigationPath returns null (parameterized pattern, unknown id)
|
|
254
|
+
// navigateRouter will skip the push — set lastSyncedPath to route as before so
|
|
255
|
+
// the dedup guard still fires correctly on the next identical signal value.
|
|
256
|
+
const resolved = this.resolveNavigationPath(route);
|
|
257
|
+
this.lastSyncedPath = resolved ?? route;
|
|
212
258
|
this.navigateRouter(route);
|
|
213
|
-
queueMicrotask(() => {
|
|
214
|
-
this.isProcessingNavigation = false;
|
|
215
|
-
});
|
|
216
259
|
}
|
|
217
260
|
/**
|
|
218
261
|
* Sync actor state when router location changes.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router-bridge-base.js","sourceRoot":"","sources":["../src/router-bridge-base.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"router-bridge-base.js","sourceRoot":"","sources":["../src/router-bridge-base.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -78,7 +78,7 @@ export interface RouteNode {
|
|
|
78
78
|
/**
|
|
79
79
|
* The fully resolved absolute path from the root (e.g. `"/dashboard/overview"`).
|
|
80
80
|
* Always use `fullPath` for browser URL matching and route map construction.
|
|
81
|
-
* `createRouteMapFromTree` and `
|
|
81
|
+
* `createRouteMapFromTree` and `createRouteMap` both use this field.
|
|
82
82
|
*/
|
|
83
83
|
fullPath: string;
|
|
84
84
|
/** Route pattern with parameters (e.g., /profile/:userId) if path contains params */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmachines/play-router",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.44",
|
|
4
4
|
"description": "Route tree extraction from XState v5 state machines. Part of @xmachines/play Universal Player Architecture.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"routing",
|
|
@@ -47,15 +47,15 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@statelyai/graph": "^0.11.0",
|
|
50
|
-
"@xmachines/play": "1.0.0-beta.
|
|
51
|
-
"@xmachines/play-actor": "1.0.0-beta.
|
|
52
|
-
"@xmachines/play-signals": "1.0.0-beta.
|
|
50
|
+
"@xmachines/play": "1.0.0-beta.44",
|
|
51
|
+
"@xmachines/play-actor": "1.0.0-beta.44",
|
|
52
|
+
"@xmachines/play-signals": "1.0.0-beta.44",
|
|
53
53
|
"quick-lru": "^7.3.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/node": "^25.6.0",
|
|
57
|
-
"@xmachines/play-xstate": "1.0.0-beta.
|
|
58
|
-
"@xmachines/shared": "1.0.0-beta.
|
|
57
|
+
"@xmachines/play-xstate": "1.0.0-beta.44",
|
|
58
|
+
"@xmachines/shared": "1.0.0-beta.44",
|
|
59
59
|
"oxfmt": "^0.45.0",
|
|
60
60
|
"oxlint": "^1.60.0",
|
|
61
61
|
"typescript": "^5.9.3 || ^6.0.3",
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { AnyStateMachine } from "xstate";
|
|
2
|
-
import { RouteMap } from "./base-route-map.js";
|
|
3
|
-
/**
|
|
4
|
-
* Create a `RouteMap` from an XState state machine.
|
|
5
|
-
*
|
|
6
|
-
* Extracts all routable states (those with `meta.route`) and builds a bidirectional
|
|
7
|
-
* path ↔ stateId lookup structure. The returned map is used by `RouterBridgeBase`
|
|
8
|
-
* subclasses to translate browser URL changes into `play.route` actor events and
|
|
9
|
-
* vice-versa.
|
|
10
|
-
*
|
|
11
|
-
* @param machine - XState v5 state machine with `meta.route` annotations on states.
|
|
12
|
-
* @returns A `RouteMap` for passing to any `RouterBridgeBase`-based adapter.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```typescript
|
|
16
|
-
* import { createRouteMapFromMachine } from '@xmachines/play-router';
|
|
17
|
-
* import { connectRouter } from '@xmachines/play-dom-router';
|
|
18
|
-
*
|
|
19
|
-
* const routeMap = createRouteMapFromMachine(myMachine);
|
|
20
|
-
* const disconnect = connectRouter({ actor, router, routeMap });
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
export declare function createRouteMapFromMachine(machine: AnyStateMachine): RouteMap;
|
|
24
|
-
//# sourceMappingURL=create-route-map-from-machine.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-route-map-from-machine.d.ts","sourceRoot":"","sources":["../src/create-route-map-from-machine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAG9C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CAS5E"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { extractMachineRoutes } from "./extract-routes.js";
|
|
2
|
-
import { getRoutableRoutes } from "./query.js";
|
|
3
|
-
import { RouteMap } from "./base-route-map.js";
|
|
4
|
-
/**
|
|
5
|
-
* Create a `RouteMap` from an XState state machine.
|
|
6
|
-
*
|
|
7
|
-
* Extracts all routable states (those with `meta.route`) and builds a bidirectional
|
|
8
|
-
* path ↔ stateId lookup structure. The returned map is used by `RouterBridgeBase`
|
|
9
|
-
* subclasses to translate browser URL changes into `play.route` actor events and
|
|
10
|
-
* vice-versa.
|
|
11
|
-
*
|
|
12
|
-
* @param machine - XState v5 state machine with `meta.route` annotations on states.
|
|
13
|
-
* @returns A `RouteMap` for passing to any `RouterBridgeBase`-based adapter.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```typescript
|
|
17
|
-
* import { createRouteMapFromMachine } from '@xmachines/play-router';
|
|
18
|
-
* import { connectRouter } from '@xmachines/play-dom-router';
|
|
19
|
-
*
|
|
20
|
-
* const routeMap = createRouteMapFromMachine(myMachine);
|
|
21
|
-
* const disconnect = connectRouter({ actor, router, routeMap });
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export function createRouteMapFromMachine(machine) {
|
|
25
|
-
const routeTree = extractMachineRoutes(machine);
|
|
26
|
-
const routes = getRoutableRoutes(routeTree);
|
|
27
|
-
return new RouteMap(routes.map((node) => ({
|
|
28
|
-
stateId: node.stateId,
|
|
29
|
-
path: node.fullPath,
|
|
30
|
-
})));
|
|
31
|
-
}
|
|
32
|
-
//# sourceMappingURL=create-route-map-from-machine.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-route-map-from-machine.js","sourceRoot":"","sources":["../src/create-route-map-from-machine.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAwB;IACjE,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC5C,OAAO,IAAI,QAAQ,CAClB,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACrB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,IAAI,CAAC,QAAQ;KACnB,CAAC,CAAC,CACH,CAAC;AACH,CAAC"}
|