@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.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +259 -335
  3. package/dist/base-route-map.d.ts +136 -0
  4. package/dist/base-route-map.d.ts.map +1 -0
  5. package/dist/base-route-map.js +182 -0
  6. package/dist/base-route-map.js.map +1 -0
  7. package/dist/build-tree.d.ts +7 -0
  8. package/dist/build-tree.d.ts.map +1 -1
  9. package/dist/build-tree.js +21 -6
  10. package/dist/build-tree.js.map +1 -1
  11. package/dist/create-route-map-from-tree.d.ts +33 -0
  12. package/dist/create-route-map-from-tree.d.ts.map +1 -0
  13. package/dist/create-route-map-from-tree.js +43 -0
  14. package/dist/create-route-map-from-tree.js.map +1 -0
  15. package/dist/create-route-map.d.ts +29 -33
  16. package/dist/create-route-map.d.ts.map +1 -1
  17. package/dist/create-route-map.js +25 -64
  18. package/dist/create-route-map.js.map +1 -1
  19. package/dist/errors.d.ts +152 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +186 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/extract-routes.d.ts +5 -31
  24. package/dist/extract-routes.d.ts.map +1 -1
  25. package/dist/extract-routes.js +73 -51
  26. package/dist/extract-routes.js.map +1 -1
  27. package/dist/find-route.d.ts +44 -0
  28. package/dist/find-route.d.ts.map +1 -0
  29. package/dist/find-route.js +103 -0
  30. package/dist/find-route.js.map +1 -0
  31. package/dist/index.d.ts +13 -51
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +13 -133
  34. package/dist/index.js.map +1 -1
  35. package/dist/machine-to-graph.d.ts +17 -0
  36. package/dist/machine-to-graph.d.ts.map +1 -0
  37. package/dist/machine-to-graph.js +115 -0
  38. package/dist/machine-to-graph.js.map +1 -0
  39. package/dist/query.d.ts +49 -1
  40. package/dist/query.d.ts.map +1 -1
  41. package/dist/query.js +98 -3
  42. package/dist/query.js.map +1 -1
  43. package/dist/router-bridge-base.d.ts +196 -32
  44. package/dist/router-bridge-base.d.ts.map +1 -1
  45. package/dist/router-bridge-base.js +319 -85
  46. package/dist/router-bridge-base.js.map +1 -1
  47. package/dist/router-sync.d.ts +95 -0
  48. package/dist/router-sync.d.ts.map +1 -0
  49. package/dist/router-sync.js +161 -0
  50. package/dist/router-sync.js.map +1 -0
  51. package/dist/types.d.ts +163 -22
  52. package/dist/types.d.ts.map +1 -1
  53. package/dist/url-pattern-utils.d.ts +117 -0
  54. package/dist/url-pattern-utils.d.ts.map +1 -0
  55. package/dist/url-pattern-utils.js +176 -0
  56. package/dist/url-pattern-utils.js.map +1 -0
  57. package/dist/validate-routes.d.ts +32 -14
  58. package/dist/validate-routes.d.ts.map +1 -1
  59. package/dist/validate-routes.js +28 -21
  60. package/dist/validate-routes.js.map +1 -1
  61. package/package.json +42 -14
  62. package/dist/connect-router.d.ts +0 -56
  63. package/dist/connect-router.d.ts.map +0 -1
  64. package/dist/connect-router.js +0 -119
  65. package/dist/connect-router.js.map +0 -1
  66. package/dist/crawl-machine.d.ts +0 -74
  67. package/dist/crawl-machine.d.ts.map +0 -1
  68. package/dist/crawl-machine.js +0 -95
  69. package/dist/crawl-machine.js.map +0 -1
  70. package/dist/create-browser-history.d.ts +0 -68
  71. package/dist/create-browser-history.d.ts.map +0 -1
  72. package/dist/create-browser-history.js +0 -94
  73. package/dist/create-browser-history.js.map +0 -1
  74. package/dist/create-router.d.ts +0 -73
  75. package/dist/create-router.d.ts.map +0 -1
  76. package/dist/create-router.js +0 -63
  77. package/dist/create-router.js.map +0 -1
  78. package/dist/extract-route.d.ts +0 -25
  79. package/dist/extract-route.d.ts.map +0 -1
  80. package/dist/extract-route.js +0 -63
  81. 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
- * - isProcessingNavigation flag for circular update prevention
7
- * - syncRouterFromActor / syncActorFromRouter with URL parameter extraction
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: AbstractActor<AnyActorLogic> & Routable, routeMap: { getStateIdByPath(path: string): string | undefined; getPathByStateId(id: string): string | undefined; }) {
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 {@link https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md | RFC Play v1 - Invariant INV-04}
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
- * Constructor receives the 3 things all bridges need.
62
- *
63
- * @param actor - Actor with currentRoute signal and send method
64
- * @param routeMap - Bidirectional route map for stateId ↔ path resolution
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
- // Pattern from VueRouterBridge and SolidRouterBridge
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 = new Signal.subtle.Watcher(() => {
88
- queueMicrotask(() => {
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 (initialRouterPath && initialRouterPath !== initialActorRoute) {
112
- // Router path differs from actor state → sync actor from router
113
- this.syncActorFromRouter(initialRouterPath);
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(this.actor.currentRoute);
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
- * Calls navigateRouter() for framework-specific navigation.
148
- * Prevents circular updates via isProcessingNavigation flag.
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
- if (this.isProcessingNavigation)
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.lastSyncedPath = route;
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
- * Sends play.route event to actor with resolved stateId, params, and query.
170
- * Prevents circular updates via isProcessingNavigation flag.
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
- if (pathname === this.lastSyncedPath)
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 stateId = this.routeMap.getStateIdByPath(pathname);
184
- if (!stateId) {
185
- this.isProcessingNavigation = false;
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
- const params = this.extractParams(pathname, stateId);
189
- const query = this.extractQuery(search || "");
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
- console.error("[RouterBridgeBase] Error sending event:", error);
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 || !pattern.includes(":"))
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
- try {
242
- const params = {};
243
- for (const [key, value] of Array.from(new URLSearchParams(search))) {
244
- params[key] = value;
245
- }
246
- return params;
247
- }
248
- catch {
249
- return {};
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, or null if unavailable.
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
- * The default returns null (no initial router → actor sync), preserving the
263
- * previous behaviour for bridges that have not yet implemented this hook.
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 null;
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":"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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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"}