@xmachines/play-router 1.0.0-beta.2 → 1.0.0-beta.21

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 (66) hide show
  1. package/README.md +169 -47
  2. package/dist/base-route-map.d.ts +116 -0
  3. package/dist/base-route-map.d.ts.map +1 -0
  4. package/dist/base-route-map.js +206 -0
  5. package/dist/base-route-map.js.map +1 -0
  6. package/dist/build-tree.d.ts.map +1 -1
  7. package/dist/build-tree.js +6 -5
  8. package/dist/build-tree.js.map +1 -1
  9. package/dist/connect-router.d.ts.map +1 -1
  10. package/dist/connect-router.js +35 -45
  11. package/dist/connect-router.js.map +1 -1
  12. package/dist/create-browser-history.d.ts +38 -5
  13. package/dist/create-browser-history.d.ts.map +1 -1
  14. package/dist/create-browser-history.js +43 -17
  15. package/dist/create-browser-history.js.map +1 -1
  16. package/dist/create-route-map.d.ts +21 -1
  17. package/dist/create-route-map.d.ts.map +1 -1
  18. package/dist/create-route-map.js +73 -22
  19. package/dist/create-route-map.js.map +1 -1
  20. package/dist/errors.d.ts +75 -0
  21. package/dist/errors.d.ts.map +1 -0
  22. package/dist/errors.js +85 -0
  23. package/dist/errors.js.map +1 -0
  24. package/dist/extract-routes.d.ts +5 -31
  25. package/dist/extract-routes.d.ts.map +1 -1
  26. package/dist/extract-routes.js +70 -49
  27. package/dist/extract-routes.js.map +1 -1
  28. package/dist/find-route.d.ts +44 -0
  29. package/dist/find-route.d.ts.map +1 -0
  30. package/dist/find-route.js +126 -0
  31. package/dist/find-route.js.map +1 -0
  32. package/dist/index.d.ts +9 -48
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +16 -131
  35. package/dist/index.js.map +1 -1
  36. package/dist/machine-to-graph.d.ts +17 -0
  37. package/dist/machine-to-graph.d.ts.map +1 -0
  38. package/dist/machine-to-graph.js +115 -0
  39. package/dist/machine-to-graph.js.map +1 -0
  40. package/dist/query.d.ts +44 -1
  41. package/dist/query.d.ts.map +1 -1
  42. package/dist/query.js +80 -3
  43. package/dist/query.js.map +1 -1
  44. package/dist/router-bridge-base.d.ts +49 -19
  45. package/dist/router-bridge-base.d.ts.map +1 -1
  46. package/dist/router-bridge-base.js +120 -56
  47. package/dist/router-bridge-base.js.map +1 -1
  48. package/dist/router-sync.d.ts +62 -0
  49. package/dist/router-sync.d.ts.map +1 -0
  50. package/dist/router-sync.js +87 -0
  51. package/dist/router-sync.js.map +1 -0
  52. package/dist/types.d.ts +73 -14
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/validate-routes.d.ts +9 -9
  55. package/dist/validate-routes.d.ts.map +1 -1
  56. package/dist/validate-routes.js +12 -11
  57. package/dist/validate-routes.js.map +1 -1
  58. package/package.json +36 -18
  59. package/dist/crawl-machine.d.ts +0 -74
  60. package/dist/crawl-machine.d.ts.map +0 -1
  61. package/dist/crawl-machine.js +0 -95
  62. package/dist/crawl-machine.js.map +0 -1
  63. package/dist/extract-route.d.ts +0 -25
  64. package/dist/extract-route.d.ts.map +0 -1
  65. package/dist/extract-route.js +0 -63
  66. package/dist/extract-route.js.map +0 -1
package/README.md CHANGED
@@ -2,17 +2,19 @@
2
2
 
3
3
  **Route tree extraction from XState v5 state machines with routing patterns**
4
4
 
5
- BFS graph crawling and bidirectional route lookup enabling Actor Authority over navigation.
5
+ Graph-based route extraction and bidirectional lookup enabling Actor Authority over navigation.
6
6
 
7
7
  ## Overview
8
8
 
9
- `@xmachines/play-router` extracts route trees from XState state machines by crawling the state graph using breadth-first traversal. It extracts `meta.route` paths from state machines and builds hierarchical route trees with bidirectional state ID ↔ path mapping.
9
+ `@xmachines/play-router` extracts route trees from XState state machines using `@statelyai/graph` a typed, JSON-serializable graph library. It converts machines to a directed `Graph` with hierarchy, transition edges, and route metadata, then builds hierarchical `RouteTree` structures with bidirectional state ID ↔ path mapping.
10
10
 
11
11
  It also exports `RouterBridgeBase`, the shared base class used by framework adapters to implement `RouterBridge` with consistent actor↔router synchronization behavior.
12
12
 
13
13
  `RouterBridgeBase` is the policy point; framework adapters are thin ports that implement only framework-specific navigate/subscribe/unsubscribe behavior.
14
14
 
15
- Per [RFC Play v1](https://gitlab.com/xmachin-es/rfc/-/blob/main/src/play-v1.md), this package implements:
15
+ The low-level `connectRouter()` path now uses the same router-to-actor event builder and route-map match helper as `RouterBridgeBase`, so pathname sanitization, state-id normalization, param extraction, and query extraction stay aligned across vanilla and framework adapters.
16
+
17
+ Per [Play RFC](../docs/rfc/play.md), this package implements:
16
18
 
17
19
  - **Actor Authority (INV-01):** Routes derive from machine definitions, not external configuration
18
20
 
@@ -27,13 +29,32 @@ npm install @xmachines/play-router
27
29
 
28
30
  **Peer dependencies:**
29
31
 
30
- - `xstate` ^5.0.0 - State machine runtime
32
+ - `xstate` ^5.0.0 State machine runtime
33
+
34
+ **URLPattern polyfill (Node.js < 24 / older browsers):**
35
+
36
+ `@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+).
37
+
38
+ On environments without native support, load a polyfill **before** importing this package:
39
+
40
+ ```typescript
41
+ // Entry point — must run before any @xmachines/play-router import
42
+ import "urlpattern-polyfill";
43
+ ```
44
+
45
+ Install the polyfill:
46
+
47
+ ```bash
48
+ npm install urlpattern-polyfill
49
+ ```
50
+
51
+ `urlpattern-polyfill` is declared as an optional peer dependency. Package managers will not install it automatically — consumers must install and load it when their runtime lacks native URLPattern support (Node.js < 24, older browsers).
31
52
 
32
53
  ## Quick Start
33
54
 
34
55
  ```typescript
35
56
  import { createMachine } from "xstate";
36
- import { extractMachineRoutes } from "@xmachines/play-router";
57
+ import { extractMachineRoutes, findRouteByPath } from "@xmachines/play-router";
37
58
 
38
59
  // Route pattern (recommended)
39
60
  const machine = createMachine({
@@ -65,11 +86,11 @@ const machine = createMachine({
65
86
  const tree = extractMachineRoutes(machine);
66
87
 
67
88
  // Bidirectional lookup
68
- console.log(tree.byPath.get("/dashboard/overview")); // RouteNode
69
- console.log(tree.byId.get("overview")); // RouteNode
89
+ console.log(findRouteByPath(tree, "/overview")); // RouteNode
90
+ console.log(tree.byStateId.get("overview")); // RouteNode
70
91
 
71
92
  // Pattern matching for dynamic routes
72
- const settingsRoute = tree.byPath.get("/settings/profile");
93
+ const settingsRoute = findRouteByPath(tree, "/settings/profile");
73
94
  console.log(settingsRoute?.id); // "settings"
74
95
  ```
75
96
 
@@ -159,6 +180,17 @@ Bridge teardown must be explicit and deterministic:
159
180
  - `disconnect`/`dispose` must unwatch signal subscriptions and unhook router listeners.
160
181
  - Do not rely on GC-only cleanup guidance.
161
182
  - Infrastructure remains passive: bridges observe and forward intents, actors decide validity.
183
+ - `createBrowserHistory().destroy()` is idempotent and restores shared `window.history` patches only after the last wrapper for that window is removed.
184
+
185
+ ## Diagnostics
186
+
187
+ Router infrastructure reports runtime failures through `PlayDiagnostics` from `@xmachines/play`.
188
+
189
+ - Default behavior uses `consoleDiagnostics`.
190
+ - Messages follow a stable `[scope:code] message` format.
191
+ - Example: `[RouterBridgeBase:PLAY_ROUTER_SYNC_FAILED] Failed to sync actor state from router location.`
192
+
193
+ This keeps router errors observable while letting applications swap in their own diagnostics implementation upstream.
162
194
 
163
195
  ## API Reference
164
196
 
@@ -176,9 +208,10 @@ const tree = extractMachineRoutes(machine: AnyStateMachine): RouteTree;
176
208
 
177
209
  **Returns:** `RouteTree` with:
178
210
 
179
- - `routes: RouteNode[]` - Array of route nodes
180
- - `byPath: Map<string, RouteNode>` - URL path → route node
181
- - `byId: Map<string, RouteNode>` - State ID → route node
211
+ - `byPath: Map<string, RouteNode>` URL path route node
212
+ - `byStateId: Map<string, RouteNode>` State ID → route node
213
+ - `root: RouteNode` synthetic root node
214
+ - `graph: MachineGraph` — `@statelyai/graph` Graph for hierarchy queries, reachability checks, and transition-aware navigation
182
215
 
183
216
  **Throws:** Error if routes are invalid (malformed paths, missing state IDs, duplicates)
184
217
 
@@ -190,57 +223,85 @@ import { extractMachineRoutes } from "@xmachines/play-router";
190
223
  const tree = extractMachineRoutes(authMachine);
191
224
 
192
225
  // Query routes
193
- const loginRoute = tree.byId.get("login");
226
+ const loginRoute = tree.byStateId.get("login");
194
227
  console.log(loginRoute?.path); // "/login"
195
228
 
196
229
  const dashboardRoute = tree.byPath.get("/dashboard");
197
230
  console.log(dashboardRoute?.id); // "dashboard"
198
231
  ```
199
232
 
200
- ### crawlMachine()
233
+ ### machineToGraph()
201
234
 
202
- Low-level BFS traversal of state machine graph:
235
+ Converts an XState v5 machine to a typed `@statelyai/graph` `Graph`:
203
236
 
204
237
  ```typescript
205
- const visits = crawlMachine(machine: AnyStateMachine): StateVisit[];
206
- ```
238
+ import { machineToGraph } from "@xmachines/play-router";
239
+ import { getChildren, getSuccessors, hasPath } from "@statelyai/graph";
207
240
 
208
- **Returns:** Array of state visits in breadth-first order with:
241
+ const graph = machineToGraph(machine);
209
242
 
210
- - `path: string[]` - State path (e.g., ["dashboard", "settings"])
211
- - `parent: StateNode | null` - Parent state node
212
- - `node: StateNode` - Current state node
243
+ // Hierarchy queries
244
+ const children = getChildren(graph, "myMachine");
245
+ const successors = getSuccessors(graph, "myMachine.home"); // transition-reachable states
213
246
 
214
- **Example:**
247
+ // Reachability
248
+ const reachable = hasPath(graph, "myMachine.home", "myMachine.dashboard");
249
+ ```
250
+
251
+ The graph is also available on any `RouteTree` returned by `extractMachineRoutes()`:
215
252
 
216
253
  ```typescript
217
- import { crawlMachine } from "@xmachines/play-router";
254
+ const tree = extractMachineRoutes(machine);
218
255
 
219
- const visits = crawlMachine(machine);
220
- visits.forEach((visit) => {
221
- console.log("State:", visit.path.join("."));
222
- console.log("Parent:", visit.parent?.id ?? "root");
223
- });
256
+ // Use @statelyai/graph algorithms directly on the graph
257
+ const successors = getSuccessors(tree.graph!, "myMachine.home");
224
258
  ```
225
259
 
260
+ **Node data** (`MachineNodeData`):
261
+
262
+ - `stateId: string` — XState state ID (e.g., `"myMachine.dashboard.overview"`)
263
+ - `type` — `"atomic" | "compound" | "parallel" | "final" | "history"`
264
+ - `meta?: Record<string, unknown>` — original state meta object
265
+ - `route?: string` — extracted route path from `meta.route`
266
+
267
+ **Edge data** (`MachineEdgeData`):
268
+
269
+ - `eventType: string` — event type triggering this transition
270
+ - `guardType?: string` — guard name/description (if transition is guarded)
271
+
226
272
  ### Query Utilities
227
273
 
228
274
  ```typescript
229
- // Get child routes from state
230
- const children = getNavigableRoutes(tree, "dashboard");
275
+ import {
276
+ getNavigableRoutes,
277
+ getRoutableRoutes,
278
+ routeExists,
279
+ getTransitionReachableRoutes,
280
+ isRouteReachable,
281
+ } from "@xmachines/play-router";
282
+
283
+ // Child routes (hierarchical + transition-reachable)
284
+ const navigable = getNavigableRoutes(tree, "dashboard");
231
285
 
232
- // Check if route exists
286
+ // All routable routes as flat array
287
+ const allRoutes = getRoutableRoutes(tree);
288
+
289
+ // Check path exists
233
290
  const exists = routeExists(tree, "/profile/:userId");
234
- ```
235
291
 
236
- **Complete API:** See [API Documentation](../../docs/api/@xmachines/play-router)
292
+ // Transition-aware: which state IDs are reachable via transitions?
293
+ const reachableIds = getTransitionReachableRoutes(tree.graph!, "myMachine.home");
294
+
295
+ // Is there any path from state A to state B via transitions?
296
+ const canReach = isRouteReachable(tree.graph!, "myMachine.home", "myMachine.dashboard");
297
+ ```
237
298
 
238
299
  ## Examples
239
300
 
240
301
  ### Route Detection
241
302
 
242
303
  ```typescript
243
- import { extractMachineRoutes } from "@xmachines/play-router";
304
+ import { extractMachineRoutes, findRouteByPath } from "@xmachines/play-router";
244
305
  import { createMachine } from "xstate";
245
306
 
246
307
  const machine = createMachine({
@@ -264,10 +325,10 @@ const machine = createMachine({
264
325
  const tree = extractMachineRoutes(machine);
265
326
 
266
327
  // Bidirectional mapping
267
- const profileById = tree.byId.get("profile");
328
+ const profileById = tree.byStateId.get("profile");
268
329
  console.log(profileById?.path); // "/profile/:userId"
269
330
 
270
- const profileByPath = tree.byPath.get("/profile/user123");
331
+ const profileByPath = findRouteByPath(tree, "/profile/user123");
271
332
  console.log(profileByPath?.id); // "profile"
272
333
  ```
273
334
 
@@ -311,14 +372,14 @@ const dashboardChildren = getNavigableRoutes(tree, "dashboard");
311
372
  console.log(dashboardChildren.map((r) => r.id)); // ["overview", "analytics"]
312
373
 
313
374
  // Route inheritance
314
- const analyticsRoute = tree.byId.get("analytics");
315
- console.log(analyticsRoute?.path); // "/dashboard/analytics" (inherited parent path)
375
+ const analyticsRoute = tree.byStateId.get("analytics");
376
+ console.log(analyticsRoute?.fullPath); // "/analytics" (absolute route)
316
377
  ```
317
378
 
318
379
  ### Pattern Matching
319
380
 
320
381
  ```typescript
321
- import { extractMachineRoutes } from "@xmachines/play-router";
382
+ import { extractMachineRoutes, findRouteByPath } from "@xmachines/play-router";
322
383
 
323
384
  const machine = createMachine({
324
385
  states: {
@@ -336,13 +397,13 @@ const machine = createMachine({
336
397
  const tree = extractMachineRoutes(machine);
337
398
 
338
399
  // Pattern matching for actual URLs
339
- const userRoute = tree.byPath.get("/user/user123");
400
+ const userRoute = findRouteByPath(tree, "/user/user123");
340
401
  console.log(userRoute?.id); // "user"
341
402
 
342
- const settingsDefault = tree.byPath.get("/settings");
403
+ const settingsDefault = findRouteByPath(tree, "/settings");
343
404
  console.log(settingsDefault?.id); // "settings" (optional param)
344
405
 
345
- const settingsProfile = tree.byPath.get("/settings/profile");
406
+ const settingsProfile = findRouteByPath(tree, "/settings/profile");
346
407
  console.log(settingsProfile?.id); // "settings" (with param)
347
408
  ```
348
409
 
@@ -388,7 +449,7 @@ states: {
388
449
  },
389
450
  relative: {
390
451
  id: "relative",
391
- meta: { route: "relative", view: { component: "Relative" } }, // No leading / → inherits parent
452
+ meta: { route: "relative", view: { component: "Relative" } }, // No leading / → inherits parent path prefix
392
453
  // Final path: "/parent/relative"
393
454
  },
394
455
  },
@@ -409,20 +470,78 @@ meta: {
409
470
 
410
471
  **Parameter substitution:** Values extracted from context or event params (handled by play-xstate adapter).
411
472
 
473
+ ## Security Utilities
474
+
475
+ ### `sanitizePathname()`
476
+
477
+ Normalize a raw pathname before route-map lookup. Used internally by `RouterBridgeBase.syncActorFromRouter()` and available to adapters that bypass the base class:
478
+
479
+ ```typescript
480
+ import { sanitizePathname } from "@xmachines/play-router";
481
+
482
+ // In a custom adapter's route-watch callback:
483
+ const clean = sanitizePathname(rawPath);
484
+ if (clean === null) return; // Reject malformed/oversized paths (> 2048 chars)
485
+ // Proceed with clean normalized path
486
+ ```
487
+
488
+ Returns `null` for paths exceeding 2048 characters. Strips query fragments, hash fragments, and collapses duplicate slashes.
489
+
490
+ ---
491
+
492
+ ## Error Handling
493
+
494
+ All runtime errors thrown by this package extend `PlayError` from `@xmachines/play` and
495
+ are exported from the `./errors` subpath:
496
+
497
+ ```typescript
498
+ import {
499
+ RouterSyncError,
500
+ URLPatternUnavailableError,
501
+ InvalidRoutePatternError,
502
+ } from "@xmachines/play-router/errors";
503
+ ```
504
+
505
+ | Class | Code | When thrown |
506
+ | ---------------------------- | --------------------------------------- | -------------------------------------------------------------- |
507
+ | `RouterSyncError` | `PLAY_ROUTER_SYNC_FAILED` | `syncActorFromRouter()` fails to send a `play.route` event |
508
+ | `URLPatternUnavailableError` | `PLAY_ROUTE_MAP_URLPATTERN_UNAVAILABLE` | `createRouteMap()` called before URLPattern polyfill is loaded |
509
+ | `InvalidRoutePatternError` | `PLAY_ROUTE_MAP_INVALID_PATTERN` | A route pattern string is rejected by URLPattern constructor |
510
+
511
+ `InvalidRoutePatternError` carries a `pattern: string` field with the offending pattern.
512
+
513
+ ```typescript
514
+ import { PlayError } from "@xmachines/play";
515
+ import { RouterSyncError } from "@xmachines/play-router/errors";
516
+
517
+ try {
518
+ bridge.connect();
519
+ } catch (err) {
520
+ if (err instanceof RouterSyncError) {
521
+ // err.cause — the original error that triggered the sync failure
522
+ monitoringService.record(err);
523
+ } else if (err instanceof PlayError) {
524
+ console.error(`[${err.scope}:${err.code}] ${err.message}`);
525
+ }
526
+ }
527
+ ```
528
+
412
529
  ## Architecture
413
530
 
414
531
  This package enables **Actor Authority (INV-01)**:
415
532
 
416
533
  1. **Routes derive from machine:** Business logic defines routes in state machine, not external config
417
- 2. **BFS traversal:** Systematic state discovery ensures all nested states visited
418
- 3. **Bidirectional mapping:** Fast lookup by path (browser URL) or by ID (state machine)
419
- 4. **Build-time validation:** Invalid routes throw errors during extraction, not runtime
534
+ 2. **Graph-based extraction:** `machineToGraph()` converts machines to typed `@statelyai/graph` `Graph` objects with hierarchy, transition edges, and route metadata
535
+ 3. **Bidirectional mapping:** Fast lookup by path (browser URL) or by state ID (state machine)
536
+ 4. **Transition-aware queries:** `getTransitionReachableRoutes()` and `isRouteReachable()` use graph algorithms for navigation reachability
537
+ 5. **Build-time validation:** Invalid routes throw errors during extraction, not runtime
420
538
 
421
539
  **Enhancements:**
422
540
 
423
- - `meta.route` detection via state metadata
541
+ - `meta.route` detection via state metadata — extracted into typed graph node data
424
542
  - Pattern matching for dynamic routes (`:param` and `:param?`)
425
543
  - State ID ↔ path bidirectional maps for `play.route` events
544
+ - `@statelyai/graph` integration — hierarchy queries, reachability checks, transition traversal
426
545
 
427
546
  ## Related Packages
428
547
 
@@ -433,4 +552,7 @@ This package enables **Actor Authority (INV-01)**:
433
552
 
434
553
  ## License
435
554
 
436
- MIT
555
+ Copyright (c) 2016 [Mikael Karon](mailto:mikael@karon.se). All rights reserved.
556
+
557
+ This work is licensed under the terms of the MIT license.
558
+ For a copy, see <https://opensource.org/licenses/MIT>.
@@ -0,0 +1,116 @@
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
+ * Uses URLPattern for parameterized route matching (same engine as createRouteMap).
10
+ */
11
+ /**
12
+ * A single state ID ↔ path mapping entry.
13
+ *
14
+ * Both fields are `readonly` — mappings are immutable once passed to `BaseRouteMap`.
15
+ * Adapter packages re-export a structurally compatible `RouteMapping` type under
16
+ * their own name. This type is published from `@xmachines/play-router` as
17
+ * `BaseRouteMapping` to avoid name collisions with those adapter-local types.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const mapping: BaseRouteMapping = { stateId: "home", path: "/" };
22
+ * const paramMapping: BaseRouteMapping = { stateId: "profile", path: "/profile/:userId" };
23
+ * const optionalMapping: BaseRouteMapping = { stateId: "settings", path: "/settings/:section?" };
24
+ * ```
25
+ */
26
+ export interface RouteMapping {
27
+ /** State machine state ID (e.g., `"home"`, `"#profile"`) */
28
+ readonly stateId: string;
29
+ /** URL path pattern (e.g., `"/"`, `"/profile/:userId"`, `"/settings/:section?"`) */
30
+ readonly path: string;
31
+ }
32
+ /**
33
+ * Shared bidirectional route map base class.
34
+ *
35
+ * All framework adapter `RouteMap` classes extend this — they add no logic of their
36
+ * own and inherit the full public API from here.
37
+ *
38
+ * **Lookup strategy:**
39
+ * - Static paths (no `:param`) → O(1) `Map` lookup
40
+ * - Dynamic paths → O(k) bucket-indexed scan using `URLPattern`, where `k` is the number
41
+ * of routes sharing the same first path segment
42
+ * - Results are cached after the first match
43
+ *
44
+ * **Pattern syntax** (`:param` / `:param?`):
45
+ * - `:param` — required segment, matches exactly one non-`/` segment
46
+ * - `:param?` — optional segment, matches zero or one non-`/` segment
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * import { BaseRouteMap } from "@xmachines/play-router";
51
+ *
52
+ * const map = new BaseRouteMap([
53
+ * { stateId: "home", path: "/" },
54
+ * { stateId: "profile", path: "/profile/:userId" },
55
+ * { stateId: "settings", path: "/settings/:section?" },
56
+ * ]);
57
+ *
58
+ * map.getStateIdByPath("/"); // "home"
59
+ * map.getStateIdByPath("/profile/123"); // "profile"
60
+ * map.getStateIdByPath("/settings"); // "settings"
61
+ * map.getStateIdByPath("/unknown"); // null
62
+ *
63
+ * map.getPathByStateId("profile"); // "/profile/:userId"
64
+ * map.getPathByStateId("missing"); // null
65
+ * ```
66
+ */
67
+ export declare class BaseRouteMap {
68
+ private stateIdToPath;
69
+ private pathToStateId;
70
+ private patternBuckets;
71
+ private pathMatchCache;
72
+ /**
73
+ * Build a route map from an array of state ID ↔ path mappings.
74
+ *
75
+ * Static paths (no `:param`) are indexed in an O(1) `Map`.
76
+ * Parameterized paths are compiled to `URLPattern` and grouped into first-segment
77
+ * buckets for efficient candidate selection.
78
+ *
79
+ * @param mappings - Array of `{ stateId, path }` entries. Order determines
80
+ * priority when multiple patterns could match the same path.
81
+ */
82
+ constructor(mappings: RouteMapping[]);
83
+ /**
84
+ * Resolve a URL path to its mapped state ID.
85
+ *
86
+ * Strips query strings and hash fragments before matching. Tries an O(1) exact
87
+ * lookup first, then falls back to bucket-indexed pattern matching. Results are
88
+ * cached after the first pattern match.
89
+ *
90
+ * @param path - URL pathname, optionally including query/hash (e.g., `"/profile/123?ref=nav"`)
91
+ * @returns The mapped state ID, or `null` if no route matches
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * map.getStateIdByPath("/profile/123"); // "profile"
96
+ * map.getStateIdByPath("/unknown"); // null
97
+ * ```
98
+ */
99
+ getStateIdByPath(path: string): string | null;
100
+ /**
101
+ * Look up the path pattern registered for a state ID.
102
+ *
103
+ * @param stateId - State machine state ID (e.g., `"profile"`, `"#settings"`)
104
+ * @returns The registered path pattern, or `null` if the state ID is unknown
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * map.getPathByStateId("profile"); // "/profile/:userId"
109
+ * map.getPathByStateId("missing"); // null
110
+ * ```
111
+ */
112
+ getPathByStateId(stateId: string): string | null;
113
+ private getIndexKey;
114
+ private getCandidates;
115
+ }
116
+ //# 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;;;;;;;;;GASG;AAuCH;;;;;;;;;;;;;;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,CAGpB;IACF,OAAO,CAAC,cAAc,CAAkC;IAExD;;;;;;;;;OASG;gBACS,QAAQ,EAAE,YAAY,EAAE;IAkCpC;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAuB7C;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIhD,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,aAAa;CA8BrB"}