@xmachines/play-router 1.0.0-beta.7 → 1.0.0-beta.9
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 +20 -1
- package/dist/base-route-map.d.ts +115 -0
- package/dist/base-route-map.d.ts.map +1 -0
- package/dist/base-route-map.js +175 -0
- package/dist/base-route-map.js.map +1 -0
- package/dist/connect-router.d.ts.map +1 -1
- package/dist/connect-router.js +11 -18
- package/dist/connect-router.js.map +1 -1
- package/dist/create-browser-history.d.ts +29 -4
- package/dist/create-browser-history.d.ts.map +1 -1
- package/dist/create-browser-history.js.map +1 -1
- package/dist/create-route-map.d.ts +21 -1
- package/dist/create-route-map.d.ts.map +1 -1
- package/dist/create-route-map.js +72 -21
- package/dist/create-route-map.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/dist/router-bridge-base.d.ts +32 -7
- package/dist/router-bridge-base.d.ts.map +1 -1
- package/dist/router-bridge-base.js +56 -10
- package/dist/router-bridge-base.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +20 -11
package/README.md
CHANGED
|
@@ -27,7 +27,26 @@ npm install @xmachines/play-router
|
|
|
27
27
|
|
|
28
28
|
**Peer dependencies:**
|
|
29
29
|
|
|
30
|
-
- `xstate` ^5.0.0
|
|
30
|
+
- `xstate` ^5.0.0 — State machine runtime
|
|
31
|
+
|
|
32
|
+
**URLPattern polyfill (Node.js < 24 / older browsers):**
|
|
33
|
+
|
|
34
|
+
`@xmachines/play-router` uses the [URLPattern API](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern) for dynamic route matching. URLPattern is available natively on Node.js 24+ and modern browsers (Chrome 95+, Firefox 117+, Safari 16.4+).
|
|
35
|
+
|
|
36
|
+
On environments without native support, load a polyfill **before** importing this package:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// Entry point — must run before any @xmachines/play-router import
|
|
40
|
+
import "urlpattern-polyfill";
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Install the polyfill:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install urlpattern-polyfill
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`urlpattern-polyfill` is declared as an optional peer dependency. Package managers will not install it automatically — it is the consumer's responsibility to load it when needed.
|
|
31
50
|
|
|
32
51
|
## Quick Start
|
|
33
52
|
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BaseRouteMap — Shared bidirectional route mapping base class
|
|
3
|
+
*
|
|
4
|
+
* Provides bucket-based pattern matching shared across all framework adapters.
|
|
5
|
+
* Adapters extend this class rather than duplicating the pattern-match logic.
|
|
6
|
+
*
|
|
7
|
+
* Algorithm: O(1) exact match via Map, then bucket-based O(k) pattern match
|
|
8
|
+
* where k = routes in the first-segment bucket (typically << total routes).
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* A single state ID ↔ path mapping entry.
|
|
12
|
+
*
|
|
13
|
+
* Both fields are `readonly` — mappings are immutable once passed to `BaseRouteMap`.
|
|
14
|
+
* Adapter packages re-export a structurally compatible `RouteMapping` type under
|
|
15
|
+
* their own name. This type is published from `@xmachines/play-router` as
|
|
16
|
+
* `BaseRouteMapping` to avoid name collisions with those adapter-local types.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const mapping: BaseRouteMapping = { stateId: "home", path: "/" };
|
|
21
|
+
* const paramMapping: BaseRouteMapping = { stateId: "profile", path: "/profile/:userId" };
|
|
22
|
+
* const optionalMapping: BaseRouteMapping = { stateId: "settings", path: "/settings/:section?" };
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export interface RouteMapping {
|
|
26
|
+
/** State machine state ID (e.g., `"home"`, `"#profile"`) */
|
|
27
|
+
readonly stateId: string;
|
|
28
|
+
/** URL path pattern (e.g., `"/"`, `"/profile/:userId"`, `"/settings/:section?"`) */
|
|
29
|
+
readonly path: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Shared bidirectional route map base class.
|
|
33
|
+
*
|
|
34
|
+
* All framework adapter `RouteMap` classes extend this — they add no logic of their
|
|
35
|
+
* own and inherit the full public API from here.
|
|
36
|
+
*
|
|
37
|
+
* **Lookup strategy:**
|
|
38
|
+
* - Static paths (no `:param`) → O(1) `Map` lookup
|
|
39
|
+
* - Dynamic paths → O(k) bucket-indexed scan using `RegExp`, where `k` is the number
|
|
40
|
+
* of routes sharing the same first path segment
|
|
41
|
+
* - Results are cached after the first match
|
|
42
|
+
*
|
|
43
|
+
* **Pattern syntax** (`:param` / `:param?`):
|
|
44
|
+
* - `:param` — required segment, matches exactly one non-`/` segment
|
|
45
|
+
* - `:param?` — optional segment, matches zero or one non-`/` segment
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* import { BaseRouteMap } from "@xmachines/play-router";
|
|
50
|
+
*
|
|
51
|
+
* const map = new BaseRouteMap([
|
|
52
|
+
* { stateId: "home", path: "/" },
|
|
53
|
+
* { stateId: "profile", path: "/profile/:userId" },
|
|
54
|
+
* { stateId: "settings", path: "/settings/:section?" },
|
|
55
|
+
* ]);
|
|
56
|
+
*
|
|
57
|
+
* map.getStateIdByPath("/"); // "home"
|
|
58
|
+
* map.getStateIdByPath("/profile/123"); // "profile"
|
|
59
|
+
* map.getStateIdByPath("/settings"); // "settings"
|
|
60
|
+
* map.getStateIdByPath("/unknown"); // null
|
|
61
|
+
*
|
|
62
|
+
* map.getPathByStateId("profile"); // "/profile/:userId"
|
|
63
|
+
* map.getPathByStateId("missing"); // null
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare class BaseRouteMap {
|
|
67
|
+
private stateIdToPath;
|
|
68
|
+
private pathToStateId;
|
|
69
|
+
private patternBuckets;
|
|
70
|
+
private pathMatchCache;
|
|
71
|
+
/**
|
|
72
|
+
* Build a route map from an array of state ID ↔ path mappings.
|
|
73
|
+
*
|
|
74
|
+
* Static paths (no `:param`) are indexed in an O(1) `Map`.
|
|
75
|
+
* Parameterized paths are compiled to `RegExp` and grouped into first-segment
|
|
76
|
+
* buckets for efficient candidate selection.
|
|
77
|
+
*
|
|
78
|
+
* @param mappings - Array of `{ stateId, path }` entries. Order determines
|
|
79
|
+
* priority when multiple patterns could match the same path.
|
|
80
|
+
*/
|
|
81
|
+
constructor(mappings: RouteMapping[]);
|
|
82
|
+
/**
|
|
83
|
+
* Resolve a URL path to its mapped state ID.
|
|
84
|
+
*
|
|
85
|
+
* Strips query strings and hash fragments before matching. Tries an O(1) exact
|
|
86
|
+
* lookup first, then falls back to bucket-indexed pattern matching. Results are
|
|
87
|
+
* cached after the first pattern match.
|
|
88
|
+
*
|
|
89
|
+
* @param path - URL pathname, optionally including query/hash (e.g., `"/profile/123?ref=nav"`)
|
|
90
|
+
* @returns The mapped state ID, or `null` if no route matches
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* map.getStateIdByPath("/profile/123"); // "profile"
|
|
95
|
+
* map.getStateIdByPath("/unknown"); // null
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
getStateIdByPath(path: string): string | null;
|
|
99
|
+
/**
|
|
100
|
+
* Look up the path pattern registered for a state ID.
|
|
101
|
+
*
|
|
102
|
+
* @param stateId - State machine state ID (e.g., `"profile"`, `"#settings"`)
|
|
103
|
+
* @returns The registered path pattern, or `null` if the state ID is unknown
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* map.getPathByStateId("profile"); // "/profile/:userId"
|
|
108
|
+
* map.getPathByStateId("missing"); // null
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
getPathByStateId(stateId: string): string | null;
|
|
112
|
+
private getIndexKey;
|
|
113
|
+
private getCandidates;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=base-route-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-route-map.d.ts","sourceRoot":"","sources":["../src/base-route-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;;;;;;;;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,YAAY;IACxB,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,cAAc,CAA0E;IAChG,OAAO,CAAC,cAAc,CAAkC;IAExD;;;;;;;;;OASG;gBACS,QAAQ,EAAE,YAAY,EAAE;IA4BpC;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAsB7C;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIhD,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,aAAa;CA8BrB"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BaseRouteMap — Shared bidirectional route mapping base class
|
|
3
|
+
*
|
|
4
|
+
* Provides bucket-based pattern matching shared across all framework adapters.
|
|
5
|
+
* Adapters extend this class rather than duplicating the pattern-match logic.
|
|
6
|
+
*
|
|
7
|
+
* Algorithm: O(1) exact match via Map, then bucket-based O(k) pattern match
|
|
8
|
+
* where k = routes in the first-segment bucket (typically << total routes).
|
|
9
|
+
*/
|
|
10
|
+
import QuickLRU from "quick-lru";
|
|
11
|
+
/**
|
|
12
|
+
* Shared bidirectional route map base class.
|
|
13
|
+
*
|
|
14
|
+
* All framework adapter `RouteMap` classes extend this — they add no logic of their
|
|
15
|
+
* own and inherit the full public API from here.
|
|
16
|
+
*
|
|
17
|
+
* **Lookup strategy:**
|
|
18
|
+
* - Static paths (no `:param`) → O(1) `Map` lookup
|
|
19
|
+
* - Dynamic paths → O(k) bucket-indexed scan using `RegExp`, where `k` is the number
|
|
20
|
+
* of routes sharing the same first path segment
|
|
21
|
+
* - Results are cached after the first match
|
|
22
|
+
*
|
|
23
|
+
* **Pattern syntax** (`:param` / `:param?`):
|
|
24
|
+
* - `:param` — required segment, matches exactly one non-`/` segment
|
|
25
|
+
* - `:param?` — optional segment, matches zero or one non-`/` segment
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { BaseRouteMap } from "@xmachines/play-router";
|
|
30
|
+
*
|
|
31
|
+
* const map = new BaseRouteMap([
|
|
32
|
+
* { stateId: "home", path: "/" },
|
|
33
|
+
* { stateId: "profile", path: "/profile/:userId" },
|
|
34
|
+
* { stateId: "settings", path: "/settings/:section?" },
|
|
35
|
+
* ]);
|
|
36
|
+
*
|
|
37
|
+
* map.getStateIdByPath("/"); // "home"
|
|
38
|
+
* map.getStateIdByPath("/profile/123"); // "profile"
|
|
39
|
+
* map.getStateIdByPath("/settings"); // "settings"
|
|
40
|
+
* map.getStateIdByPath("/unknown"); // null
|
|
41
|
+
*
|
|
42
|
+
* map.getPathByStateId("profile"); // "/profile/:userId"
|
|
43
|
+
* map.getPathByStateId("missing"); // null
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export class BaseRouteMap {
|
|
47
|
+
stateIdToPath;
|
|
48
|
+
pathToStateId;
|
|
49
|
+
patternBuckets;
|
|
50
|
+
pathMatchCache;
|
|
51
|
+
/**
|
|
52
|
+
* Build a route map from an array of state ID ↔ path mappings.
|
|
53
|
+
*
|
|
54
|
+
* Static paths (no `:param`) are indexed in an O(1) `Map`.
|
|
55
|
+
* Parameterized paths are compiled to `RegExp` and grouped into first-segment
|
|
56
|
+
* buckets for efficient candidate selection.
|
|
57
|
+
*
|
|
58
|
+
* @param mappings - Array of `{ stateId, path }` entries. Order determines
|
|
59
|
+
* priority when multiple patterns could match the same path.
|
|
60
|
+
*/
|
|
61
|
+
constructor(mappings) {
|
|
62
|
+
this.stateIdToPath = new Map();
|
|
63
|
+
this.pathToStateId = new Map();
|
|
64
|
+
this.patternBuckets = new Map();
|
|
65
|
+
this.pathMatchCache = new QuickLRU({ maxSize: 500 });
|
|
66
|
+
let patternOrder = 0;
|
|
67
|
+
for (const { stateId, path } of mappings) {
|
|
68
|
+
this.stateIdToPath.set(stateId, path);
|
|
69
|
+
if (path.includes(":")) {
|
|
70
|
+
const pattern = path.replace(/\/:[^/]+\??/g, (segment) => segment.endsWith("?") ? "(?:/([^/]+))?" : "/([^/]+)");
|
|
71
|
+
const bucketKey = this.getIndexKey(path);
|
|
72
|
+
const bucket = this.patternBuckets.get(bucketKey) ?? [];
|
|
73
|
+
bucket.push({
|
|
74
|
+
pattern: new RegExp(`^${pattern}$`),
|
|
75
|
+
stateId,
|
|
76
|
+
order: patternOrder++,
|
|
77
|
+
});
|
|
78
|
+
this.patternBuckets.set(bucketKey, bucket);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
this.pathToStateId.set(path, stateId);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Resolve a URL path to its mapped state ID.
|
|
87
|
+
*
|
|
88
|
+
* Strips query strings and hash fragments before matching. Tries an O(1) exact
|
|
89
|
+
* lookup first, then falls back to bucket-indexed pattern matching. Results are
|
|
90
|
+
* cached after the first pattern match.
|
|
91
|
+
*
|
|
92
|
+
* @param path - URL pathname, optionally including query/hash (e.g., `"/profile/123?ref=nav"`)
|
|
93
|
+
* @returns The mapped state ID, or `null` if no route matches
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* map.getStateIdByPath("/profile/123"); // "profile"
|
|
98
|
+
* map.getStateIdByPath("/unknown"); // null
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
getStateIdByPath(path) {
|
|
102
|
+
// Strip query string and hash fragment for matching
|
|
103
|
+
const cleanPath = path.split("?")[0].split("#")[0];
|
|
104
|
+
const exactMatch = this.pathToStateId.get(cleanPath);
|
|
105
|
+
if (exactMatch !== undefined)
|
|
106
|
+
return exactMatch;
|
|
107
|
+
const cachedMatch = this.pathMatchCache.get(cleanPath);
|
|
108
|
+
if (cachedMatch !== undefined)
|
|
109
|
+
return cachedMatch;
|
|
110
|
+
const candidates = this.getCandidates(this.getIndexKey(cleanPath));
|
|
111
|
+
for (const { pattern, stateId } of candidates) {
|
|
112
|
+
if (pattern.test(cleanPath)) {
|
|
113
|
+
this.pathMatchCache.set(cleanPath, stateId);
|
|
114
|
+
return stateId;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
this.pathMatchCache.set(cleanPath, null);
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Look up the path pattern registered for a state ID.
|
|
122
|
+
*
|
|
123
|
+
* @param stateId - State machine state ID (e.g., `"profile"`, `"#settings"`)
|
|
124
|
+
* @returns The registered path pattern, or `null` if the state ID is unknown
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* map.getPathByStateId("profile"); // "/profile/:userId"
|
|
129
|
+
* map.getPathByStateId("missing"); // null
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
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;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=base-route-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-route-map.js","sourceRoot":"","sources":["../src/base-route-map.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect-router.d.ts","sourceRoot":"","sources":["../src/connect-router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"connect-router.d.ts","sourceRoot":"","sources":["../src/connect-router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ,CAAC;IAC/C,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,QAAQ,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,IAAI,CA4EvE"}
|
package/dist/connect-router.js
CHANGED
|
@@ -47,40 +47,33 @@ import { Signal } from "@xmachines/play-signals";
|
|
|
47
47
|
export function connectRouter(options) {
|
|
48
48
|
const { actor, router, routeMap } = options;
|
|
49
49
|
const { history } = router;
|
|
50
|
-
// Prevent circular updates
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
let
|
|
50
|
+
// Prevent circular updates — single navigation-processing guard.
|
|
51
|
+
// Matches RouterBridgeBase.isProcessingNavigation pattern.
|
|
52
|
+
// Reduces race-condition surface from 2³=8 flag combinations to 2.
|
|
53
|
+
let isProcessingNavigation = false;
|
|
54
54
|
// Subscribe to history changes (browser navigation)
|
|
55
55
|
const unsubscribeHistory = history.subscribe((location) => {
|
|
56
|
-
if (
|
|
56
|
+
if (isProcessingNavigation)
|
|
57
57
|
return;
|
|
58
58
|
const { to, params } = routeMap.resolve(location.pathname);
|
|
59
59
|
if (to) {
|
|
60
|
-
|
|
61
|
-
isProcessingPopstate = true;
|
|
60
|
+
isProcessingNavigation = true;
|
|
62
61
|
actor.send({ type: "play.route", to, params });
|
|
63
62
|
// Check immediately if actor redirected (always-guard)
|
|
64
63
|
// XState processes events synchronously, so snapshot is already updated
|
|
65
64
|
const newActorRoute = actor.currentRoute.get();
|
|
66
65
|
if (newActorRoute && newActorRoute !== location.pathname) {
|
|
67
66
|
// Actor redirected - update URL
|
|
68
|
-
isProcessingActorChange = true;
|
|
69
67
|
history.replace(newActorRoute);
|
|
70
|
-
isProcessingActorChange = false;
|
|
71
68
|
}
|
|
72
|
-
// Clear
|
|
73
|
-
|
|
74
|
-
isProcessingPopstate = false;
|
|
69
|
+
// Clear flag synchronously to prevent race conditions
|
|
70
|
+
isProcessingNavigation = false;
|
|
75
71
|
}
|
|
76
72
|
});
|
|
77
73
|
// Watch actor's currentRoute signal (actor-driven navigation)
|
|
78
74
|
const watcher = new Signal.subtle.Watcher(() => {
|
|
79
75
|
queueMicrotask(() => {
|
|
80
|
-
if (
|
|
81
|
-
return;
|
|
82
|
-
// Skip if we're processing popstate - let the history subscriber handle URL updates
|
|
83
|
-
if (isProcessingPopstate)
|
|
76
|
+
if (isProcessingNavigation)
|
|
84
77
|
return;
|
|
85
78
|
for (const signal of watcher.getPending()) {
|
|
86
79
|
signal.get();
|
|
@@ -93,9 +86,9 @@ export function connectRouter(options) {
|
|
|
93
86
|
// Note: This assumes route tree has path metadata
|
|
94
87
|
// For now, simple implementation - can enhance later
|
|
95
88
|
if (route && route !== currentPath) {
|
|
96
|
-
|
|
89
|
+
isProcessingNavigation = true;
|
|
97
90
|
history.push(route);
|
|
98
|
-
|
|
91
|
+
isProcessingNavigation = false;
|
|
99
92
|
}
|
|
100
93
|
});
|
|
101
94
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect-router.js","sourceRoot":"","sources":["../src/connect-router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"connect-router.js","sourceRoot":"","sources":["../src/connect-router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAYjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,UAAU,aAAa,CAAC,OAA6B;IAC1D,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAC5C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAE3B,iEAAiE;IACjE,2DAA2D;IAC3D,mEAAmE;IACnE,IAAI,sBAAsB,GAAG,KAAK,CAAC;IAEnC,oDAAoD;IACpD,MAAM,kBAAkB,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE,EAAE;QACzD,IAAI,sBAAsB;YAAE,OAAO;QAEnC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC3D,IAAI,EAAE,EAAE,CAAC;YACR,sBAAsB,GAAG,IAAI,CAAC;YAE9B,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;YAE/C,uDAAuD;YACvD,wEAAwE;YACxE,MAAM,aAAa,GAAG,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;YAC/C,IAAI,aAAa,IAAI,aAAa,KAAK,QAAQ,CAAC,QAAQ,EAAE,CAAC;gBAC1D,gCAAgC;gBAChC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YAChC,CAAC;YAED,sDAAsD;YACtD,sBAAsB,GAAG,KAAK,CAAC;QAChC,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,8DAA8D;IAC9D,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;QAC9C,cAAc,CAAC,GAAG,EAAE;YACnB,IAAI,sBAAsB;gBAAE,OAAO;YAEnC,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;gBAC3C,MAAM,CAAC,GAAG,EAAE,CAAC;YACd,CAAC;YACD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAElC,sBAAsB;YACtB,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;YACvC,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAE9C,wCAAwC;YACxC,kDAAkD;YAClD,qDAAqD;YACrD,IAAI,KAAK,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;gBACpC,sBAAsB,GAAG,IAAI,CAAC;gBAC9B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACpB,sBAAsB,GAAG,KAAK,CAAC;YAChC,CAAC;QACF,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAElC,4BAA4B;IAC5B,mEAAmE;IACnE,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC9C,MAAM,iBAAiB,GAAG,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;IAEnD,IAAI,WAAW,KAAK,iBAAiB,EAAE,CAAC;QACvC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACrD,IAAI,EAAE,EAAE,CAAC;YACR,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAChD,CAAC;IACF,CAAC;IAED,iBAAiB;IACjB,OAAO,GAAG,EAAE;QACX,kBAAkB,EAAE,CAAC;QACrB,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACrC,CAAC,CAAC;AACH,CAAC"}
|
|
@@ -6,16 +6,16 @@ export interface BrowserHistory {
|
|
|
6
6
|
pathname: string;
|
|
7
7
|
search: string;
|
|
8
8
|
hash: string;
|
|
9
|
-
state:
|
|
9
|
+
state: unknown;
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
12
|
* Push new URL to history
|
|
13
13
|
*/
|
|
14
|
-
push(path: string, state?:
|
|
14
|
+
push(path: string, state?: unknown): void;
|
|
15
15
|
/**
|
|
16
16
|
* Replace current URL in history
|
|
17
17
|
*/
|
|
18
|
-
replace(path: string, state?:
|
|
18
|
+
replace(path: string, state?: unknown): void;
|
|
19
19
|
/**
|
|
20
20
|
* Go back/forward
|
|
21
21
|
*/
|
|
@@ -36,6 +36,30 @@ export interface BrowserHistory {
|
|
|
36
36
|
*/
|
|
37
37
|
destroy(): void;
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Minimal window interface for createBrowserHistory
|
|
41
|
+
*
|
|
42
|
+
* Structural interface covering only the properties actually used.
|
|
43
|
+
* Accepts Window, JSDOM window, or any compatible test double.
|
|
44
|
+
* Avoids coupling to `Window & typeof globalThis` which varies across environments.
|
|
45
|
+
*/
|
|
46
|
+
interface BrowserWindow {
|
|
47
|
+
history: {
|
|
48
|
+
readonly state: unknown;
|
|
49
|
+
pushState(state: unknown, title: string, url?: string | null): void;
|
|
50
|
+
replaceState(state: unknown, title: string, url?: string | null): void;
|
|
51
|
+
go(delta: number): void;
|
|
52
|
+
back(): void;
|
|
53
|
+
forward(): void;
|
|
54
|
+
};
|
|
55
|
+
location: {
|
|
56
|
+
readonly pathname: string;
|
|
57
|
+
readonly search: string;
|
|
58
|
+
readonly hash: string;
|
|
59
|
+
};
|
|
60
|
+
addEventListener(type: "popstate", listener: () => void): void;
|
|
61
|
+
removeEventListener(type: "popstate", listener: () => void): void;
|
|
62
|
+
}
|
|
39
63
|
/**
|
|
40
64
|
* Create browser history that wraps window.history
|
|
41
65
|
*
|
|
@@ -63,6 +87,7 @@ export interface BrowserHistory {
|
|
|
63
87
|
* ```
|
|
64
88
|
*/
|
|
65
89
|
export declare function createBrowserHistory(options: {
|
|
66
|
-
window:
|
|
90
|
+
window: BrowserWindow;
|
|
67
91
|
}): BrowserHistory;
|
|
92
|
+
export {};
|
|
68
93
|
//# sourceMappingURL=create-browser-history.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-browser-history.d.ts","sourceRoot":"","sources":["../src/create-browser-history.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"create-browser-history.d.ts","sourceRoot":"","sources":["../src/create-browser-history.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,OAAO,CAAC;KACf,CAAC;IAEF;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAE1C;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAE7C;;OAEG;IACH,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,IAAI,IAAI,CAAC;IACb,OAAO,IAAI,IAAI,CAAC;IAEhB;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAEhF;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAEjC;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;CAChB;AAED;;;;;;GAMG;AACH,UAAU,aAAa;IACtB,OAAO,EAAE;QACR,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;QACxB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;QACpE,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;QACvE,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,IAAI,IAAI,IAAI,CAAC;QACb,OAAO,IAAI,IAAI,CAAC;KAChB,CAAC;IACF,QAAQ,EAAE;QACT,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC/D,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CAClE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GAAG,cAAc,CAmFvF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-browser-history.js","sourceRoot":"","sources":["../src/create-browser-history.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create-browser-history.js","sourceRoot":"","sources":["../src/create-browser-history.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { RouteTree } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Bidirectional route resolution interface returned by `createRouteMap()`.
|
|
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
|
|
11
|
+
*/
|
|
2
12
|
export interface RouteMap {
|
|
3
13
|
/**
|
|
4
14
|
* Resolve a URL path to a state ID and params.
|
|
@@ -27,10 +37,20 @@ export interface RouteMap {
|
|
|
27
37
|
* Architecture:
|
|
28
38
|
* - Pure function - no side effects
|
|
29
39
|
* - O(1) lookups for exact matches via Map
|
|
30
|
-
* - O(
|
|
40
|
+
* - O(k) bucket-indexed pattern matching with URLPattern, where k = routes in the
|
|
41
|
+
* first-segment bucket (typically << total routes)
|
|
31
42
|
* - Returns formatted state IDs (with # prefix)
|
|
32
43
|
* - Extracts params from matched patterns
|
|
33
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, parameterized routes will not match and a
|
|
52
|
+
* `console.warn` is emitted. Static (exact) routes are unaffected.
|
|
53
|
+
*
|
|
34
54
|
* Usage:
|
|
35
55
|
* ```typescript
|
|
36
56
|
* const routeTree = extractMachineRoutes(machine);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-route-map.d.ts","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"create-route-map.d.ts","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAgBvD;;;;;;;;;GASG;AACH,MAAM,WAAW,QAAQ;IACxB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG;QACtB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,CAAC;CACF;AAyCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,QAAQ,CA0D7D"}
|
package/dist/create-route-map.js
CHANGED
|
@@ -1,14 +1,65 @@
|
|
|
1
|
-
|
|
1
|
+
function getURLPatternCtor() {
|
|
2
|
+
return globalThis["URLPattern"];
|
|
3
|
+
}
|
|
4
|
+
function getIndexKey(path) {
|
|
5
|
+
const trimmed = path.startsWith("/") ? path.slice(1) : path;
|
|
6
|
+
if (trimmed.length === 0)
|
|
7
|
+
return "/";
|
|
8
|
+
const segment = trimmed.split("/")[0];
|
|
9
|
+
if (segment === undefined || segment.startsWith(":"))
|
|
10
|
+
return "*";
|
|
11
|
+
return segment;
|
|
12
|
+
}
|
|
13
|
+
function getCandidates(patternBuckets, indexKey) {
|
|
14
|
+
const bucket = patternBuckets.get(indexKey) ?? [];
|
|
15
|
+
const wildcardBucket = patternBuckets.get("*") ?? [];
|
|
16
|
+
if (bucket.length === 0)
|
|
17
|
+
return wildcardBucket;
|
|
18
|
+
if (wildcardBucket.length === 0)
|
|
19
|
+
return bucket;
|
|
20
|
+
const merged = [];
|
|
21
|
+
let b = 0;
|
|
22
|
+
let w = 0;
|
|
23
|
+
while (b < bucket.length && w < wildcardBucket.length) {
|
|
24
|
+
if (bucket[b].order < wildcardBucket[w].order) {
|
|
25
|
+
merged.push(bucket[b]);
|
|
26
|
+
b += 1;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
merged.push(wildcardBucket[w]);
|
|
30
|
+
w += 1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
while (b < bucket.length) {
|
|
34
|
+
merged.push(bucket[b]);
|
|
35
|
+
b += 1;
|
|
36
|
+
}
|
|
37
|
+
while (w < wildcardBucket.length) {
|
|
38
|
+
merged.push(wildcardBucket[w]);
|
|
39
|
+
w += 1;
|
|
40
|
+
}
|
|
41
|
+
return merged;
|
|
42
|
+
}
|
|
2
43
|
/**
|
|
3
44
|
* Create a RouteMap from a RouteTree for efficient path lookups.
|
|
4
45
|
*
|
|
5
46
|
* Architecture:
|
|
6
47
|
* - Pure function - no side effects
|
|
7
48
|
* - O(1) lookups for exact matches via Map
|
|
8
|
-
* - O(
|
|
49
|
+
* - O(k) bucket-indexed pattern matching with URLPattern, where k = routes in the
|
|
50
|
+
* first-segment bucket (typically << total routes)
|
|
9
51
|
* - Returns formatted state IDs (with # prefix)
|
|
10
52
|
* - Extracts params from matched patterns
|
|
11
53
|
*
|
|
54
|
+
* **URLPattern requirement:** Dynamic route pattern matching requires `URLPattern`
|
|
55
|
+
* to be available on `globalThis`. On Node.js < 24 and older browsers, load a polyfill
|
|
56
|
+
* before calling this function:
|
|
57
|
+
* ```typescript
|
|
58
|
+
* import "urlpattern-polyfill"; // before importing @xmachines/play-router
|
|
59
|
+
* ```
|
|
60
|
+
* If `URLPattern` is unavailable, parameterized routes will not match and a
|
|
61
|
+
* `console.warn` is emitted. Static (exact) routes are unaffected.
|
|
62
|
+
*
|
|
12
63
|
* Usage:
|
|
13
64
|
* ```typescript
|
|
14
65
|
* const routeTree = extractMachineRoutes(machine);
|
|
@@ -21,15 +72,22 @@ import { URLPattern } from "urlpattern-polyfill";
|
|
|
21
72
|
* ```
|
|
22
73
|
*/
|
|
23
74
|
export function createRouteMap(routeTree) {
|
|
24
|
-
// Build pattern matchers for routes with dynamic segments
|
|
25
|
-
const
|
|
26
|
-
|
|
75
|
+
// Build bucket-indexed pattern matchers for routes with dynamic segments
|
|
76
|
+
const patternBuckets = new Map();
|
|
77
|
+
let patternOrder = 0;
|
|
78
|
+
const URLPatternCtor = getURLPatternCtor();
|
|
27
79
|
function collectPatterns(node) {
|
|
28
80
|
if (node.routable && node.pattern) {
|
|
81
|
+
if (!URLPatternCtor) {
|
|
82
|
+
console.warn("[RouteMap] URLPattern is not available. Load a polyfill (e.g. urlpattern-polyfill) before using @xmachines/play-router on Node < 24 or older browsers.");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
29
85
|
try {
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
86
|
+
const urlPattern = new URLPatternCtor({ pathname: node.pattern });
|
|
87
|
+
const bucketKey = getIndexKey(node.pattern);
|
|
88
|
+
const bucket = patternBuckets.get(bucketKey) ?? [];
|
|
89
|
+
bucket.push({ pattern: urlPattern, node, order: patternOrder++ });
|
|
90
|
+
patternBuckets.set(bucketKey, bucket);
|
|
33
91
|
}
|
|
34
92
|
catch (err) {
|
|
35
93
|
console.warn("[RouteMap] Invalid pattern:", node.pattern, err);
|
|
@@ -42,30 +100,23 @@ export function createRouteMap(routeTree) {
|
|
|
42
100
|
collectPatterns(routeTree.root);
|
|
43
101
|
return {
|
|
44
102
|
resolve(path) {
|
|
45
|
-
//
|
|
103
|
+
// O(1) exact match via routeTree.byPath Map (unchanged)
|
|
46
104
|
const exactMatch = routeTree.byPath.get(path);
|
|
47
105
|
if (exactMatch?.routable) {
|
|
48
|
-
return {
|
|
49
|
-
to: `#${exactMatch.id}`,
|
|
50
|
-
params: {},
|
|
51
|
-
};
|
|
106
|
+
return { to: `#${exactMatch.id}`, params: {} };
|
|
52
107
|
}
|
|
53
|
-
//
|
|
54
|
-
|
|
108
|
+
// Bucket-indexed pattern match: only scan candidates for this path's first segment
|
|
109
|
+
const candidates = getCandidates(patternBuckets, getIndexKey(path));
|
|
110
|
+
for (const { pattern, node } of candidates) {
|
|
55
111
|
const match = pattern.exec({ pathname: path });
|
|
56
112
|
if (match) {
|
|
57
|
-
// Extract pathname params (e.g., { section: "account" })
|
|
58
113
|
const params = {};
|
|
59
114
|
if (match.pathname.groups) {
|
|
60
115
|
Object.assign(params, match.pathname.groups);
|
|
61
116
|
}
|
|
62
|
-
return {
|
|
63
|
-
to: `#${node.id}`,
|
|
64
|
-
params,
|
|
65
|
-
};
|
|
117
|
+
return { to: `#${node.id}`, params };
|
|
66
118
|
}
|
|
67
119
|
}
|
|
68
|
-
// No match found
|
|
69
120
|
return { to: null, params: {} };
|
|
70
121
|
},
|
|
71
122
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-route-map.js","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create-route-map.js","sourceRoot":"","sources":["../src/create-route-map.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { extractMachineRoutes } from "./extract-routes.js";
|
|
|
7
7
|
export { getNavigableRoutes, getRoutableRoutes, routeExists } from "./query.js";
|
|
8
8
|
export type { StateVisit, RouteInfo, RouteNode, RouteTree, RouteObject, RouteMetadata, PlayRouteEvent, RouterBridge, } from "./types.js";
|
|
9
9
|
export { createRouteMap, type RouteMap } from "./create-route-map.js";
|
|
10
|
+
export { BaseRouteMap, type RouteMapping as BaseRouteMapping } from "./base-route-map.js";
|
|
10
11
|
export { createBrowserHistory, type BrowserHistory } from "./create-browser-history.js";
|
|
11
12
|
export { createRouter, type VanillaRouter } from "./create-router.js";
|
|
12
13
|
export { connectRouter, type ConnectRouterOptions } from "./connect-router.js";
|
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;AAG3D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEhF,YAAY,EACX,UAAU,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,GACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG3D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEhF,YAAY,EACX,UAAU,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,GACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAKtE,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,IAAI,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG1F,OAAO,EAAE,oBAAoB,EAAE,KAAK,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAGxF,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGtE,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,EAAE,IAAI,MAAM,KAAG,SAAS,GAAG,SAEvE,CAAC;AAqEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,SAAS,EAAE,MAAM,MAAM,KAAG,SAAS,GAAG,SAyB3E,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,13 +7,17 @@ export { validateRouteFormat, validateStateExists, detectDuplicateRoutes, } from
|
|
|
7
7
|
export { buildRouteTree } from "./build-tree.js";
|
|
8
8
|
export { extractMachineRoutes } from "./extract-routes.js";
|
|
9
9
|
export { getNavigableRoutes, getRoutableRoutes, routeExists } from "./query.js";
|
|
10
|
-
//
|
|
10
|
+
// Route map for path → state ID resolution (createRouteMap / RouteMap interface)
|
|
11
11
|
export { createRouteMap } from "./create-route-map.js";
|
|
12
|
-
//
|
|
12
|
+
// Shared bidirectional route mapping base class.
|
|
13
|
+
// Re-exported as `BaseRouteMapping` (not `RouteMapping`) so adapter packages can
|
|
14
|
+
// define their own local `RouteMapping` type without a name collision.
|
|
15
|
+
export { BaseRouteMap } from "./base-route-map.js";
|
|
16
|
+
// Browser history abstraction (aligned with TanStack Router)
|
|
13
17
|
export { createBrowserHistory } from "./create-browser-history.js";
|
|
14
|
-
//
|
|
18
|
+
// Vanilla router (framework-agnostic)
|
|
15
19
|
export { createRouter } from "./create-router.js";
|
|
16
|
-
//
|
|
20
|
+
// Pure browser integration (low-level API)
|
|
17
21
|
export { connectRouter } from "./connect-router.js";
|
|
18
22
|
/**
|
|
19
23
|
* Find route node by state ID
|
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;AAE3D,6BAA6B;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACN,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAahF,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* export class MyRouterBridge extends RouterBridgeBase {
|
|
18
18
|
* private unsubscribe: (() => void) | null = null;
|
|
19
19
|
*
|
|
20
|
-
* constructor(private router: MyRouter, actor:
|
|
20
|
+
* constructor(private router: MyRouter, actor: AbstractActor<AnyActorLogic> & Routable, routeMap: { getStateIdByPath(path: string): string | undefined; getPathByStateId(id: string): string | undefined; }) {
|
|
21
21
|
* super(actor, routeMap);
|
|
22
22
|
* }
|
|
23
23
|
*
|
|
@@ -54,8 +54,8 @@ import type { RouterBridge } from "./types.js";
|
|
|
54
54
|
export declare abstract class RouterBridgeBase implements RouterBridge {
|
|
55
55
|
protected readonly actor: AbstractActor<AnyActorLogic> & Routable;
|
|
56
56
|
protected readonly routeMap: {
|
|
57
|
-
getStateIdByPath(path: string): string | undefined;
|
|
58
|
-
getPathByStateId(id: string): string | undefined;
|
|
57
|
+
getStateIdByPath(path: string): string | null | undefined;
|
|
58
|
+
getPathByStateId(id: string): string | null | undefined;
|
|
59
59
|
};
|
|
60
60
|
protected isConnected: boolean;
|
|
61
61
|
protected hasConnectedOnce: boolean;
|
|
@@ -69,8 +69,8 @@ export declare abstract class RouterBridgeBase implements RouterBridge {
|
|
|
69
69
|
* @param routeMap - Bidirectional route map for stateId ↔ path resolution
|
|
70
70
|
*/
|
|
71
71
|
constructor(actor: AbstractActor<AnyActorLogic> & Routable, routeMap: {
|
|
72
|
-
getStateIdByPath(path: string): string | undefined;
|
|
73
|
-
getPathByStateId(id: string): string | undefined;
|
|
72
|
+
getStateIdByPath(path: string): string | null | undefined;
|
|
73
|
+
getPathByStateId(id: string): string | null | undefined;
|
|
74
74
|
});
|
|
75
75
|
/**
|
|
76
76
|
* Connect the router bridge to the Actor.
|
|
@@ -100,11 +100,36 @@ export declare abstract class RouterBridgeBase implements RouterBridge {
|
|
|
100
100
|
*/
|
|
101
101
|
protected syncActorFromRouter(pathname: string, search?: string): void;
|
|
102
102
|
/**
|
|
103
|
-
*
|
|
103
|
+
* Sanitize a raw pathname before routing to prevent injection and DoS.
|
|
104
|
+
*
|
|
105
|
+
* - Enforces maximum length (2048 chars) to prevent DoS via expensive URLPattern.exec()
|
|
106
|
+
* - Strips query string (everything from ? onwards) — some adapters may pass
|
|
107
|
+
* a pathname that still contains ? if extraction was imprecise
|
|
108
|
+
* - Strips hash fragment (everything from # onwards)
|
|
109
|
+
* - Normalizes consecutive slashes to single slash
|
|
110
|
+
*
|
|
111
|
+
* Returns null if the path should be rejected (too long). Returns the
|
|
112
|
+
* normalized path string otherwise.
|
|
113
|
+
*
|
|
114
|
+
* Called at the top of syncActorFromRouter() before any routing logic.
|
|
115
|
+
* Because all adapters pass through this single method, sanitization
|
|
116
|
+
* protects Vue, Solid, TanStack, and React Router bridges simultaneously.
|
|
117
|
+
*
|
|
118
|
+
* @param pathname - Raw pathname from router adapter
|
|
119
|
+
* @returns Sanitized pathname, or null if rejected
|
|
120
|
+
*/
|
|
121
|
+
private sanitizePathname;
|
|
122
|
+
/**
|
|
123
|
+
* Extract path parameters from URL using the URLPattern API.
|
|
124
|
+
*
|
|
125
|
+
* Accesses `globalThis.URLPattern` at runtime — no polyfill is imported by this
|
|
126
|
+
* library. If `URLPattern` is unavailable (Node.js < 24, older browsers without a
|
|
127
|
+
* polyfill), this method returns `{}` silently (graceful degradation — routing still
|
|
128
|
+
* works, params will be empty).
|
|
104
129
|
*
|
|
105
130
|
* @param pathname - The actual URL path (e.g., '/profile/john')
|
|
106
131
|
* @param stateId - The matched state ID for looking up route pattern
|
|
107
|
-
* @returns Extracted path parameters or empty object
|
|
132
|
+
* @returns Extracted path parameters, or empty object if URLPattern is unavailable or no match
|
|
108
133
|
*/
|
|
109
134
|
protected extractParams(pathname: string, stateId: string): Record<string, string>;
|
|
110
135
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router-bridge-base.d.ts","sourceRoot":"","sources":["../src/router-bridge-base.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/D;;;;;;GAMG;AACH,8BAAsB,gBAAiB,YAAW,YAAY;IAe5D,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ;IACjE,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE;QAC5B,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"router-bridge-base.d.ts","sourceRoot":"","sources":["../src/router-bridge-base.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/D;;;;;;GAMG;AACH,8BAAsB,gBAAiB,YAAW,YAAY;IAe5D,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ;IACjE,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE;QAC5B,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;QAC1D,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;KACxD;IAjBF,SAAS,CAAC,WAAW,EAAE,OAAO,CAAS;IACvC,SAAS,CAAC,gBAAgB,EAAE,OAAO,CAAS;IAC5C,SAAS,CAAC,cAAc,EAAE,MAAM,CAAM;IACtC,SAAS,CAAC,sBAAsB,EAAE,OAAO,CAAS;IAClD,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAQ;IAE5D;;;;;OAKG;gBAEiB,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ,EAC9C,QAAQ,EAAE;QAC5B,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;QAC1D,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;KACxD;IASF;;;;;OAKG;IACH,OAAO,IAAI,IAAI;IA6Cf;;;;OAIG;IACH,UAAU,IAAI,IAAI;IAqBlB;;;;;OAKG;IACH,SAAS,CAAC,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,GAAG,IAAI;IAcnE;;;;;OAKG;IACH,SAAS,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IA4CtE;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,gBAAgB;IAcxB;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAqBlF;;;;;OAKG;IACH,SAAS,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAc9D;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAErD;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,kBAAkB,IAAI,IAAI;IAE7C;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,oBAAoB,IAAI,IAAI;IAE/C;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,oBAAoB,IAAI,MAAM,GAAG,IAAI;CAG/C"}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* export class MyRouterBridge extends RouterBridgeBase {
|
|
18
18
|
* private unsubscribe: (() => void) | null = null;
|
|
19
19
|
*
|
|
20
|
-
* constructor(private router: MyRouter, actor:
|
|
20
|
+
* constructor(private router: MyRouter, actor: AbstractActor<AnyActorLogic> & Routable, routeMap: { getStateIdByPath(path: string): string | undefined; getPathByStateId(id: string): string | undefined; }) {
|
|
21
21
|
* super(actor, routeMap);
|
|
22
22
|
* }
|
|
23
23
|
*
|
|
@@ -137,7 +137,7 @@ export class RouterBridgeBase {
|
|
|
137
137
|
this.unwatchRouterChanges();
|
|
138
138
|
}
|
|
139
139
|
this.isProcessingNavigation = false;
|
|
140
|
-
this.hasConnectedOnce = true;
|
|
140
|
+
// (removed: this.hasConnectedOnce = true; — copy-paste artifact from connect(), per CONS-07)
|
|
141
141
|
this.isConnected = false;
|
|
142
142
|
}
|
|
143
143
|
// ── Sync methods (protected, overridable if subclass needs custom behavior) ──
|
|
@@ -174,18 +174,21 @@ export class RouterBridgeBase {
|
|
|
174
174
|
return;
|
|
175
175
|
if (typeof pathname !== "string")
|
|
176
176
|
return;
|
|
177
|
-
|
|
177
|
+
const sanitized = this.sanitizePathname(pathname);
|
|
178
|
+
if (sanitized === null)
|
|
179
|
+
return; // Path too long — reject
|
|
180
|
+
if (sanitized === this.lastSyncedPath)
|
|
178
181
|
return;
|
|
179
182
|
if (this.isProcessingNavigation)
|
|
180
183
|
return;
|
|
181
184
|
this.isProcessingNavigation = true;
|
|
182
185
|
try {
|
|
183
|
-
const stateId = this.routeMap.getStateIdByPath(
|
|
186
|
+
const stateId = this.routeMap.getStateIdByPath(sanitized);
|
|
184
187
|
if (!stateId) {
|
|
185
188
|
this.isProcessingNavigation = false;
|
|
186
189
|
return;
|
|
187
190
|
}
|
|
188
|
-
const params = this.extractParams(
|
|
191
|
+
const params = this.extractParams(sanitized, stateId);
|
|
189
192
|
const query = this.extractQuery(search || "");
|
|
190
193
|
// formatPlayRouteTransitions generates guards that match event.to === "#stateId"
|
|
191
194
|
// RouteMap stateIds from extractMachineRoutes don't include the "#" prefix,
|
|
@@ -199,7 +202,7 @@ export class RouterBridgeBase {
|
|
|
199
202
|
query,
|
|
200
203
|
};
|
|
201
204
|
this.actor.send(event);
|
|
202
|
-
this.lastSyncedPath =
|
|
205
|
+
this.lastSyncedPath = sanitized;
|
|
203
206
|
}
|
|
204
207
|
catch (error) {
|
|
205
208
|
console.error("[RouterBridgeBase] Error sending event:", error);
|
|
@@ -208,20 +211,59 @@ export class RouterBridgeBase {
|
|
|
208
211
|
this.isProcessingNavigation = false;
|
|
209
212
|
}
|
|
210
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Sanitize a raw pathname before routing to prevent injection and DoS.
|
|
216
|
+
*
|
|
217
|
+
* - Enforces maximum length (2048 chars) to prevent DoS via expensive URLPattern.exec()
|
|
218
|
+
* - Strips query string (everything from ? onwards) — some adapters may pass
|
|
219
|
+
* a pathname that still contains ? if extraction was imprecise
|
|
220
|
+
* - Strips hash fragment (everything from # onwards)
|
|
221
|
+
* - Normalizes consecutive slashes to single slash
|
|
222
|
+
*
|
|
223
|
+
* Returns null if the path should be rejected (too long). Returns the
|
|
224
|
+
* normalized path string otherwise.
|
|
225
|
+
*
|
|
226
|
+
* Called at the top of syncActorFromRouter() before any routing logic.
|
|
227
|
+
* Because all adapters pass through this single method, sanitization
|
|
228
|
+
* protects Vue, Solid, TanStack, and React Router bridges simultaneously.
|
|
229
|
+
*
|
|
230
|
+
* @param pathname - Raw pathname from router adapter
|
|
231
|
+
* @returns Sanitized pathname, or null if rejected
|
|
232
|
+
*/
|
|
233
|
+
sanitizePathname(pathname) {
|
|
234
|
+
// Enforce maximum length to prevent DoS via expensive URLPattern.exec()
|
|
235
|
+
if (pathname.length > 2048)
|
|
236
|
+
return null;
|
|
237
|
+
// Strip query string (everything from ? onwards)
|
|
238
|
+
const withoutQuery = pathname.split("?")[0];
|
|
239
|
+
// Strip hash fragment (everything from # onwards)
|
|
240
|
+
const withoutHash = withoutQuery.split("#")[0];
|
|
241
|
+
// Normalize double slashes to single slash
|
|
242
|
+
const normalized = withoutHash.replace(/\/+/g, "/");
|
|
243
|
+
return normalized;
|
|
244
|
+
}
|
|
211
245
|
// ── Utilities (protected, overridable for framework-native param extraction) ──
|
|
212
246
|
/**
|
|
213
|
-
* Extract path parameters from URL using URLPattern API.
|
|
247
|
+
* Extract path parameters from URL using the URLPattern API.
|
|
248
|
+
*
|
|
249
|
+
* Accesses `globalThis.URLPattern` at runtime — no polyfill is imported by this
|
|
250
|
+
* library. If `URLPattern` is unavailable (Node.js < 24, older browsers without a
|
|
251
|
+
* polyfill), this method returns `{}` silently (graceful degradation — routing still
|
|
252
|
+
* works, params will be empty).
|
|
214
253
|
*
|
|
215
254
|
* @param pathname - The actual URL path (e.g., '/profile/john')
|
|
216
255
|
* @param stateId - The matched state ID for looking up route pattern
|
|
217
|
-
* @returns Extracted path parameters or empty object
|
|
256
|
+
* @returns Extracted path parameters, or empty object if URLPattern is unavailable or no match
|
|
218
257
|
*/
|
|
219
258
|
extractParams(pathname, stateId) {
|
|
220
259
|
const pattern = this.routeMap.getPathByStateId(stateId);
|
|
221
260
|
if (!pattern || !pattern.includes(":"))
|
|
222
261
|
return {};
|
|
223
262
|
try {
|
|
224
|
-
const
|
|
263
|
+
const URLPatternCtor = globalThis["URLPattern"];
|
|
264
|
+
if (!URLPatternCtor)
|
|
265
|
+
return {};
|
|
266
|
+
const urlPattern = new URLPatternCtor({ pathname: pattern });
|
|
225
267
|
const result = urlPattern.exec({ pathname });
|
|
226
268
|
if (!result)
|
|
227
269
|
return {};
|
|
@@ -239,7 +281,11 @@ export class RouterBridgeBase {
|
|
|
239
281
|
*/
|
|
240
282
|
extractQuery(search) {
|
|
241
283
|
try {
|
|
242
|
-
|
|
284
|
+
const params = {};
|
|
285
|
+
for (const [key, value] of Array.from(new URLSearchParams(search))) {
|
|
286
|
+
params[key] = value;
|
|
287
|
+
}
|
|
288
|
+
return params;
|
|
243
289
|
}
|
|
244
290
|
catch {
|
|
245
291
|
return {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router-bridge-base.js","sourceRoot":"","sources":["../src/router-bridge-base.ts"],"names":[],"mappings":"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
|
|
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
|
@@ -184,7 +184,8 @@ export interface PlayRouteEvent {
|
|
|
184
184
|
readonly to: string;
|
|
185
185
|
readonly params?: Record<string, string>;
|
|
186
186
|
readonly query?: Record<string, string>;
|
|
187
|
-
readonly match?:
|
|
187
|
+
readonly match?: unknown;
|
|
188
|
+
[key: string]: unknown;
|
|
188
189
|
}
|
|
189
190
|
/**
|
|
190
191
|
* RouterBridge interface for runtime infrastructure adapters
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAExC;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,mCAAmC;IACnC,IAAI,EAAE,SAAS,CAAC;IAChB,qEAAqE;IACrE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wCAAwC;IACxC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,UAAU,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,8BAA8B;IAC9B,QAAQ,EAAE,aAAa,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,mCAAmC;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,mBAAmB;IACnB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,mCAAmC;IACnC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,mCAAmC;IACnC,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACzB,sBAAsB;IACtB,IAAI,EAAE,SAAS,CAAC;IAChB;;;OAGG;IACH,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC;;;OAGG;IACH,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AACH,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAExC;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,mCAAmC;IACnC,IAAI,EAAE,SAAS,CAAC;IAChB,qEAAqE;IACrE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wCAAwC;IACxC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,UAAU,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,8BAA8B;IAC9B,QAAQ,EAAE,aAAa,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,mCAAmC;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,mBAAmB;IACnB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,mCAAmC;IACnC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,mCAAmC;IACnC,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACzB,sBAAsB;IACtB,IAAI,EAAE,SAAS,CAAC;IAChB;;;OAGG;IACH,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC;;;OAGG;IACH,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AACH,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,WAAW,YAAY;IAC5B;;;;;;;;;;;;;;OAcG;IACH,OAAO,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC;;;;;;;;;;;;;OAaG;IACH,UAAU,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC"}
|
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.9",
|
|
4
4
|
"description": "Route tree extraction from XState v5 state machines. Part of @xmachines/play Universal Player Architecture.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"routing",
|
|
@@ -28,24 +28,33 @@
|
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "tsc --build",
|
|
31
|
-
"clean": "rm -rf dist *.tsbuildinfo",
|
|
31
|
+
"clean": "rm -rf dist *.tsbuildinfo node_modules/.vite node_modules/.vite-temp",
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
|
-
"test": "vitest
|
|
33
|
+
"test": "vitest",
|
|
34
34
|
"prepublishOnly": "npm run build"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@xmachines/play": "1.0.0-beta.
|
|
38
|
-
"@xmachines/play-actor": "1.0.0-beta.
|
|
39
|
-
"@xmachines/play-signals": "1.0.0-beta.
|
|
40
|
-
"
|
|
37
|
+
"@xmachines/play": "1.0.0-beta.9",
|
|
38
|
+
"@xmachines/play-actor": "1.0.0-beta.9",
|
|
39
|
+
"@xmachines/play-signals": "1.0.0-beta.9",
|
|
40
|
+
"quick-lru": "^7.0.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^25.5.0",
|
|
44
|
-
"@xmachines/shared": "1.0.0-beta.
|
|
44
|
+
"@xmachines/shared": "1.0.0-beta.9",
|
|
45
45
|
"typescript": "^5.9.3",
|
|
46
|
-
"
|
|
46
|
+
"urlpattern-polyfill": "^10.1.0",
|
|
47
|
+
"vitest": "^4.1.0",
|
|
48
|
+
"xstate": "^5.0.0"
|
|
47
49
|
},
|
|
48
50
|
"peerDependencies": {
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
+
"urlpattern-polyfill": "^10.1.0",
|
|
52
|
+
"xstate": "^5.0.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"urlpattern-polyfill": {
|
|
56
|
+
"optional": true
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"_devDependencies_note": "xstate appears in both peerDependencies and devDependencies intentionally. devDependencies provides workspace resolution for local builds and tests. peerDependencies declares the consumer version constraint. Both are pinned to ^5.28.0 to prevent drift. urlpattern-polyfill is an optional peer: required on Node < 24 and older browsers without native URLPattern support. Consumers must load it before importing this package."
|
|
51
60
|
}
|