@xmachines/play-router 1.0.0-beta.9 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +246 -341
- package/dist/base-route-map.d.ts +38 -17
- package/dist/base-route-map.d.ts.map +1 -1
- package/dist/base-route-map.js +69 -62
- package/dist/base-route-map.js.map +1 -1
- package/dist/build-tree.d.ts +7 -0
- package/dist/build-tree.d.ts.map +1 -1
- package/dist/build-tree.js +21 -6
- package/dist/build-tree.js.map +1 -1
- package/dist/create-route-map-from-tree.d.ts +33 -0
- package/dist/create-route-map-from-tree.d.ts.map +1 -0
- package/dist/create-route-map-from-tree.js +43 -0
- package/dist/create-route-map-from-tree.js.map +1 -0
- 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 -114
- package/dist/create-route-map.js.map +1 -1
- package/dist/errors.d.ts +152 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +186 -0
- package/dist/errors.js.map +1 -0
- package/dist/extract-routes.d.ts +5 -31
- package/dist/extract-routes.d.ts.map +1 -1
- package/dist/extract-routes.js +73 -51
- package/dist/extract-routes.js.map +1 -1
- package/dist/find-route.d.ts +44 -0
- package/dist/find-route.d.ts.map +1 -0
- package/dist/find-route.js +103 -0
- package/dist/find-route.js.map +1 -0
- package/dist/index.d.ts +13 -52
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -137
- package/dist/index.js.map +1 -1
- package/dist/machine-to-graph.d.ts +17 -0
- package/dist/machine-to-graph.d.ts.map +1 -0
- package/dist/machine-to-graph.js +115 -0
- package/dist/machine-to-graph.js.map +1 -0
- package/dist/query.d.ts +49 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +98 -3
- package/dist/query.js.map +1 -1
- package/dist/router-bridge-base.d.ts +187 -48
- package/dist/router-bridge-base.d.ts.map +1 -1
- package/dist/router-bridge-base.js +312 -120
- package/dist/router-bridge-base.js.map +1 -1
- package/dist/router-sync.d.ts +95 -0
- package/dist/router-sync.d.ts.map +1 -0
- package/dist/router-sync.js +161 -0
- package/dist/router-sync.js.map +1 -0
- package/dist/types.d.ts +163 -22
- package/dist/types.d.ts.map +1 -1
- package/dist/url-pattern-utils.d.ts +117 -0
- package/dist/url-pattern-utils.d.ts.map +1 -0
- package/dist/url-pattern-utils.js +176 -0
- package/dist/url-pattern-utils.js.map +1 -0
- package/dist/validate-routes.d.ts +32 -14
- package/dist/validate-routes.d.ts.map +1 -1
- package/dist/validate-routes.js +28 -21
- package/dist/validate-routes.js.map +1 -1
- package/package.json +34 -14
- package/dist/connect-router.d.ts +0 -56
- package/dist/connect-router.d.ts.map +0 -1
- package/dist/connect-router.js +0 -112
- package/dist/connect-router.js.map +0 -1
- package/dist/crawl-machine.d.ts +0 -74
- package/dist/crawl-machine.d.ts.map +0 -1
- package/dist/crawl-machine.js +0 -95
- package/dist/crawl-machine.js.map +0 -1
- package/dist/create-browser-history.d.ts +0 -93
- package/dist/create-browser-history.d.ts.map +0 -1
- package/dist/create-browser-history.js +0 -94
- package/dist/create-browser-history.js.map +0 -1
- package/dist/create-router.d.ts +0 -73
- package/dist/create-router.d.ts.map +0 -1
- package/dist/create-router.js +0 -63
- package/dist/create-router.js.map +0 -1
- package/dist/extract-route.d.ts +0 -25
- package/dist/extract-route.d.ts.map +0 -1
- package/dist/extract-route.js +0 -63
- package/dist/extract-route.js.map +0 -1
package/dist/base-route-map.d.ts
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* RouteMap — Shared bidirectional route mapping base class
|
|
3
3
|
*
|
|
4
4
|
* Provides bucket-based pattern matching shared across all framework adapters.
|
|
5
5
|
* Adapters extend this class rather than duplicating the pattern-match logic.
|
|
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.
|
|
9
10
|
*/
|
|
10
11
|
/**
|
|
11
12
|
* A single state ID ↔ path mapping entry.
|
|
12
13
|
*
|
|
13
|
-
* Both fields are `readonly` — mappings are immutable once passed to `
|
|
14
|
+
* Both fields are `readonly` — mappings are immutable once passed to `RouteMap`.
|
|
14
15
|
* Adapter packages re-export a structurally compatible `RouteMapping` type under
|
|
15
16
|
* their own name. This type is published from `@xmachines/play-router` as
|
|
16
|
-
* `
|
|
17
|
+
* `RouteMapping` to avoid name collisions with those adapter-local types.
|
|
17
18
|
*
|
|
18
19
|
* @example
|
|
19
20
|
* ```typescript
|
|
20
|
-
* const mapping:
|
|
21
|
-
* const paramMapping:
|
|
22
|
-
* const optionalMapping:
|
|
21
|
+
* const mapping: RouteMapping = { stateId: "home", path: "/" };
|
|
22
|
+
* const paramMapping: RouteMapping = { stateId: "profile", path: "/profile/:userId" };
|
|
23
|
+
* const optionalMapping: RouteMapping = { stateId: "settings", path: "/settings/:section?" };
|
|
23
24
|
* ```
|
|
24
25
|
*/
|
|
25
26
|
export interface RouteMapping {
|
|
@@ -31,24 +32,32 @@ export interface RouteMapping {
|
|
|
31
32
|
/**
|
|
32
33
|
* Shared bidirectional route map base class.
|
|
33
34
|
*
|
|
34
|
-
* All framework
|
|
35
|
+
* All framework adapters use this class as their route map — they add no logic of their
|
|
35
36
|
* own and inherit the full public API from here.
|
|
36
37
|
*
|
|
37
38
|
* **Lookup strategy:**
|
|
38
39
|
* - Static paths (no `:param`) → O(1) `Map` lookup
|
|
39
|
-
* - Dynamic paths → O(k) bucket-indexed scan using `
|
|
40
|
+
* - Dynamic paths → O(k) bucket-indexed scan using `URLPattern`, where `k` is the number
|
|
40
41
|
* of routes sharing the same first path segment
|
|
41
|
-
* - Results are cached after the first match
|
|
42
|
+
* - Results are cached after the first match in an LRU cache (default 500 entries,
|
|
43
|
+
* configurable via the `cacheSize` constructor option)
|
|
42
44
|
*
|
|
43
|
-
* **Pattern syntax** (`:param` / `:param?`):
|
|
45
|
+
* **Pattern syntax** (`:param` / `:param?` / `*`):
|
|
44
46
|
* - `:param` — required segment, matches exactly one non-`/` segment
|
|
45
47
|
* - `:param?` — optional segment, matches zero or one non-`/` segment
|
|
48
|
+
* - `*` — wildcard, matches any number of segments (URLPattern semantics)
|
|
49
|
+
*
|
|
50
|
+
* **StateId forms:** stateIds may be registered and looked up in either
|
|
51
|
+
* `"#stateId"` or `"stateId"` form — `RouteMap` canonicalizes internally.
|
|
52
|
+
* `getStateIdByPath` returns the stateId exactly as registered;
|
|
53
|
+
* `getPathByStateId` accepts both forms. Registering the same stateId in both
|
|
54
|
+
* forms refers to one entry (the later registration wins for reverse lookup).
|
|
46
55
|
*
|
|
47
56
|
* @example
|
|
48
57
|
* ```typescript
|
|
49
|
-
* import {
|
|
58
|
+
* import { RouteMap } from "@xmachines/play-router";
|
|
50
59
|
*
|
|
51
|
-
* const map = new
|
|
60
|
+
* const map = new RouteMap([
|
|
52
61
|
* { stateId: "home", path: "/" },
|
|
53
62
|
* { stateId: "profile", path: "/profile/:userId" },
|
|
54
63
|
* { stateId: "settings", path: "/settings/:section?" },
|
|
@@ -63,7 +72,8 @@ export interface RouteMapping {
|
|
|
63
72
|
* map.getPathByStateId("missing"); // null
|
|
64
73
|
* ```
|
|
65
74
|
*/
|
|
66
|
-
export declare class
|
|
75
|
+
export declare class RouteMap {
|
|
76
|
+
/** Keyed on the canonical (bare, `#`-stripped) stateId form. */
|
|
67
77
|
private stateIdToPath;
|
|
68
78
|
private pathToStateId;
|
|
69
79
|
private patternBuckets;
|
|
@@ -72,13 +82,21 @@ export declare class BaseRouteMap {
|
|
|
72
82
|
* Build a route map from an array of state ID ↔ path mappings.
|
|
73
83
|
*
|
|
74
84
|
* Static paths (no `:param`) are indexed in an O(1) `Map`.
|
|
75
|
-
* Parameterized paths are compiled to `
|
|
85
|
+
* Parameterized paths are compiled to `URLPattern` and grouped into first-segment
|
|
76
86
|
* buckets for efficient candidate selection.
|
|
77
87
|
*
|
|
78
88
|
* @param mappings - Array of `{ stateId, path }` entries. Order determines
|
|
79
89
|
* priority when multiple patterns could match the same path.
|
|
90
|
+
* @param options - Optional configuration.
|
|
91
|
+
* `options.cacheSize`: Maximum number of resolved parameterized path lookups
|
|
92
|
+
* to cache. Defaults to `500`. Increase for applications with many unique
|
|
93
|
+
* parameterized URL values (e.g. user profile pages with thousands of distinct IDs).
|
|
94
|
+
* After eviction the path falls back to the O(k) bucket pattern scan — correct
|
|
95
|
+
* but slower. Minimum effective value is `1` (QuickLRU constraint).
|
|
80
96
|
*/
|
|
81
|
-
constructor(mappings: RouteMapping[]
|
|
97
|
+
constructor(mappings: RouteMapping[], { cacheSize }?: {
|
|
98
|
+
cacheSize?: number;
|
|
99
|
+
});
|
|
82
100
|
/**
|
|
83
101
|
* Resolve a URL path to its mapped state ID.
|
|
84
102
|
*
|
|
@@ -99,17 +117,20 @@ export declare class BaseRouteMap {
|
|
|
99
117
|
/**
|
|
100
118
|
* Look up the path pattern registered for a state ID.
|
|
101
119
|
*
|
|
120
|
+
* Accepts the stateId in either `"#stateId"` or `"stateId"` form regardless of
|
|
121
|
+
* which form was used at registration — lookups are canonicalized internally,
|
|
122
|
+
* so consumers never need to try both forms.
|
|
123
|
+
*
|
|
102
124
|
* @param stateId - State machine state ID (e.g., `"profile"`, `"#settings"`)
|
|
103
125
|
* @returns The registered path pattern, or `null` if the state ID is unknown
|
|
104
126
|
*
|
|
105
127
|
* @example
|
|
106
128
|
* ```typescript
|
|
107
129
|
* map.getPathByStateId("profile"); // "/profile/:userId"
|
|
130
|
+
* map.getPathByStateId("#profile"); // "/profile/:userId" (same entry)
|
|
108
131
|
* map.getPathByStateId("missing"); // null
|
|
109
132
|
* ```
|
|
110
133
|
*/
|
|
111
134
|
getPathByStateId(stateId: string): string | null;
|
|
112
|
-
private getIndexKey;
|
|
113
|
-
private getCandidates;
|
|
114
135
|
}
|
|
115
136
|
//# sourceMappingURL=base-route-map.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-route-map.d.ts","sourceRoot":"","sources":["../src/base-route-map.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"base-route-map.d.ts","sourceRoot":"","sources":["../src/base-route-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA0BH;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,YAAY;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,qBAAa,QAAQ;IACpB,gEAAgE;IAChE,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,cAAc,CAGpB;IACF,OAAO,CAAC,cAAc,CAAkC;IAExD;;;;;;;;;;;;;;;OAeG;gBACS,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,SAAe,EAAE,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO;IAmCtF;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAuB7C;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAGhD"}
|
package/dist/base-route-map.js
CHANGED
|
@@ -1,34 +1,57 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* RouteMap — Shared bidirectional route mapping base class
|
|
3
3
|
*
|
|
4
4
|
* Provides bucket-based pattern matching shared across all framework adapters.
|
|
5
5
|
* Adapters extend this class rather than duplicating the pattern-match logic.
|
|
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.
|
|
9
10
|
*/
|
|
10
11
|
import QuickLRU from "quick-lru";
|
|
12
|
+
import { getURLPatternCtor, getCompiledPattern, getIndexKey, getCandidates, isParameterizedPattern, } from "./url-pattern-utils.js";
|
|
13
|
+
import { URLPatternUnavailableError, InvalidRoutePatternError } from "./errors.js";
|
|
14
|
+
/**
|
|
15
|
+
* Canonical stateId key: the bare form with any leading `#` stripped.
|
|
16
|
+
*
|
|
17
|
+
* `RouteMap` accepts stateIds in both `"#stateId"` and `"stateId"` form on insert
|
|
18
|
+
* and on lookup. Internally every stateId is keyed on this canonical bare form so
|
|
19
|
+
* consumers never need to re-implement dual-form handling. The *registered* form
|
|
20
|
+
* is preserved and returned by `getStateIdByPath` so the map is transparent about
|
|
21
|
+
* how routes were declared.
|
|
22
|
+
*/
|
|
23
|
+
function canonicalStateIdKey(stateId) {
|
|
24
|
+
return stateId.startsWith("#") ? stateId.slice(1) : stateId;
|
|
25
|
+
}
|
|
11
26
|
/**
|
|
12
27
|
* Shared bidirectional route map base class.
|
|
13
28
|
*
|
|
14
|
-
* All framework
|
|
29
|
+
* All framework adapters use this class as their route map — they add no logic of their
|
|
15
30
|
* own and inherit the full public API from here.
|
|
16
31
|
*
|
|
17
32
|
* **Lookup strategy:**
|
|
18
33
|
* - Static paths (no `:param`) → O(1) `Map` lookup
|
|
19
|
-
* - Dynamic paths → O(k) bucket-indexed scan using `
|
|
34
|
+
* - Dynamic paths → O(k) bucket-indexed scan using `URLPattern`, where `k` is the number
|
|
20
35
|
* of routes sharing the same first path segment
|
|
21
|
-
* - Results are cached after the first match
|
|
36
|
+
* - Results are cached after the first match in an LRU cache (default 500 entries,
|
|
37
|
+
* configurable via the `cacheSize` constructor option)
|
|
22
38
|
*
|
|
23
|
-
* **Pattern syntax** (`:param` / `:param?`):
|
|
39
|
+
* **Pattern syntax** (`:param` / `:param?` / `*`):
|
|
24
40
|
* - `:param` — required segment, matches exactly one non-`/` segment
|
|
25
41
|
* - `:param?` — optional segment, matches zero or one non-`/` segment
|
|
42
|
+
* - `*` — wildcard, matches any number of segments (URLPattern semantics)
|
|
43
|
+
*
|
|
44
|
+
* **StateId forms:** stateIds may be registered and looked up in either
|
|
45
|
+
* `"#stateId"` or `"stateId"` form — `RouteMap` canonicalizes internally.
|
|
46
|
+
* `getStateIdByPath` returns the stateId exactly as registered;
|
|
47
|
+
* `getPathByStateId` accepts both forms. Registering the same stateId in both
|
|
48
|
+
* forms refers to one entry (the later registration wins for reverse lookup).
|
|
26
49
|
*
|
|
27
50
|
* @example
|
|
28
51
|
* ```typescript
|
|
29
|
-
* import {
|
|
52
|
+
* import { RouteMap } from "@xmachines/play-router";
|
|
30
53
|
*
|
|
31
|
-
* const map = new
|
|
54
|
+
* const map = new RouteMap([
|
|
32
55
|
* { stateId: "home", path: "/" },
|
|
33
56
|
* { stateId: "profile", path: "/profile/:userId" },
|
|
34
57
|
* { stateId: "settings", path: "/settings/:section?" },
|
|
@@ -43,7 +66,8 @@ import QuickLRU from "quick-lru";
|
|
|
43
66
|
* map.getPathByStateId("missing"); // null
|
|
44
67
|
* ```
|
|
45
68
|
*/
|
|
46
|
-
export class
|
|
69
|
+
export class RouteMap {
|
|
70
|
+
/** Keyed on the canonical (bare, `#`-stripped) stateId form. */
|
|
47
71
|
stateIdToPath;
|
|
48
72
|
pathToStateId;
|
|
49
73
|
patternBuckets;
|
|
@@ -52,29 +76,45 @@ export class BaseRouteMap {
|
|
|
52
76
|
* Build a route map from an array of state ID ↔ path mappings.
|
|
53
77
|
*
|
|
54
78
|
* Static paths (no `:param`) are indexed in an O(1) `Map`.
|
|
55
|
-
* Parameterized paths are compiled to `
|
|
79
|
+
* Parameterized paths are compiled to `URLPattern` and grouped into first-segment
|
|
56
80
|
* buckets for efficient candidate selection.
|
|
57
81
|
*
|
|
58
82
|
* @param mappings - Array of `{ stateId, path }` entries. Order determines
|
|
59
83
|
* priority when multiple patterns could match the same path.
|
|
84
|
+
* @param options - Optional configuration.
|
|
85
|
+
* `options.cacheSize`: Maximum number of resolved parameterized path lookups
|
|
86
|
+
* to cache. Defaults to `500`. Increase for applications with many unique
|
|
87
|
+
* parameterized URL values (e.g. user profile pages with thousands of distinct IDs).
|
|
88
|
+
* After eviction the path falls back to the O(k) bucket pattern scan — correct
|
|
89
|
+
* but slower. Minimum effective value is `1` (QuickLRU constraint).
|
|
60
90
|
*/
|
|
61
|
-
constructor(mappings) {
|
|
91
|
+
constructor(mappings, { cacheSize = 500 } = {}) {
|
|
62
92
|
this.stateIdToPath = new Map();
|
|
63
93
|
this.pathToStateId = new Map();
|
|
64
94
|
this.patternBuckets = new Map();
|
|
65
|
-
this.pathMatchCache = new QuickLRU({ maxSize:
|
|
95
|
+
this.pathMatchCache = new QuickLRU({ maxSize: Math.max(1, cacheSize) });
|
|
66
96
|
let patternOrder = 0;
|
|
67
97
|
for (const { stateId, path } of mappings) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
98
|
+
// Key on the canonical (bare) form so getPathByStateId accepts both
|
|
99
|
+
// "#stateId" and "stateId". The registered form is kept for returns.
|
|
100
|
+
this.stateIdToPath.set(canonicalStateIdKey(stateId), path);
|
|
101
|
+
if (isParameterizedPattern(path)) {
|
|
102
|
+
const URLPatternCtorFn = getURLPatternCtor();
|
|
103
|
+
if (!URLPatternCtorFn) {
|
|
104
|
+
throw new URLPatternUnavailableError();
|
|
105
|
+
}
|
|
106
|
+
const bucketKey = getIndexKey(path);
|
|
72
107
|
const bucket = this.patternBuckets.get(bucketKey) ?? [];
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
108
|
+
try {
|
|
109
|
+
bucket.push({
|
|
110
|
+
pattern: getCompiledPattern(path, URLPatternCtorFn),
|
|
111
|
+
stateId,
|
|
112
|
+
order: patternOrder++,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
throw new InvalidRoutePatternError(path, { cause: err });
|
|
117
|
+
}
|
|
78
118
|
this.patternBuckets.set(bucketKey, bucket);
|
|
79
119
|
}
|
|
80
120
|
else {
|
|
@@ -107,9 +147,10 @@ export class BaseRouteMap {
|
|
|
107
147
|
const cachedMatch = this.pathMatchCache.get(cleanPath);
|
|
108
148
|
if (cachedMatch !== undefined)
|
|
109
149
|
return cachedMatch;
|
|
110
|
-
const candidates =
|
|
150
|
+
const candidates = getCandidates(this.patternBuckets, getIndexKey(cleanPath));
|
|
111
151
|
for (const { pattern, stateId } of candidates) {
|
|
112
|
-
|
|
152
|
+
const match = pattern.exec({ pathname: cleanPath });
|
|
153
|
+
if (match) {
|
|
113
154
|
this.pathMatchCache.set(cleanPath, stateId);
|
|
114
155
|
return stateId;
|
|
115
156
|
}
|
|
@@ -120,56 +161,22 @@ export class BaseRouteMap {
|
|
|
120
161
|
/**
|
|
121
162
|
* Look up the path pattern registered for a state ID.
|
|
122
163
|
*
|
|
164
|
+
* Accepts the stateId in either `"#stateId"` or `"stateId"` form regardless of
|
|
165
|
+
* which form was used at registration — lookups are canonicalized internally,
|
|
166
|
+
* so consumers never need to try both forms.
|
|
167
|
+
*
|
|
123
168
|
* @param stateId - State machine state ID (e.g., `"profile"`, `"#settings"`)
|
|
124
169
|
* @returns The registered path pattern, or `null` if the state ID is unknown
|
|
125
170
|
*
|
|
126
171
|
* @example
|
|
127
172
|
* ```typescript
|
|
128
173
|
* map.getPathByStateId("profile"); // "/profile/:userId"
|
|
174
|
+
* map.getPathByStateId("#profile"); // "/profile/:userId" (same entry)
|
|
129
175
|
* map.getPathByStateId("missing"); // null
|
|
130
176
|
* ```
|
|
131
177
|
*/
|
|
132
178
|
getPathByStateId(stateId) {
|
|
133
|
-
return this.stateIdToPath.get(stateId) ?? null;
|
|
134
|
-
}
|
|
135
|
-
getIndexKey(path) {
|
|
136
|
-
const trimmed = path.startsWith("/") ? path.slice(1) : path;
|
|
137
|
-
if (trimmed.length === 0)
|
|
138
|
-
return "/";
|
|
139
|
-
const segment = trimmed.split("/")[0];
|
|
140
|
-
if (segment === undefined || segment.startsWith(":"))
|
|
141
|
-
return "*";
|
|
142
|
-
return segment;
|
|
143
|
-
}
|
|
144
|
-
getCandidates(indexKey) {
|
|
145
|
-
const bucket = this.patternBuckets.get(indexKey) ?? [];
|
|
146
|
-
const wildcardBucket = this.patternBuckets.get("*") ?? [];
|
|
147
|
-
if (bucket.length === 0)
|
|
148
|
-
return wildcardBucket;
|
|
149
|
-
if (wildcardBucket.length === 0)
|
|
150
|
-
return bucket;
|
|
151
|
-
const merged = [];
|
|
152
|
-
let b = 0;
|
|
153
|
-
let w = 0;
|
|
154
|
-
while (b < bucket.length && w < wildcardBucket.length) {
|
|
155
|
-
if (bucket[b].order < wildcardBucket[w].order) {
|
|
156
|
-
merged.push(bucket[b]);
|
|
157
|
-
b += 1;
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
merged.push(wildcardBucket[w]);
|
|
161
|
-
w += 1;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
while (b < bucket.length) {
|
|
165
|
-
merged.push(bucket[b]);
|
|
166
|
-
b += 1;
|
|
167
|
-
}
|
|
168
|
-
while (w < wildcardBucket.length) {
|
|
169
|
-
merged.push(wildcardBucket[w]);
|
|
170
|
-
w += 1;
|
|
171
|
-
}
|
|
172
|
-
return merged;
|
|
179
|
+
return this.stateIdToPath.get(canonicalStateIdKey(stateId)) ?? null;
|
|
173
180
|
}
|
|
174
181
|
}
|
|
175
182
|
//# sourceMappingURL=base-route-map.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-route-map.js","sourceRoot":"","sources":["../src/base-route-map.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"base-route-map.js","sourceRoot":"","sources":["../src/base-route-map.ts"],"names":[],"mappings":"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"}
|
package/dist/build-tree.d.ts
CHANGED
|
@@ -6,8 +6,15 @@ import type { RouteInfo, RouteTree } from "./types.js";
|
|
|
6
6
|
* Absolute routes become top-level, relative routes nest under parents.
|
|
7
7
|
* Creates bidirectional maps for state ID ↔ path lookup.
|
|
8
8
|
*
|
|
9
|
+
* Duplicate detection runs here — after relative routes are resolved against
|
|
10
|
+
* their parent's full path — so two relative routes with the same raw string
|
|
11
|
+
* under different parents are valid, while distinct raw strings that resolve to
|
|
12
|
+
* the same full path (which would silently overwrite each other in `byPath`)
|
|
13
|
+
* are rejected.
|
|
14
|
+
*
|
|
9
15
|
* @param routes - Flat list of RouteInfo from extraction
|
|
10
16
|
* @returns RouteTree with root, byStateId map, and byPath map
|
|
17
|
+
* @throws {DuplicateRoutePathError} If two states resolve to the same full path
|
|
11
18
|
*/
|
|
12
19
|
export declare const buildRouteTree: (routes: RouteInfo[]) => RouteTree;
|
|
13
20
|
//# sourceMappingURL=build-tree.d.ts.map
|
package/dist/build-tree.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-tree.d.ts","sourceRoot":"","sources":["../src/build-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"build-tree.d.ts","sourceRoot":"","sources":["../src/build-tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAA4B,SAAS,EAAE,MAAM,YAAY,CAAC;AAGjF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,SAAS,EAAE,KAAG,SAwEpD,CAAC"}
|
package/dist/build-tree.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { detectDuplicateRoutes } from "./validate-routes.js";
|
|
1
2
|
/**
|
|
2
3
|
* Build hierarchical route tree from flat route list
|
|
3
4
|
*
|
|
@@ -5,8 +6,15 @@
|
|
|
5
6
|
* Absolute routes become top-level, relative routes nest under parents.
|
|
6
7
|
* Creates bidirectional maps for state ID ↔ path lookup.
|
|
7
8
|
*
|
|
9
|
+
* Duplicate detection runs here — after relative routes are resolved against
|
|
10
|
+
* their parent's full path — so two relative routes with the same raw string
|
|
11
|
+
* under different parents are valid, while distinct raw strings that resolve to
|
|
12
|
+
* the same full path (which would silently overwrite each other in `byPath`)
|
|
13
|
+
* are rejected.
|
|
14
|
+
*
|
|
8
15
|
* @param routes - Flat list of RouteInfo from extraction
|
|
9
16
|
* @returns RouteTree with root, byStateId map, and byPath map
|
|
17
|
+
* @throws {DuplicateRoutePathError} If two states resolve to the same full path
|
|
10
18
|
*/
|
|
11
19
|
export const buildRouteTree = (routes) => {
|
|
12
20
|
// 1. Create root node
|
|
@@ -18,20 +26,23 @@ export const buildRouteTree = (routes) => {
|
|
|
18
26
|
routable: false, // Root is not a routable state
|
|
19
27
|
children: [],
|
|
20
28
|
parent: null,
|
|
21
|
-
metadata:
|
|
29
|
+
metadata: "", // Synthetic non-routable root — no real route metadata
|
|
22
30
|
};
|
|
23
31
|
// 2. Initialize maps
|
|
24
32
|
const byStateId = new Map([[root.stateId, root]]);
|
|
25
33
|
const byPath = new Map([[root.fullPath, root]]);
|
|
26
34
|
// 3. Sort routes by depth (shallowest first for proper parent linking)
|
|
35
|
+
// oxlint-disable-next-line unicorn/no-array-sort
|
|
27
36
|
const sorted = routes.slice().sort((a, b) => a.statePath.length - b.statePath.length);
|
|
28
|
-
// 4. Build tree
|
|
37
|
+
// 4. Build tree, collecting inserted nodes for resolved-path duplicate detection
|
|
38
|
+
const insertedNodes = [];
|
|
29
39
|
for (const route of sorted) {
|
|
30
|
-
// Find parent by walking up state path
|
|
40
|
+
// Find parent by walking up state path.
|
|
41
|
+
// Each entry in statePath is a full dotted stateId (e.g., "chain.app.section"),
|
|
42
|
+
// so we check each ancestor stateId directly from the end of the list.
|
|
31
43
|
let parentNode = root;
|
|
32
|
-
for (let i = route.statePath.length -
|
|
33
|
-
const
|
|
34
|
-
const parentStateId = parentPath.join(".");
|
|
44
|
+
for (let i = route.statePath.length - 2; i >= 0; i--) {
|
|
45
|
+
const parentStateId = route.statePath[i]; // nosemgrep: gitlab.eslint.detect-object-injection
|
|
35
46
|
if (byStateId.has(parentStateId)) {
|
|
36
47
|
parentNode = byStateId.get(parentStateId);
|
|
37
48
|
break;
|
|
@@ -61,7 +72,11 @@ export const buildRouteTree = (routes) => {
|
|
|
61
72
|
// Add to maps for bidirectional lookup
|
|
62
73
|
byStateId.set(node.stateId, node);
|
|
63
74
|
byPath.set(node.fullPath, node);
|
|
75
|
+
insertedNodes.push(node);
|
|
64
76
|
}
|
|
77
|
+
// 5. Reject resolved full-path collisions — a silent byPath overwrite would
|
|
78
|
+
// leave one state unreachable by URL.
|
|
79
|
+
detectDuplicateRoutes(insertedNodes);
|
|
65
80
|
return { root, byStateId, byPath };
|
|
66
81
|
};
|
|
67
82
|
//# sourceMappingURL=build-tree.js.map
|
package/dist/build-tree.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-tree.js","sourceRoot":"","sources":["../src/build-tree.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build-tree.js","sourceRoot":"","sources":["../src/build-tree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { RouteTree } from "./types.js";
|
|
2
|
+
import { RouteMap } from "./base-route-map.js";
|
|
3
|
+
import type { RouteMapOptions } from "./create-route-map.js";
|
|
4
|
+
/**
|
|
5
|
+
* Create a `RouteMap` from a `RouteTree` node structure.
|
|
6
|
+
*
|
|
7
|
+
* Used by framework-router adapters that pass a
|
|
8
|
+
* `RouteTree` produced by `extractMachineRoutes()` rather than calling
|
|
9
|
+
* `createRouteMap()` directly.
|
|
10
|
+
*
|
|
11
|
+
* Traverses all nodes collecting `{ stateId: node.id, path: node.fullPath }` pairs.
|
|
12
|
+
* `node.fullPath` is always the absolute resolved path (e.g. `"/dashboard/overview"`),
|
|
13
|
+
* which is what `RouteMap` needs for browser URL matching. This matches the
|
|
14
|
+
* behaviour of `createRouteMap(machine)`, which also uses `node.fullPath`.
|
|
15
|
+
*
|
|
16
|
+
* @param routeTree - A `RouteTree` as returned by `extractMachineRoutes()`.
|
|
17
|
+
* @param options - Optional configuration (e.g. `{ cacheSize }` to override the LRU cache size).
|
|
18
|
+
* @returns A `RouteMap` for use with any `RouterBridgeBase`-based adapter.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* // Preferred — single call for XState machines:
|
|
23
|
+
* import { createRouteMap } from '@xmachines/play-router';
|
|
24
|
+
* const routeMap = createRouteMap(machine); // takes AnyStateMachine
|
|
25
|
+
*
|
|
26
|
+
* // Two-step form used by framework adapters that work with route trees:
|
|
27
|
+
* import { extractMachineRoutes, createRouteMapFromTree } from '@xmachines/play-router';
|
|
28
|
+
* const routeTree = extractMachineRoutes(machine);
|
|
29
|
+
* const routeMap = createRouteMapFromTree(routeTree); // uses node.fullPath (absolute)
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function createRouteMapFromTree(routeTree: RouteTree, options?: RouteMapOptions): RouteMap;
|
|
33
|
+
//# sourceMappingURL=create-route-map-from-tree.d.ts.map
|
|
@@ -0,0 +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;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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { RouteMap } from "./base-route-map.js";
|
|
2
|
+
/**
|
|
3
|
+
* Create a `RouteMap` from a `RouteTree` node structure.
|
|
4
|
+
*
|
|
5
|
+
* Used by framework-router adapters that pass a
|
|
6
|
+
* `RouteTree` produced by `extractMachineRoutes()` rather than calling
|
|
7
|
+
* `createRouteMap()` directly.
|
|
8
|
+
*
|
|
9
|
+
* Traverses all nodes collecting `{ stateId: node.id, path: node.fullPath }` pairs.
|
|
10
|
+
* `node.fullPath` is always the absolute resolved path (e.g. `"/dashboard/overview"`),
|
|
11
|
+
* which is what `RouteMap` needs for browser URL matching. This matches the
|
|
12
|
+
* behaviour of `createRouteMap(machine)`, which also uses `node.fullPath`.
|
|
13
|
+
*
|
|
14
|
+
* @param routeTree - A `RouteTree` as returned by `extractMachineRoutes()`.
|
|
15
|
+
* @param options - Optional configuration (e.g. `{ cacheSize }` to override the LRU cache size).
|
|
16
|
+
* @returns A `RouteMap` for use with any `RouterBridgeBase`-based adapter.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // Preferred — single call for XState machines:
|
|
21
|
+
* import { createRouteMap } from '@xmachines/play-router';
|
|
22
|
+
* const routeMap = createRouteMap(machine); // takes AnyStateMachine
|
|
23
|
+
*
|
|
24
|
+
* // Two-step form used by framework adapters that work with route trees:
|
|
25
|
+
* import { extractMachineRoutes, createRouteMapFromTree } from '@xmachines/play-router';
|
|
26
|
+
* const routeTree = extractMachineRoutes(machine);
|
|
27
|
+
* const routeMap = createRouteMapFromTree(routeTree); // uses node.fullPath (absolute)
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function createRouteMapFromTree(routeTree, options) {
|
|
31
|
+
const routes = [];
|
|
32
|
+
function traverse(node) {
|
|
33
|
+
if (node.id && node.fullPath) {
|
|
34
|
+
routes.push({ stateId: node.id, path: node.fullPath });
|
|
35
|
+
}
|
|
36
|
+
if (node.children) {
|
|
37
|
+
node.children.forEach(traverse);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
traverse(routeTree.root);
|
|
41
|
+
return new RouteMap(routes, options);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=create-route-map-from-tree.js.map
|
|
@@ -0,0 +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;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
|
-
* Resolves 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 BaseRouteMap} 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 RouteMap from
|
|
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, parameterized routes will not match and a
|
|
52
|
-
* `console.warn` is emitted. Static (exact) routes are unaffected.
|
|
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
|