@xmachines/play-router 1.0.0-beta.8 → 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 +259 -335
- package/dist/base-route-map.d.ts +136 -0
- package/dist/base-route-map.d.ts.map +1 -0
- package/dist/base-route-map.js +182 -0
- package/dist/base-route-map.js.map +1 -0
- 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 +29 -33
- package/dist/create-route-map.d.ts.map +1 -1
- package/dist/create-route-map.js +25 -64
- 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 -51
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -133
- 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 +196 -32
- package/dist/router-bridge-base.d.ts.map +1 -1
- package/dist/router-bridge-base.js +319 -85
- 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 +42 -14
- package/dist/connect-router.d.ts +0 -56
- package/dist/connect-router.d.ts.map +0 -1
- package/dist/connect-router.js +0 -119
- 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 -68
- 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
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Captures the 90% identical logic from all existing router bridges:
|
|
5
5
|
* - TC39 Signal watcher for actor → router direction
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
6
|
+
* - `lastSyncedPath` for echo suppression in the actor→router direction
|
|
7
|
+
* - `isProcessingNavigation` flag for guard-redirect loop prevention in `syncActorFromRouter` only
|
|
8
|
+
* - `syncRouterFromActor` / `syncActorFromRouter` with URL parameter extraction
|
|
8
9
|
* - connect / disconnect lifecycle matching the RouterBridge protocol
|
|
9
10
|
*
|
|
10
11
|
* Subclasses implement only the 3 framework-specific abstract methods:
|
|
@@ -17,7 +18,7 @@
|
|
|
17
18
|
* export class MyRouterBridge extends RouterBridgeBase {
|
|
18
19
|
* private unsubscribe: (() => void) | null = null;
|
|
19
20
|
*
|
|
20
|
-
* constructor(private router: MyRouter, actor:
|
|
21
|
+
* constructor(private router: MyRouter, actor: RoutableActor, routeMap: { getStateIdByPath(path: string): string | undefined; getPathByStateId(id: string): string | undefined; }) {
|
|
21
22
|
* super(actor, routeMap);
|
|
22
23
|
* }
|
|
23
24
|
*
|
|
@@ -38,9 +39,23 @@
|
|
|
38
39
|
* }
|
|
39
40
|
* ```
|
|
40
41
|
*
|
|
41
|
-
* @see
|
|
42
|
+
* @see [Play RFC](../../docs/rfc/play.md) - Invariant INV-04
|
|
42
43
|
*/
|
|
43
|
-
import { Signal } from "@xmachines/play-signals";
|
|
44
|
+
import { Signal, watchSignal } from "@xmachines/play-signals";
|
|
45
|
+
import { DuplicateBridgeError, RouterSyncError } from "./errors.js";
|
|
46
|
+
import { buildPlayRouteEvent, extractQuery, extractRouteParams, matchRouteMap, sanitizePathname, } from "./router-sync.js";
|
|
47
|
+
import { URLPatternUnavailableError } from "./errors.js";
|
|
48
|
+
import { isParameterizedPattern } from "./url-pattern-utils.js";
|
|
49
|
+
/**
|
|
50
|
+
* Module-level registry of actors that currently have an active bridge connection.
|
|
51
|
+
*
|
|
52
|
+
* Keyed on the actor instance (WeakMap — no reference retention). Used to detect and
|
|
53
|
+
* reject duplicate `connect()` calls for the same actor, which would produce duplicate
|
|
54
|
+
* `play.route` events and conflicting `lastSyncedPath` echo-suppression state.
|
|
55
|
+
*
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
const activeBridges = new WeakMap();
|
|
44
59
|
/**
|
|
45
60
|
* Abstract base class for all `@xmachines` router adapter bridges.
|
|
46
61
|
*
|
|
@@ -54,21 +69,34 @@ export class RouterBridgeBase {
|
|
|
54
69
|
// ── Common state (identical across all 4 existing bridges) ──
|
|
55
70
|
isConnected = false;
|
|
56
71
|
hasConnectedOnce = false;
|
|
57
|
-
lastSyncedPath =
|
|
72
|
+
lastSyncedPath = null;
|
|
73
|
+
/**
|
|
74
|
+
* Guards `syncActorFromRouter` against re-entrant calls triggered by the
|
|
75
|
+
* actor's own guard redirects (router→actor send → signal fires → actor→router
|
|
76
|
+
* push → another syncActorFromRouter before the first one returns).
|
|
77
|
+
*
|
|
78
|
+
* NOT used for actor→router echo suppression — that is handled exclusively by
|
|
79
|
+
* `lastSyncedPath`, which is updated before `navigateRouter()` is called so
|
|
80
|
+
* any router callback for the same path short-circuits at the
|
|
81
|
+
* `sanitized === lastSyncedPath` check in `syncActorFromRouter`.
|
|
82
|
+
*/
|
|
58
83
|
isProcessingNavigation = false;
|
|
59
84
|
routeWatcher = null;
|
|
60
85
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
86
|
+
* @param actor - A `RoutableActor` exposing `currentRoute`, `initialRoute`, and `send`.
|
|
87
|
+
* @param routeMap - Bidirectional route map for `stateId ↔ path` resolution.
|
|
88
|
+
* Provide `getStateIdByPath` and `getPathByStateId`. Framework adapters
|
|
89
|
+
* typically wrap the result of `createRouteMap(machine)` or an equivalent.
|
|
90
|
+
* `getPathByStateId` may be keyed on either the `"#stateId"` or the bare
|
|
91
|
+
* `"stateId"` form — both forms are tried automatically by the bridge, so
|
|
92
|
+
* custom implementations (e.g. plain test objects) need only handle one.
|
|
65
93
|
*/
|
|
66
94
|
constructor(actor, routeMap) {
|
|
67
95
|
this.actor = actor;
|
|
68
96
|
this.routeMap = routeMap;
|
|
69
|
-
// Initialize lastSyncedPath to actor's current route (prevents initial sync loop)
|
|
70
|
-
//
|
|
71
|
-
this.lastSyncedPath = this.actor.currentRoute.get()
|
|
97
|
+
// Initialize lastSyncedPath to actor's current route (prevents initial sync loop).
|
|
98
|
+
// null means "nothing synced yet" — distinct from any real path string.
|
|
99
|
+
this.lastSyncedPath = this.actor.currentRoute.get() ?? null;
|
|
72
100
|
}
|
|
73
101
|
// ── RouterBridge protocol (final — subclasses must not override) ──
|
|
74
102
|
/**
|
|
@@ -76,23 +104,33 @@ export class RouterBridgeBase {
|
|
|
76
104
|
*
|
|
77
105
|
* Sets up the TC39 Signal watcher for actor → router direction and
|
|
78
106
|
* starts watching router changes (framework-specific).
|
|
107
|
+
*
|
|
108
|
+
* Ordering here is part of the bridge contract:
|
|
109
|
+
* - `lastSyncedPath` is seeded in the constructor from `actor.currentRoute`
|
|
110
|
+
* - the actor watcher is installed before adapter router subscriptions
|
|
111
|
+
* - initial sync then resolves deep-link vs restore using `actor.initialRoute`
|
|
112
|
+
*
|
|
113
|
+
* Adapters that need custom initial-sync behavior should override
|
|
114
|
+
* `getInitialRouterPath()` rather than reordering `connect()` steps.
|
|
79
115
|
*/
|
|
80
116
|
connect() {
|
|
81
117
|
if (this.isConnected) {
|
|
82
118
|
return;
|
|
83
119
|
}
|
|
120
|
+
// Enforce single-bridge-per-actor: two connected bridges for the same actor
|
|
121
|
+
// would both receive every router navigation event, sending duplicate play.route
|
|
122
|
+
// events and producing conflicting lastSyncedPath echo-suppression state.
|
|
123
|
+
const existingBridge = activeBridges.get(this.actor);
|
|
124
|
+
if (existingBridge !== undefined && existingBridge !== this) {
|
|
125
|
+
throw new DuplicateBridgeError();
|
|
126
|
+
}
|
|
127
|
+
activeBridges.set(this.actor, this);
|
|
84
128
|
this.isConnected = true;
|
|
85
129
|
this.hasConnectedOnce = true;
|
|
86
130
|
// Set up TC39 Signal watcher for actor → router direction
|
|
87
|
-
this.routeWatcher =
|
|
88
|
-
|
|
89
|
-
this.routeWatcher?.getPending(); // Acknowledge notification
|
|
90
|
-
const route = this.actor.currentRoute.get();
|
|
91
|
-
this.syncRouterFromActor(route);
|
|
92
|
-
this.routeWatcher?.watch(this.actor.currentRoute); // Re-watch (one-shot)
|
|
93
|
-
});
|
|
131
|
+
this.routeWatcher = createRouteWatcher(this.actor.currentRoute, (route) => {
|
|
132
|
+
this.syncRouterFromActor(route);
|
|
94
133
|
});
|
|
95
|
-
this.routeWatcher.watch(this.actor.currentRoute);
|
|
96
134
|
// Start watching router changes (framework-specific)
|
|
97
135
|
this.watchRouterChanges();
|
|
98
136
|
// Initial sync: router → actor takes priority over actor → router.
|
|
@@ -107,10 +145,58 @@ export class RouterBridgeBase {
|
|
|
107
145
|
// fire for the already-loaded location. Subclasses that can read the router's
|
|
108
146
|
// current location synchronously should override getInitialRouterPath().
|
|
109
147
|
const initialRouterPath = this.getInitialRouterPath();
|
|
148
|
+
const initialRouterSearch = this.getInitialRouterSearch();
|
|
110
149
|
const initialActorRoute = this.actor.currentRoute.get();
|
|
111
|
-
if (
|
|
112
|
-
//
|
|
113
|
-
|
|
150
|
+
if (typeof initialRouterPath === "string") {
|
|
151
|
+
// Check if actor is already at the router's current location.
|
|
152
|
+
// Actor currentRoute may be a stateId (e.g. "#app.home") while initialRouterPath
|
|
153
|
+
// is a URL path (e.g. "/home"). Resolve the path to a stateId before comparing
|
|
154
|
+
// to avoid a false "they differ" that would trigger an unnecessary initial sync.
|
|
155
|
+
const resolvedStateId = this.routeMap.getStateIdByPath(sanitizePathname(initialRouterPath) ?? initialRouterPath);
|
|
156
|
+
// The actor is already at the router's location when either side matches
|
|
157
|
+
// directly, or when the actor route is a stateId that resolves to the same
|
|
158
|
+
// registered path as the matched stateId. Lookups go through
|
|
159
|
+
// lookupPathByStateId, which tries both the "#stateId" and bare "stateId"
|
|
160
|
+
// forms automatically — so a route map storing "#about" recognizes an
|
|
161
|
+
// actor route of "about" (and vice versa) even when the map is a custom
|
|
162
|
+
// structural implementation keyed on a single form. Concrete-path actor
|
|
163
|
+
// routes (starting with "/") are compared directly against the router
|
|
164
|
+
// path only.
|
|
165
|
+
const actorAlreadyAtRouterLocation = resolvedStateId !== null &&
|
|
166
|
+
resolvedStateId !== undefined &&
|
|
167
|
+
(resolvedStateId === initialActorRoute ||
|
|
168
|
+
initialRouterPath === initialActorRoute ||
|
|
169
|
+
(initialActorRoute != null &&
|
|
170
|
+
!initialActorRoute.startsWith("/") &&
|
|
171
|
+
this.lookupPathByStateId(initialActorRoute) != null &&
|
|
172
|
+
this.lookupPathByStateId(initialActorRoute) ===
|
|
173
|
+
this.lookupPathByStateId(resolvedStateId)));
|
|
174
|
+
if (!actorAlreadyAtRouterLocation) {
|
|
175
|
+
// Router path differs from actor route — but is this a deep-link or a restore?
|
|
176
|
+
//
|
|
177
|
+
// Deep-link: router is at a non-initial URL the actor hasn't seen yet.
|
|
178
|
+
// → router wins: syncActorFromRouter (guards then evaluate access).
|
|
179
|
+
//
|
|
180
|
+
// Restore: browser is at the machine's initial URL while the actor was
|
|
181
|
+
// restored to a different route from a snapshot.
|
|
182
|
+
// → actor wins: push actor's restored route to the router.
|
|
183
|
+
//
|
|
184
|
+
// Detection: if the router URL equals the machine's initial route AND the
|
|
185
|
+
// actor is at a different route, this is a restore scenario.
|
|
186
|
+
if (initialActorRoute &&
|
|
187
|
+
initialRouterPath === this.actor.initialRoute &&
|
|
188
|
+
initialActorRoute !== this.actor.initialRoute) {
|
|
189
|
+
this.pushResolvedRoute(initialActorRoute);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
this.syncActorFromRouter(initialRouterPath, initialRouterSearch);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else if (initialActorRoute && initialRouterPath === null) {
|
|
197
|
+
// Explicit null: no router path available (restore/bootstrap) → push actor route.
|
|
198
|
+
// undefined means the adapter handles initial sync itself — fall through.
|
|
199
|
+
this.pushResolvedRoute(initialActorRoute);
|
|
114
200
|
}
|
|
115
201
|
else if (initialActorRoute && initialActorRoute !== this.lastSyncedPath) {
|
|
116
202
|
// No router path override → sync router from actor (original behaviour)
|
|
@@ -126,7 +212,7 @@ export class RouterBridgeBase {
|
|
|
126
212
|
const hadRouteWatcher = this.routeWatcher !== null;
|
|
127
213
|
if (this.routeWatcher) {
|
|
128
214
|
try {
|
|
129
|
-
this.routeWatcher.unwatch(
|
|
215
|
+
this.routeWatcher.unwatch();
|
|
130
216
|
}
|
|
131
217
|
catch {
|
|
132
218
|
// Ignore detached watcher errors to keep disconnect idempotent.
|
|
@@ -137,15 +223,33 @@ export class RouterBridgeBase {
|
|
|
137
223
|
this.unwatchRouterChanges();
|
|
138
224
|
}
|
|
139
225
|
this.isProcessingNavigation = false;
|
|
140
|
-
this.hasConnectedOnce = true;
|
|
141
226
|
this.isConnected = false;
|
|
227
|
+
// Release the actor slot so a new bridge can connect to the same actor.
|
|
228
|
+
// Only clear if this bridge is the currently registered one — a second bridge
|
|
229
|
+
// that was rejected at connect() time must not evict the legitimate bridge.
|
|
230
|
+
if (activeBridges.get(this.actor) === this) {
|
|
231
|
+
activeBridges.delete(this.actor);
|
|
232
|
+
}
|
|
142
233
|
}
|
|
143
234
|
// ── Sync methods (protected, overridable if subclass needs custom behavior) ──
|
|
144
235
|
/**
|
|
145
236
|
* Sync router location when actor route signal changes.
|
|
146
237
|
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
238
|
+
* Resolves the actor route to a concrete URL path and calls navigateRouter()
|
|
239
|
+
* for framework-specific navigation. When the route cannot be resolved
|
|
240
|
+
* (unknown stateId, parameterized pattern with no concrete values) the push
|
|
241
|
+
* is skipped entirely — `navigateRouter` is never called with a raw stateId
|
|
242
|
+
* or pattern.
|
|
243
|
+
*
|
|
244
|
+
* Echo suppression — preventing the router's own callback from re-driving the
|
|
245
|
+
* actor — is handled entirely by `lastSyncedPath`: it is set to the resolved
|
|
246
|
+
* path before `navigateRouter()` is called, so any `syncActorFromRouter`
|
|
247
|
+
* invocation for the same path short-circuits at the
|
|
248
|
+
* `sanitized === lastSyncedPath` check and sends no event regardless of
|
|
249
|
+
* whether the callback fires synchronously or asynchronously.
|
|
250
|
+
*
|
|
251
|
+
* `isProcessingNavigation` is NOT set here — it is only used inside
|
|
252
|
+
* `syncActorFromRouter` to guard against re-entrant guard-redirect loops.
|
|
149
253
|
*/
|
|
150
254
|
syncRouterFromActor(route) {
|
|
151
255
|
if (this.hasConnectedOnce && !this.isConnected)
|
|
@@ -154,55 +258,98 @@ export class RouterBridgeBase {
|
|
|
154
258
|
return;
|
|
155
259
|
if (route === this.lastSyncedPath)
|
|
156
260
|
return;
|
|
157
|
-
|
|
261
|
+
this.pushResolvedRoute(route);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Resolve an actor route to its concrete URL path and push it to the router.
|
|
265
|
+
*
|
|
266
|
+
* lastSyncedPath must store the concrete path so that the router callback
|
|
267
|
+
* (which fires with the concrete path) matches and short-circuits in
|
|
268
|
+
* syncActorFromRouter. Storing the raw stateId (e.g. "#home") would cause a
|
|
269
|
+
* mismatch against the sanitized path ("/") in the watcher.
|
|
270
|
+
*
|
|
271
|
+
* When resolveNavigationPath returns null (parameterized/wildcard pattern,
|
|
272
|
+
* unknown id) the push is skipped entirely — pushing the raw value would write
|
|
273
|
+
* stateIds, `:param` patterns, or a literal `*` into the browser URL.
|
|
274
|
+
* lastSyncedPath is still set to the
|
|
275
|
+
* raw route so the dedup guard fires correctly on the next identical signal value.
|
|
276
|
+
*/
|
|
277
|
+
pushResolvedRoute(route) {
|
|
278
|
+
const resolved = this.resolveNavigationPath(route);
|
|
279
|
+
this.lastSyncedPath = resolved ?? route;
|
|
280
|
+
if (resolved === null)
|
|
158
281
|
return;
|
|
159
|
-
this.
|
|
160
|
-
this.isProcessingNavigation = true;
|
|
161
|
-
this.navigateRouter(route);
|
|
162
|
-
queueMicrotask(() => {
|
|
163
|
-
this.isProcessingNavigation = false;
|
|
164
|
-
});
|
|
282
|
+
this.navigateRouter(resolved);
|
|
165
283
|
}
|
|
166
284
|
/**
|
|
167
285
|
* Sync actor state when router location changes.
|
|
168
286
|
*
|
|
169
|
-
*
|
|
170
|
-
* Prevents circular updates via
|
|
287
|
+
* **Known path:** sends a `play.route` event to the actor with the matched
|
|
288
|
+
* stateId, params, and query. Prevents circular updates via the
|
|
289
|
+
* `isProcessingNavigation` flag.
|
|
290
|
+
*
|
|
291
|
+
* **Unknown/unmapped path:** does NOT send a `play.route` event (actor state
|
|
292
|
+
* is unchanged). Instead, actively corrects the browser URL by calling
|
|
293
|
+
* `navigateRouter(actor.currentRoute.get())` — keeping the URL in sync with
|
|
294
|
+
* actor state even when the user types an invalid path into the address bar
|
|
295
|
+
* or pushes one programmatically mid-session. `lastSyncedPath` is set to the
|
|
296
|
+
* resolved concrete path before calling `navigateRouter` so the router's own
|
|
297
|
+
* callback for that navigation short-circuits the echo-suppression guard and
|
|
298
|
+
* sends no spurious event.
|
|
171
299
|
*/
|
|
172
300
|
syncActorFromRouter(pathname, search) {
|
|
173
301
|
if (this.hasConnectedOnce && !this.isConnected)
|
|
174
302
|
return;
|
|
175
303
|
if (typeof pathname !== "string")
|
|
176
304
|
return;
|
|
177
|
-
|
|
305
|
+
const sanitized = sanitizePathname(pathname);
|
|
306
|
+
if (sanitized === null)
|
|
307
|
+
return; // Path too long — reject
|
|
308
|
+
if (sanitized === this.lastSyncedPath)
|
|
178
309
|
return;
|
|
179
310
|
if (this.isProcessingNavigation)
|
|
180
311
|
return;
|
|
181
312
|
this.isProcessingNavigation = true;
|
|
182
313
|
try {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
314
|
+
const nextRoute = buildPlayRouteEvent({
|
|
315
|
+
pathname,
|
|
316
|
+
search,
|
|
317
|
+
match: (nextPathname) => matchRouteMap(nextPathname, this.routeMap, (resolvedPathname, stateId) => this.extractParams(resolvedPathname, stateId)),
|
|
318
|
+
});
|
|
319
|
+
if (!nextRoute) {
|
|
320
|
+
// Unknown path — no matching route in the map.
|
|
321
|
+
// Push the actor's current route back to the router so the browser URL
|
|
322
|
+
// stays in sync with actor state (corrects an invalid address-bar entry
|
|
323
|
+
// on cold-load or mid-session navigation to a non-existent path).
|
|
324
|
+
//
|
|
325
|
+
// Call pushResolvedRoute() directly rather than syncRouterFromActor() to
|
|
326
|
+
// avoid the dedup guard (lastSyncedPath is already the actor's route).
|
|
327
|
+
// It sets lastSyncedPath to the resolved concrete path before pushing so
|
|
328
|
+
// that the router's own callback for this navigation (hashchange /
|
|
329
|
+
// history event) short-circuits in the sanitized === lastSyncedPath
|
|
330
|
+
// guard and does not send a spurious play.route event to the actor.
|
|
331
|
+
//
|
|
332
|
+
// isProcessingNavigation is left true here; the finally block below
|
|
333
|
+
// clears it after navigateRouter returns, providing a synchronous
|
|
334
|
+
// re-entrancy guard for any router callback that fires inline.
|
|
335
|
+
const currentActorRoute = this.actor.currentRoute.get();
|
|
336
|
+
if (currentActorRoute) {
|
|
337
|
+
this.pushResolvedRoute(currentActorRoute);
|
|
338
|
+
}
|
|
186
339
|
return;
|
|
187
340
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// formatPlayRouteTransitions generates guards that match event.to === "#stateId"
|
|
191
|
-
// RouteMap stateIds from extractMachineRoutes don't include the "#" prefix,
|
|
192
|
-
// so we normalise here. If the routeMap already stores "#stateId" (as some
|
|
193
|
-
// manually-constructed maps do) we avoid doubling the prefix.
|
|
194
|
-
const eventTarget = stateId.startsWith("#") ? stateId : `#${stateId}`;
|
|
195
|
-
const event = {
|
|
196
|
-
type: "play.route",
|
|
197
|
-
to: eventTarget,
|
|
198
|
-
params,
|
|
199
|
-
query,
|
|
200
|
-
};
|
|
201
|
-
this.actor.send(event);
|
|
202
|
-
this.lastSyncedPath = pathname;
|
|
341
|
+
this.actor.send(nextRoute.event);
|
|
342
|
+
this.lastSyncedPath = nextRoute.pathname;
|
|
203
343
|
}
|
|
204
344
|
catch (error) {
|
|
205
|
-
|
|
345
|
+
// URLPatternUnavailableError is a user-actionable error — propagate as-is
|
|
346
|
+
// so callers can detect it and prompt the user to add a URLPattern polyfill.
|
|
347
|
+
if (error instanceof URLPatternUnavailableError) {
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
throw new RouterSyncError("Failed to sync actor state from router location.", {
|
|
351
|
+
cause: error,
|
|
352
|
+
});
|
|
206
353
|
}
|
|
207
354
|
finally {
|
|
208
355
|
this.isProcessingNavigation = false;
|
|
@@ -210,26 +357,68 @@ export class RouterBridgeBase {
|
|
|
210
357
|
}
|
|
211
358
|
// ── Utilities (protected, overridable for framework-native param extraction) ──
|
|
212
359
|
/**
|
|
213
|
-
* Extract path parameters from URL using URLPattern API.
|
|
360
|
+
* Extract path parameters from URL using the URLPattern API.
|
|
361
|
+
*
|
|
362
|
+
* Accesses `globalThis.URLPattern` at runtime — no polyfill is imported by this
|
|
363
|
+
* library. If `URLPattern` is unavailable and the matched route has parameterized
|
|
364
|
+
* segments, a `URLPatternUnavailableError` is thrown — callers must provide a polyfill
|
|
365
|
+
* for environments without native URLPattern support (Node.js < 24, older browsers).
|
|
214
366
|
*
|
|
215
367
|
* @param pathname - The actual URL path (e.g., '/profile/john')
|
|
216
|
-
* @param stateId - The matched state ID for looking up route pattern
|
|
217
|
-
* @returns Extracted path parameters or empty object
|
|
368
|
+
* @param stateId - The matched state ID for looking up the route pattern
|
|
369
|
+
* @returns Extracted path parameters, or empty object if no match
|
|
370
|
+
* @throws {URLPatternUnavailableError} When URLPattern is absent and the route is parameterized
|
|
218
371
|
*/
|
|
219
372
|
extractParams(pathname, stateId) {
|
|
220
373
|
const pattern = this.routeMap.getPathByStateId(stateId);
|
|
221
|
-
if (!pattern
|
|
222
|
-
return {};
|
|
223
|
-
try {
|
|
224
|
-
const urlPattern = new URLPattern({ pathname: pattern });
|
|
225
|
-
const result = urlPattern.exec({ pathname });
|
|
226
|
-
if (!result)
|
|
227
|
-
return {};
|
|
228
|
-
return result.pathname.groups || {};
|
|
229
|
-
}
|
|
230
|
-
catch {
|
|
374
|
+
if (!pattern)
|
|
231
375
|
return {};
|
|
232
|
-
|
|
376
|
+
return extractRouteParams(pathname, pattern);
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Resolve an actor route value to a concrete URL path for navigation.
|
|
380
|
+
*
|
|
381
|
+
* Bridges that receive raw `actor.currentRoute` values in `navigateRouter`
|
|
382
|
+
* can call this to normalize stateIds (with or without `#` prefix) to paths.
|
|
383
|
+
* Returns `null` when navigation is not possible:
|
|
384
|
+
* - unknown stateId with no route map entry
|
|
385
|
+
* - parameterized or wildcard pattern (e.g. `/profile/:id`, `/docs/*`) — no
|
|
386
|
+
* concrete values available to build a real URL
|
|
387
|
+
* - non-path string that isn't a known stateId
|
|
388
|
+
*
|
|
389
|
+
* StateId lookups try both the `"#stateId"` and bare `"stateId"` forms
|
|
390
|
+
* automatically (via `lookupPathByStateId`), so custom route maps keyed on
|
|
391
|
+
* only one form still resolve.
|
|
392
|
+
*
|
|
393
|
+
* @param route - Raw actor route value (stateId, `#`-stateId, or concrete path)
|
|
394
|
+
* @returns Concrete URL path, or `null` if navigation should be skipped
|
|
395
|
+
*/
|
|
396
|
+
resolveNavigationPath(route) {
|
|
397
|
+
const mapped = this.lookupPathByStateId(route);
|
|
398
|
+
const path = mapped ?? (route.startsWith("/") ? route : null);
|
|
399
|
+
if (path === null || isParameterizedPattern(path))
|
|
400
|
+
return null;
|
|
401
|
+
return path;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Look up a route map path for an actor route value, trying both stateId forms.
|
|
405
|
+
*
|
|
406
|
+
* The actor may emit `"#stateId"` while a custom route map is keyed on the bare
|
|
407
|
+
* `"stateId"` form (or vice versa). `RouteMap` canonicalizes internally, but the
|
|
408
|
+
* constructor accepts any structural `{ getStateIdByPath, getPathByStateId }`
|
|
409
|
+
* object — plain test objects and adapter-specific maps are documented-legal —
|
|
410
|
+
* so both forms are tried automatically here: raw first, then the `#`-stripped
|
|
411
|
+
* form, then the `#`-prefixed form.
|
|
412
|
+
*
|
|
413
|
+
* @param route - Raw actor route value (stateId with or without `#` prefix)
|
|
414
|
+
* @returns The mapped path, or `null`/`undefined` if no form is registered
|
|
415
|
+
*/
|
|
416
|
+
lookupPathByStateId(route) {
|
|
417
|
+
const direct = this.routeMap.getPathByStateId(route);
|
|
418
|
+
if (direct != null)
|
|
419
|
+
return direct;
|
|
420
|
+
const alternate = route.startsWith("#") ? route.slice(1) : `#${route}`;
|
|
421
|
+
return this.routeMap.getPathByStateId(alternate);
|
|
233
422
|
}
|
|
234
423
|
/**
|
|
235
424
|
* Extract query parameters from URL search string.
|
|
@@ -238,19 +427,29 @@ export class RouterBridgeBase {
|
|
|
238
427
|
* @returns Extracted query parameters or empty object
|
|
239
428
|
*/
|
|
240
429
|
extractQuery(search) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
430
|
+
return extractQuery(search);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Sanitize and validate a raw URL pathname received from the router.
|
|
434
|
+
*
|
|
435
|
+
* Applies the path-length cap (2048 chars), strips query strings and fragments
|
|
436
|
+
* that may have been included in the pathname segment, and normalises
|
|
437
|
+
* consecutive slashes.
|
|
438
|
+
*
|
|
439
|
+
* Implementations that bypass `syncActorFromRouter()` (e.g. when using
|
|
440
|
+
* framework-native reactive watchers that receive pre-parsed route objects)
|
|
441
|
+
* MUST call this method before passing the path to any route-map lookup.
|
|
442
|
+
* `syncActorFromRouter()` calls this internally, so bridges that delegate
|
|
443
|
+
* to it do not need to call `sanitizePath` themselves.
|
|
444
|
+
*
|
|
445
|
+
* @param pathname - Raw URL pathname from the framework router.
|
|
446
|
+
* @returns Sanitized pathname, or `null` if the path is invalid / too long.
|
|
447
|
+
*/
|
|
448
|
+
sanitizePath(pathname) {
|
|
449
|
+
return sanitizePathname(pathname);
|
|
251
450
|
}
|
|
252
451
|
/**
|
|
253
|
-
* Return the router's current pathname at connect() time
|
|
452
|
+
* Return the router's current pathname at connect() time.
|
|
254
453
|
*
|
|
255
454
|
* Called once during connect() to perform the initial URL → actor sync.
|
|
256
455
|
* router.subscribe() only fires on *future* navigation events; it does not
|
|
@@ -259,11 +458,46 @@ export class RouterBridgeBase {
|
|
|
259
458
|
* should override this method so that deep-link / direct-URL loads drive the
|
|
260
459
|
* actor to the correct state instead of leaving it at its machine default.
|
|
261
460
|
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
461
|
+
* Return semantics:
|
|
462
|
+
* - `string` → router has a current path; base connect() will sync actor from router
|
|
463
|
+
* - `null` → router is active but has no current path yet; base connect() will sync router from actor
|
|
464
|
+
* - `undefined` → adapter handles initial sync itself and base connect() should stay out of the way
|
|
465
|
+
*
|
|
466
|
+
* The default returns `undefined`, preserving the previous behaviour for
|
|
467
|
+
* bridges that have not yet implemented this hook.
|
|
264
468
|
*/
|
|
265
469
|
getInitialRouterPath() {
|
|
266
|
-
return
|
|
470
|
+
return undefined;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Return the router's current search string at connect() time.
|
|
474
|
+
*
|
|
475
|
+
* Paired with `getInitialRouterPath()` — called once during connect() to pass
|
|
476
|
+
* the initial URL query string to `syncActorFromRouter()`. If the router's current
|
|
477
|
+
* URL has no search (or the subclass doesn't override this), returns `undefined`
|
|
478
|
+
* and `syncActorFromRouter` will produce an empty `query: {}` in the event.
|
|
479
|
+
*
|
|
480
|
+
* Subclasses that override `getInitialRouterPath()` and have a query string
|
|
481
|
+
* available should also override this method.
|
|
482
|
+
*
|
|
483
|
+
* @returns URL search string (e.g. `"?tab=security"`), or `undefined` if not available.
|
|
484
|
+
*/
|
|
485
|
+
getInitialRouterSearch() {
|
|
486
|
+
return undefined;
|
|
267
487
|
}
|
|
268
488
|
}
|
|
489
|
+
function noopCleanup() { }
|
|
490
|
+
function createRouteWatcher(signal, onRoute) {
|
|
491
|
+
let cleanup = noopCleanup;
|
|
492
|
+
const watcher = {
|
|
493
|
+
watch(nextSignal) {
|
|
494
|
+
cleanup = watchSignal(nextSignal, onRoute);
|
|
495
|
+
},
|
|
496
|
+
unwatch() {
|
|
497
|
+
cleanup();
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
watcher.watch(signal);
|
|
501
|
+
return watcher;
|
|
502
|
+
}
|
|
269
503
|
//# sourceMappingURL=router-bridge-base.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router-bridge-base.js","sourceRoot":"","sources":["../src/router-bridge-base.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"router-bridge-base.js","sourceRoot":"","sources":["../src/router-bridge-base.ts"],"names":[],"mappings":"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"}
|