@xmachines/play-router 2.0.0 → 2.1.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 (58) hide show
  1. package/README.md +93 -76
  2. package/dist/base-route-map.d.ts +63 -57
  3. package/dist/base-route-map.d.ts.map +1 -1
  4. package/dist/base-route-map.js +65 -59
  5. package/dist/base-route-map.js.map +1 -1
  6. package/dist/build-tree.d.ts +13 -12
  7. package/dist/build-tree.d.ts.map +1 -1
  8. package/dist/build-tree.js +30 -28
  9. package/dist/build-tree.js.map +1 -1
  10. package/dist/create-route-map-from-tree.d.ts +15 -15
  11. package/dist/create-route-map-from-tree.js +15 -15
  12. package/dist/create-route-map.d.ts +18 -16
  13. package/dist/create-route-map.d.ts.map +1 -1
  14. package/dist/create-route-map.js +10 -9
  15. package/dist/create-route-map.js.map +1 -1
  16. package/dist/errors.d.ts +40 -38
  17. package/dist/errors.d.ts.map +1 -1
  18. package/dist/errors.js +40 -38
  19. package/dist/errors.js.map +1 -1
  20. package/dist/extract-routes.d.ts +8 -7
  21. package/dist/extract-routes.d.ts.map +1 -1
  22. package/dist/extract-routes.js +31 -27
  23. package/dist/extract-routes.js.map +1 -1
  24. package/dist/find-route.d.ts +18 -15
  25. package/dist/find-route.d.ts.map +1 -1
  26. package/dist/find-route.js +42 -38
  27. package/dist/find-route.js.map +1 -1
  28. package/dist/index.d.ts +6 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +11 -10
  31. package/dist/index.js.map +1 -1
  32. package/dist/machine-to-graph.d.ts +3 -2
  33. package/dist/machine-to-graph.d.ts.map +1 -1
  34. package/dist/machine-to-graph.js +20 -19
  35. package/dist/machine-to-graph.js.map +1 -1
  36. package/dist/query.d.ts +39 -37
  37. package/dist/query.d.ts.map +1 -1
  38. package/dist/query.js +62 -57
  39. package/dist/query.js.map +1 -1
  40. package/dist/router-bridge-base.d.ts +208 -190
  41. package/dist/router-bridge-base.d.ts.map +1 -1
  42. package/dist/router-bridge-base.js +235 -211
  43. package/dist/router-bridge-base.js.map +1 -1
  44. package/dist/router-sync.d.ts +41 -35
  45. package/dist/router-sync.d.ts.map +1 -1
  46. package/dist/router-sync.js +53 -45
  47. package/dist/router-sync.js.map +1 -1
  48. package/dist/types.d.ts +165 -147
  49. package/dist/types.d.ts.map +1 -1
  50. package/dist/url-pattern-utils.d.ts +53 -47
  51. package/dist/url-pattern-utils.d.ts.map +1 -1
  52. package/dist/url-pattern-utils.js +61 -55
  53. package/dist/url-pattern-utils.js.map +1 -1
  54. package/dist/validate-routes.d.ts +32 -31
  55. package/dist/validate-routes.d.ts.map +1 -1
  56. package/dist/validate-routes.js +30 -29
  57. package/dist/validate-routes.js.map +1 -1
  58. package/package.json +6 -5
package/README.md CHANGED
@@ -2,11 +2,9 @@
2
2
 
3
3
  Route tree extraction from XState v5 state machines. Part of [@xmachines/play](../play/README.md) Universal Player Architecture.
4
4
 
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Version](https://img.shields.io/badge/version-2.0.0-blue)](https://www.npmjs.com/package/@xmachines/play-router)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Version](https://img.shields.io/badge/version-2.1.0-blue)](https://www.npmjs.com/package/@xmachines/play-router)
6
6
 
7
- Graph-based route extraction and bidirectional lookup enabling Actor Authority over navigation.
8
-
9
- Part of the [xmachines-js monorepo](../../README.md).
7
+ This package extracts the routes from a machine graph and looks them up in both directions. The Actor therefore keeps the authority over the navigation.
10
8
 
11
9
  ## Installation
12
10
 
@@ -21,9 +19,9 @@ pnpm add @xmachines/play-router
21
19
 
22
20
  **URLPattern polyfill (Node.js < 24 / older browsers):**
23
21
 
24
- `@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+).
22
+ `@xmachines/play-router` matches each dynamic route with the [URLPattern API](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern). URLPattern is native in Node.js 24+ and in a modern browser (Chrome 95+, Firefox 117+, Safari 16.4+).
25
23
 
26
- On environments without native support, load a polyfill **before** importing this package:
24
+ In an environment without the native API, load a polyfill **before** you import this package:
27
25
 
28
26
  ```typescript
29
27
  // Entry point — must run before any @xmachines/play-router import
@@ -36,7 +34,7 @@ Install the polyfill:
36
34
  pnpm add urlpattern-polyfill
37
35
  ```
38
36
 
39
- `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.
37
+ `urlpattern-polyfill` is an optional peer dependency. A package manager does not install it for you. Install it and load it yourself when your runtime has no native URLPattern.
40
38
 
41
39
  ## Usage
42
40
 
@@ -95,8 +93,13 @@ routeMap.getPathByStateId("profile"); // "/profile/:userId"
95
93
  ### Sending `play.route` events
96
94
 
97
95
  ```typescript
96
+ import { definePlayer } from "@xmachines/play-xstate";
98
97
  import type { PlayRouteEvent } from "@xmachines/play-router";
99
98
 
99
+ // machine: your routable machine (states carry meta.route)
100
+ const actor = definePlayer({ machine })();
101
+ actor.start();
102
+
100
103
  // Navigate to a state by ID
101
104
  const event: PlayRouteEvent = {
102
105
  type: "play.route",
@@ -120,14 +123,21 @@ actor.send({
120
123
  });
121
124
  ```
122
125
 
123
- ### Implementing a `RouterBridgeBase` adapter
126
+ ### How to write a `RouterBridgeBase` adapter
124
127
 
125
- Extend `RouterBridgeBase` and implement the three abstract methods for your framework:
128
+ Extend `RouterBridgeBase`, then implement the three abstract methods for your framework:
126
129
 
127
130
  ```typescript
128
- import { RouterBridgeBase } from "@xmachines/play-router";
131
+ import { RouterBridgeBase, createRouteMap } from "@xmachines/play-router";
129
132
  import type { RoutableActor } from "@xmachines/play-router";
130
133
 
134
+ // Shape of your framework's router — adjust to its real API
135
+ type MyRouter = {
136
+ navigate(path: string): void;
137
+ subscribe(handler: (location: { pathname: string; search: string }) => void): () => void;
138
+ state: { location: { pathname: string } };
139
+ };
140
+
131
141
  export class MyRouterBridge extends RouterBridgeBase {
132
142
  private unsubscribe: (() => void) | null = null;
133
143
 
@@ -163,7 +173,8 @@ export class MyRouterBridge extends RouterBridgeBase {
163
173
  }
164
174
  }
165
175
 
166
- // Usage
176
+ // Usage — myRouter: your framework's router instance;
177
+ // machine/actor: your routable machine and its started actor
167
178
  const routeMap = createRouteMap(machine);
168
179
  const bridge = new MyRouterBridge(myRouter, actor, routeMap);
169
180
  bridge.connect();
@@ -175,79 +186,79 @@ bridge.disconnect();
175
186
 
176
187
  ### Route Extraction
177
188
 
178
- | Export | Description |
179
- | ---------------------------------------- | ---------------------------------------------------------------------------------- |
180
- | `extractMachineRoutes(machine)` | Convert an XState machine to a `RouteTree` with bidirectional state ID ↔ path maps |
181
- | `createRouteMap(machine, options?)` | Build a `RouteMap` directly from a machine (preferred form for adapters) |
182
- | `createRouteMapFromTree(tree, options?)` | Build a `RouteMap` from an already-extracted `RouteTree` |
183
- | `buildRouteTree(routes)` | Build a `RouteTree` from an array of `RouteInfo` objects |
184
- | `machineToGraph(machine)` | Convert a machine to a typed `@statelyai/graph` `Graph` for graph algorithm access |
189
+ | Export | Description |
190
+ | ---------------------------------------- | --------------------------------------------------------------------------------- |
191
+ | `extractMachineRoutes(machine)` | Converts an XState machine into a `RouteTree` with the state ID ↔ path maps |
192
+ | `createRouteMap(machine, options?)` | Builds a `RouteMap` directly from a machine. An adapter uses this form |
193
+ | `createRouteMapFromTree(tree, options?)` | Builds a `RouteMap` from a `RouteTree` that you extracted before |
194
+ | `buildRouteTree(routes)` | Builds a `RouteTree` from an array of `RouteInfo` objects |
195
+ | `machineToGraph(machine)` | Converts a machine into a typed `@statelyai/graph` `Graph`, for a graph algorithm |
185
196
 
186
197
  ### Route Matching
187
198
 
188
- | Export | Description |
189
- | ----------------------------- | ------------------------------------------------------------------------------------------ |
190
- | `RouteMap` | Bidirectional `stateId ↔ path` lookup class; supports O(1) exact and O(k) pattern matching |
191
- | `findRouteById(tree, id)` | Look up a `RouteNode` by state ID |
192
- | `findRouteByPath(tree, path)` | Look up a `RouteNode` by URL path (supports dynamic patterns) |
199
+ | Export | Description |
200
+ | ----------------------------- | ------------------------------------------------------------------------------------------------------------- |
201
+ | `RouteMap` | The `stateId ↔ path` lookup class for both directions. It matches an exact path in O(1) and a pattern in O(k) |
202
+ | `findRouteById(tree, id)` | Finds a `RouteNode` by its state ID |
203
+ | `findRouteByPath(tree, path)` | Finds a `RouteNode` by its URL path. It also matches a dynamic pattern |
193
204
 
194
205
  ### Query Utilities
195
206
 
196
- | Export | Description |
197
- | ------------------------------------------------- | ------------------------------------------------------------------------- |
198
- | `getRoutableRoutes(tree)` | All routable `RouteNode`s as a flat array |
199
- | `getNavigableRoutes(tree, stateId)` | Child routes reachable from a state (hierarchical + transition-reachable) |
200
- | `routeExists(tree, path)` | Check whether a path is registered in the tree |
201
- | `getTransitionReachableRoutes(graph, stateId)` | Route paths reachable via XState transitions from a state |
202
- | `isRouteReachable(graph, fromStateId, toStateId)` | Check whether a transition path exists between two states |
207
+ | Export | Description |
208
+ | ------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
209
+ | `getRoutableRoutes(tree)` | Returns every routable `RouteNode` in one flat array |
210
+ | `getNavigableRoutes(tree, stateId)` | Returns the child routes that a state can reach, through the hierarchy and through a transition |
211
+ | `routeExists(tree, path)` | Tells you if the tree holds a path |
212
+ | `getTransitionReachableRoutes(graph, stateId)` | Returns the route paths that a state can reach through an XState transition |
213
+ | `isRouteReachable(graph, fromStateId, toStateId)` | Tells you if a transition path is present between two states |
203
214
 
204
215
  ### Router Bridge
205
216
 
206
- | Export | Description |
207
- | --------------------------------------- | ------------------------------------------------------------------------------------- |
208
- | `RouterBridgeBase` | Abstract base class for framework router adapters; implements `RouterBridge` protocol |
209
- | `sanitizePathname(path)` | Normalize a raw pathname; returns `null` for paths > 2048 chars or malformed input |
210
- | `buildPlayRouteEvent(options)` | Build a `PlayRouteEvent` from a pathname + route-map match result |
211
- | `extractRouteParams(pathname, pattern)` | Extract path parameters from a URL using URLPattern |
212
- | `extractQuery(search)` | Extract query parameters from a URL search string |
217
+ | Export | Description |
218
+ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
219
+ | `RouterBridgeBase` | The abstract base class of each framework router adapter. It implements the `RouterBridge` protocol |
220
+ | `sanitizePathname(path)` | Normalizes a raw pathname. It returns `null` for a path of more than 2048 characters, and for malformed input |
221
+ | `buildPlayRouteEvent(options)` | Builds a `PlayRouteEvent` from a pathname and a route-map match result |
222
+ | `extractRouteParams(pathname, pattern)` | Reads the path parameters of a URL with URLPattern |
223
+ | `extractQuery(search)` | Reads the query parameters of a URL search string |
213
224
 
214
225
  ### Validation
215
226
 
216
- | Export | Description |
217
- | ---------------------------------------- | ----------------------------------------------------- |
218
- | `validateRouteFormat(route, stateId)` | Assert route path is non-empty |
219
- | `validateStateExists(stateId, stateIds)` | Assert a state ID is present in the machine graph |
220
- | `detectDuplicateRoutes(routes)` | Throw if any two states resolve to the same full path |
227
+ | Export | Description |
228
+ | ---------------------------------------- | ---------------------------------------------------- |
229
+ | `validateRouteFormat(route, stateId)` | Asserts that the route path is not empty |
230
+ | `validateStateExists(stateId, stateIds)` | Asserts that the machine graph holds the state ID |
231
+ | `detectDuplicateRoutes(routes)` | Throws when two states resolve to the same full path |
221
232
 
222
233
  ### Key Types
223
234
 
224
- | Export | Description |
225
- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
226
- | `RouterBridge` | Interface for `connect()` / `disconnect()` lifecycle |
227
- | `RouteTree` | Hierarchical tree with `root`, `byStateId`, `byPath`, and optional `graph` |
228
- | `RouteNode` | Single node in the tree with `id`, `path`, `fullPath`, `stateId`, `children`, `parent` |
229
- | `RouteInfo` | Flat route descriptor extracted from a state node |
230
- | `PlayRouteEvent` | Routing event `{ type: "play.route", to, params?, query? }` |
231
- | `RoutableActor` | Minimal actor interface required by `RouterBridgeBase` `currentRoute`, `initialRoute`, and `send(PlayRouteEvent)` |
232
- | `PlayActor` | Full actor interface used by `PlayRouterProvider` extends `RoutableActor` with `currentView` (`Routable + Viewable`) |
233
- | `RouteMapping` | `{ stateId, path }` pair used to build a `RouteMap` |
234
- | `RouteMapping as BaseRouteMapping` | Alias for `RouteMapping` (backwards-compat re-export) |
235
- | `MachineGraph` | Typed `@statelyai/graph` Graph with `MachineNodeData` / `MachineEdgeData` |
236
- | `WindowLike` | Injectable minimal `window` interface for SSR / testing |
237
- | `LocationLike` | Injectable minimal `location` interface for SSR / testing |
235
+ | Export | Description |
236
+ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
237
+ | `RouterBridge` | The interface of the `connect()` and `disconnect()` lifecycle |
238
+ | `RouteTree` | The hierarchical tree, with `root`, `byStateId`, `byPath`, and an optional `graph` |
239
+ | `RouteNode` | One node of the tree, with `id`, `path`, `fullPath`, `stateId`, `children`, and `parent` |
240
+ | `RouteInfo` | The flat route descriptor that comes from a state node |
241
+ | `PlayRouteEvent` | Routing event `{ type: "play.route", to, params?, query? }` |
242
+ | `RoutableActor` | The minimal actor interface that `RouterBridgeBase` requires: `currentRoute`, `initialRoute`, and `send(PlayRouteEvent)` |
243
+ | `PlayActor` | The complete actor interface that `PlayRouterProvider` uses. It extends `RoutableActor` with `currentView` (`Routable + Viewable`) |
244
+ | `RouteMapping` | The `{ stateId, path }` pair that builds a `RouteMap` |
245
+ | `RouteMapping as BaseRouteMapping` | The alias of `RouteMapping`, for compatibility with an earlier version |
246
+ | `MachineGraph` | The typed `@statelyai/graph` Graph, with `MachineNodeData` and `MachineEdgeData` |
247
+ | `WindowLike` | The minimal `window` interface that you can inject for SSR and for a test |
248
+ | `LocationLike` | The minimal `location` interface that you can inject for SSR and for a test |
238
249
 
239
250
  ### Errors (subpath `@xmachines/play-router/errors`)
240
251
 
241
- | Class | Code | When thrown |
242
- | ---------------------------- | --------------------------------------- | ----------------------------------------------------------------- |
243
- | `RouterSyncError` | `PLAY_ROUTER_SYNC_FAILED` | `syncActorFromRouter()` fails to send a `play.route` event |
244
- | `DuplicateBridgeError` | `PLAY_ROUTER_DUPLICATE_BRIDGE` | A second bridge tries to connect to an actor that already has one |
245
- | `URLPatternUnavailableError` | `PLAY_ROUTE_MAP_URLPATTERN_UNAVAILABLE` | URLPattern API is absent and no polyfill is loaded |
246
- | `InvalidRoutePatternError` | `PLAY_ROUTE_MAP_INVALID_PATTERN` | A route pattern string is rejected by the URLPattern constructor |
247
- | `EmptyRoutePathError` | `PLAY_ROUTE_EMPTY_PATH` | A state declares `meta.route: ""` |
248
- | `InvalidStateIdError` | `PLAY_ROUTE_INVALID_STATE_ID` | A route references a state ID not in the machine graph |
249
- | `DuplicateRoutePathError` | `PLAY_ROUTE_DUPLICATE_PATH` | Two or more states share the same URL path |
250
- | `UnknownStateTypeError` | `PLAY_ROUTE_UNKNOWN_STATE_TYPE` | A state node has an unrecognised XState `.type` value |
252
+ | Class | Code | When thrown |
253
+ | ---------------------------- | --------------------------------------- | ----------------------------------------------------------------------- |
254
+ | `RouterSyncError` | `PLAY_ROUTER_SYNC_FAILED` | `syncActorFromRouter()` cannot send a `play.route` event |
255
+ | `DuplicateBridgeError` | `PLAY_ROUTER_DUPLICATE_BRIDGE` | A second bridge tries to connect to an actor that already has one |
256
+ | `URLPatternUnavailableError` | `PLAY_ROUTE_MAP_URLPATTERN_UNAVAILABLE` | The URLPattern API is absent, and no polyfill is loaded |
257
+ | `InvalidRoutePatternError` | `PLAY_ROUTE_MAP_INVALID_PATTERN` | The URLPattern constructor refuses a route pattern string |
258
+ | `EmptyRoutePathError` | `PLAY_ROUTE_EMPTY_PATH` | A state declares `meta.route: ""` |
259
+ | `InvalidStateIdError` | `PLAY_ROUTE_INVALID_STATE_ID` | A route names a state ID that the machine graph does not hold |
260
+ | `DuplicateRoutePathError` | `PLAY_ROUTE_DUPLICATE_PATH` | Two or more states share the same URL path |
261
+ | `UnknownStateTypeError` | `PLAY_ROUTE_UNKNOWN_STATE_TYPE` | A state node has an XState `.type` value that the package does not know |
251
262
 
252
263
  ```typescript
253
264
  import {
@@ -256,6 +267,7 @@ import {
256
267
  URLPatternUnavailableError,
257
268
  } from "@xmachines/play-router/errors";
258
269
 
270
+ // bridge from the adapter example above
259
271
  try {
260
272
  bridge.connect();
261
273
  } catch (err) {
@@ -271,7 +283,7 @@ try {
271
283
 
272
284
  ### `meta.route` patterns
273
285
 
274
- Routes are declared on XState state nodes via the `meta.route` field:
286
+ Declare the route of an XState state node in its `meta.route` field:
275
287
 
276
288
  ```typescript
277
289
  states: {
@@ -296,7 +308,7 @@ states: {
296
308
 
297
309
  ### Relative vs absolute paths
298
310
 
299
- Child routes with a leading `/` are absolute (do not inherit the parent path). Without a leading `/`, they resolve relative to their nearest routable ancestor:
311
+ A child route that starts with `/` is absolute, and it does not inherit the path of its parent. A child route without the first `/` is relative to its nearest routable ancestor:
300
312
 
301
313
  ```typescript
302
314
  states: {
@@ -317,7 +329,7 @@ states: {
317
329
  }
318
330
  ```
319
331
 
320
- Always use `node.fullPath` (never `node.path`) for browser URL matching and route map construction.
332
+ Always use `node.fullPath` to match a browser URL and to build a route map. Never use `node.path` for this.
321
333
 
322
334
  ## Testing
323
335
 
@@ -329,10 +341,11 @@ pnpm --filter @xmachines/play-router test
329
341
  pnpm --filter @xmachines/play-router run test:watch
330
342
  ```
331
343
 
332
- A router bridge contract test suite for adapter authors lives in
333
- `@xmachines/play-router-shared` (it drives a real actor, so it sits one layer
334
- above this package to keep `@xmachines/play-router` free of an actor-runtime
335
- dependency):
344
+ `@xmachines/play-router-shared` holds a contract test suite of the router bridge, for the
345
+ author of an adapter. That suite drives a real actor. Therefore it is one layer above this
346
+ package, and `@xmachines/play-router` keeps no dependency on an actor runtime.
347
+ `@xmachines/play-router-shared` is a private workspace package. Thus only an adapter author
348
+ in this repository can use the suite:
336
349
 
337
350
  ```typescript
338
351
  import { runBridgeContractTests } from "@xmachines/play-router-shared/test/router-bridge-contract.js";
@@ -342,15 +355,19 @@ runBridgeContractTests({
342
355
  createHarness(initialPath) {
343
356
  // return ContractHarness with bridge, actor, simulateNavigation, getLastNavigatedPath
344
357
  },
358
+ createRestoredHarness(routedPath) {
359
+ // return ContractHarness whose actor is restored to routedPath
360
+ // while the mock router starts at the machine's initial route
361
+ },
345
362
  });
346
363
  ```
347
364
 
348
365
  ## Related Packages
349
366
 
350
367
  - **[@xmachines/play](../play/README.md)** — Core protocol types (`PlayEvent`, `PlayError`)
351
- - **[@xmachines/play-actor](../play-actor/README.md)** — Abstract actor base class (`AbstractActor`, `Routable`); all `AbstractActor` subclasses satisfy `RoutableActor` structurally
352
- - **[@xmachines/play-signals](../play-signals/README.md)** — TC39 Signals polyfill used for actor route observation
353
- - **[@xmachines/play-xstate](../play-xstate/README.md)** — XState v5 logic adapter that integrates with route trees
368
+ - **[@xmachines/play-actor](../play-actor/README.md)** — the abstract actor base class (`AbstractActor`, `Routable`). Every `AbstractActor` subclass satisfies `RoutableActor` structurally
369
+ - **[@xmachines/play-signals](../play-signals/README.md)** — the TC39 Signals polyfill that observes the actor route
370
+ - **[@xmachines/play-xstate](../play-xstate/README.md)** — the XState v5 logic adapter, which works with a route tree
354
371
  - **[@xmachines/play-tanstack-router](../play-tanstack-router/README.md)** — Shared TanStack Router bridge base (framework-agnostic)
355
372
  - **[@xmachines/play-tanstack-react-router](../play-tanstack-react-router/README.md)** — TanStack Router adapter (React)
356
373
  - **[@xmachines/play-tanstack-solid-router](../play-tanstack-solid-router/README.md)** — TanStack Router adapter (SolidJS)
@@ -1,20 +1,23 @@
1
1
  /**
2
- * RouteMap — Shared bidirectional route mapping base class
2
+ * RouteMap — the shared base class of the route map for both directions
3
3
  *
4
- * Provides bucket-based pattern matching shared across all framework adapters.
5
- * Adapters extend this class rather than duplicating the pattern-match logic.
4
+ * The class gives you the pattern match on buckets, and every framework adapter
5
+ * uses it. An adapter extends this class, and it therefore holds no copy of the
6
+ * pattern match logic.
6
7
  *
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.
8
+ * The algorithm: an exact match in O(1) through a Map, then a pattern match on the
9
+ * buckets in O(k), where k is the number of the routes in the bucket of the first
10
+ * segment, and that number is much smaller than the number of all the routes. The
11
+ * class matches a parameterized route with URLPattern.
10
12
  */
11
13
  /**
12
- * A single state ID path mapping entry.
14
+ * One entry of the map between a state ID and a path.
13
15
  *
14
- * Both fields are `readonly` mappings are immutable once passed to `RouteMap`.
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
- * `RouteMapping` to avoid name collisions with those adapter-local types.
16
+ * Both fields are `readonly`, because a mapping is immutable after the caller gives
17
+ * it to `RouteMap`. An adapter package re-exports a structurally compatible
18
+ * `RouteMapping` type under its own name. `@xmachines/play-router` publishes this
19
+ * type as `RouteMapping`, and its name therefore does not collide with such a local
20
+ * type of an adapter.
18
21
  *
19
22
  * @example
20
23
  * ```typescript
@@ -24,34 +27,35 @@
24
27
  * ```
25
28
  */
26
29
  export interface RouteMapping {
27
- /** State machine state ID (e.g., `"home"`, `"#profile"`) */
30
+ /** The state ID of the state machine, for example `"home"` or `"#profile"` */
28
31
  readonly stateId: string;
29
- /** URL path pattern (e.g., `"/"`, `"/profile/:userId"`, `"/settings/:section?"`) */
32
+ /** The pattern of the URL path, for example `"/"`, `"/profile/:userId"`, or `"/settings/:section?"` */
30
33
  readonly path: string;
31
34
  }
32
35
  /**
33
- * Shared bidirectional route map base class.
36
+ * The shared base class of the route map for both directions.
34
37
  *
35
- * All framework adapters use this class as their route map they add no logic of their
36
- * own and inherit the full public API from here.
38
+ * Every framework adapter uses this class as its route map. An adapter adds no logic
39
+ * of its own, and it inherits the complete public API from here.
37
40
  *
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 in an LRU cache (default 500 entries,
43
- * configurable via the `cacheSize` constructor option)
41
+ * **The strategy of a lookup:**
42
+ * - A static path, without a `:param` → a `Map` lookup in O(1)
43
+ * - A dynamic path a scan of the bucket index in O(k), with `URLPattern`, where
44
+ * `k` is the number of the routes with the same first path segment
45
+ * - The class keeps each result of a first match in an LRU cache. The default size
46
+ * is 500 entries, and the `cacheSize` constructor option changes it
44
47
  *
45
- * **Pattern syntax** (`:param` / `:param?` / `*`):
46
- * - `:param` — required segment, matches exactly one non-`/` segment
47
- * - `:param?` — optional segment, matches zero or one non-`/` segment
48
- * - `*` — wildcard, matches any number of segments (URLPattern semantics)
48
+ * **The syntax of a pattern** (`:param`, `:param?`, and `*`):
49
+ * - `:param` — a necessary segment. It matches exactly one segment without a `/`
50
+ * - `:param?` — an optional segment. It matches zero segments or one segment without a `/`
51
+ * - `*` — a wildcard. It matches each number of segments, as URLPattern defines
49
52
  *
50
- * **StateId forms:** stateIds may be registered and looked up in either
51
- * `"#stateId"` or `"stateId"` form — `RouteMap` canonicalizes internally.
52
- * `getStateIdByPath` returns the stateId exactly as registered;
53
- * `getPathByStateId` accepts both forms. Registering the same stateId in both
54
- * forms refers to one entry (the later registration wins for reverse lookup).
53
+ * **The forms of a stateId:** you can register a stateId, and you can look one up,
54
+ * in the form `"#stateId"` or in the form `"stateId"`. `RouteMap` makes the
55
+ * canonical form itself. `getStateIdByPath` returns the stateId exactly as you
56
+ * registered it, and `getPathByStateId` accepts both forms. A registration of the
57
+ * same stateId in both forms gives one entry, and the later registration wins for
58
+ * the lookup in the other direction.
55
59
  *
56
60
  * @example
57
61
  * ```typescript
@@ -73,39 +77,41 @@ export interface RouteMapping {
73
77
  * ```
74
78
  */
75
79
  export declare class RouteMap {
76
- /** Keyed on the canonical (bare, `#`-stripped) stateId form. */
80
+ /** The key is the canonical stateId form, which is bare and without a `#`. */
77
81
  private stateIdToPath;
78
82
  private pathToStateId;
79
83
  private patternBuckets;
80
84
  private pathMatchCache;
81
85
  /**
82
- * Build a route map from an array of state ID path mappings.
86
+ * Builds a route map from an array of the mappings between a state ID and a path.
83
87
  *
84
- * Static paths (no `:param`) are indexed in an O(1) `Map`.
85
- * Parameterized paths are compiled to `URLPattern` and grouped into first-segment
86
- * buckets for efficient candidate selection.
88
+ * The constructor puts each static path, which holds no `:param`, into a `Map` for a
89
+ * lookup in O(1). It compiles each parameterized path to a `URLPattern`, and it
90
+ * groups the patterns into the buckets of the first segment. The selection of the
91
+ * candidates is therefore efficient.
87
92
  *
88
- * @param mappings - Array of `{ stateId, path }` entries. Order determines
89
- * priority when multiple patterns could match the same path.
90
- * @param options - Optional configuration.
91
- * `options.cacheSize`: Maximum number of resolved parameterized path lookups
92
- * to cache. Defaults to `500`. Increase for applications with many unique
93
- * parameterized URL values (e.g. user profile pages with thousands of distinct IDs).
94
- * After eviction the path falls back to the O(k) bucket pattern scan correct
95
- * but slower. Minimum effective value is `1` (QuickLRU constraint).
93
+ * @param mappings - The array of the `{ stateId, path }` entries. The order gives the
94
+ * priority when more than one pattern can match the same path.
95
+ * @param options - The optional configuration.
96
+ * `options.cacheSize`: the maximum number of the resolved parameterized path
97
+ * lookups in the cache. The default is `500`. Raise it for an application with
98
+ * many different values in a parameterized URL, for example a page of a user
99
+ * profile with thousands of different IDs. After an eviction, the path goes to the
100
+ * bucket pattern scan in O(k) again, which is correct but slower. The smallest
101
+ * effective value is `1`, because QuickLRU requires it.
96
102
  */
97
103
  constructor(mappings: RouteMapping[], { cacheSize }?: {
98
104
  cacheSize?: number;
99
105
  });
100
106
  /**
101
- * Resolve a URL path to its mapped state ID.
107
+ * Resolves a URL path to its state ID.
102
108
  *
103
- * Strips query strings and hash fragments before matching. Tries an O(1) exact
104
- * lookup first, then falls back to bucket-indexed pattern matching. Results are
105
- * cached after the first pattern match.
109
+ * The method removes the query string and the hash fragment before the match. It
110
+ * tries an exact lookup in O(1) first, then it uses the pattern match on the bucket
111
+ * index. It keeps each result of a first pattern match in the cache.
106
112
  *
107
- * @param path - URL pathname, optionally including query/hash (e.g., `"/profile/123?ref=nav"`)
108
- * @returns The mapped state ID, or `null` if no route matches
113
+ * @param path - The URL pathname. It can hold a query and a hash, for example `"/profile/123?ref=nav"`
114
+ * @returns The state ID of the path, or `null` when no route matches
109
115
  *
110
116
  * @example
111
117
  * ```typescript
@@ -115,19 +121,19 @@ export declare class RouteMap {
115
121
  */
116
122
  getStateIdByPath(path: string): string | null;
117
123
  /**
118
- * Look up the path pattern registered for a state ID.
124
+ * Returns the path pattern of a state ID.
119
125
  *
120
- * Accepts the stateId in either `"#stateId"` or `"stateId"` form regardless of
121
- * which form was used at registration lookups are canonicalized internally,
122
- * so consumers never need to try both forms.
126
+ * The method accepts the stateId in the form `"#stateId"` and in the form
127
+ * `"stateId"`, and the form of the registration has no effect. The method makes the
128
+ * canonical form itself. Therefore a consumer tries never both forms.
123
129
  *
124
- * @param stateId - State machine state ID (e.g., `"profile"`, `"#settings"`)
125
- * @returns The registered path pattern, or `null` if the state ID is unknown
130
+ * @param stateId - The state ID of the state machine, for example `"profile"` or `"#settings"`
131
+ * @returns The registered path pattern, or `null` when the state ID is unknown
126
132
  *
127
133
  * @example
128
134
  * ```typescript
129
135
  * map.getPathByStateId("profile"); // "/profile/:userId"
130
- * map.getPathByStateId("#profile"); // "/profile/:userId" (same entry)
136
+ * map.getPathByStateId("#profile"); // "/profile/:userId" — the same entry
131
137
  * map.getPathByStateId("missing"); // null
132
138
  * ```
133
139
  */
@@ -1 +1 @@
1
- {"version":3,"file":"base-route-map.d.ts","sourceRoot":"","sources":["../src/base-route-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA0BH;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,YAAY;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,qBAAa,QAAQ;IACpB,gEAAgE;IAChE,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,cAAc,CAGpB;IACF,OAAO,CAAC,cAAc,CAAkC;IAExD;;;;;;;;;;;;;;;OAeG;gBACS,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,SAAe,EAAE,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO;IAmCtF;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAuB7C;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAGhD"}
1
+ {"version":3,"file":"base-route-map.d.ts","sourceRoot":"","sources":["../src/base-route-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA0BH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,YAAY;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uGAAuG;IACvG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,QAAQ;IACpB,8EAA8E;IAC9E,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,cAAc,CAGpB;IACF,OAAO,CAAC,cAAc,CAAkC;IAExD;;;;;;;;;;;;;;;;;OAiBG;gBACS,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,SAAe,EAAE,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO;IAoCtF;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAuB7C;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAGhD"}