@real-router/core 0.25.4 → 0.27.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 (49) hide show
  1. package/README.md +175 -323
  2. package/dist/cjs/index.d.ts +47 -178
  3. package/dist/cjs/index.js +1 -1
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/cjs/metafile-cjs.json +1 -1
  6. package/dist/esm/index.d.mts +47 -178
  7. package/dist/esm/index.mjs +1 -1
  8. package/dist/esm/index.mjs.map +1 -1
  9. package/dist/esm/metafile-esm.json +1 -1
  10. package/package.json +3 -3
  11. package/src/Router.ts +86 -574
  12. package/src/api/cloneRouter.ts +106 -0
  13. package/src/api/getDependenciesApi.ts +216 -0
  14. package/src/api/getLifecycleApi.ts +67 -0
  15. package/src/api/getPluginApi.ts +118 -0
  16. package/src/api/getRoutesApi.ts +566 -0
  17. package/src/api/index.ts +16 -0
  18. package/src/api/types.ts +7 -0
  19. package/src/getNavigator.ts +5 -2
  20. package/src/index.ts +17 -3
  21. package/src/internals.ts +115 -0
  22. package/src/namespaces/DependenciesNamespace/dependenciesStore.ts +30 -0
  23. package/src/namespaces/DependenciesNamespace/index.ts +3 -1
  24. package/src/namespaces/DependenciesNamespace/validators.ts +2 -4
  25. package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +1 -20
  26. package/src/namespaces/EventBusNamespace/validators.ts +36 -0
  27. package/src/namespaces/NavigationNamespace/NavigationNamespace.ts +63 -12
  28. package/src/namespaces/NavigationNamespace/transition/errorHandling.ts +2 -0
  29. package/src/namespaces/NavigationNamespace/transition/{executeLifecycleHooks.ts → executeLifecycleGuards.ts} +16 -7
  30. package/src/namespaces/NavigationNamespace/transition/index.ts +7 -4
  31. package/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts +1 -16
  32. package/src/namespaces/RoutesNamespace/RoutesNamespace.ts +133 -1089
  33. package/src/namespaces/RoutesNamespace/forwardToValidation.ts +411 -0
  34. package/src/namespaces/RoutesNamespace/helpers.ts +1 -407
  35. package/src/namespaces/RoutesNamespace/index.ts +2 -0
  36. package/src/namespaces/RoutesNamespace/routesStore.ts +388 -0
  37. package/src/namespaces/RoutesNamespace/validators.ts +209 -3
  38. package/src/namespaces/StateNamespace/StateNamespace.ts +1 -44
  39. package/src/namespaces/StateNamespace/validators.ts +46 -0
  40. package/src/namespaces/index.ts +3 -5
  41. package/src/types.ts +12 -138
  42. package/src/wiring/RouterWiringBuilder.ts +30 -36
  43. package/src/wiring/types.ts +3 -6
  44. package/src/wiring/wireRouter.ts +0 -1
  45. package/src/namespaces/CloneNamespace/CloneNamespace.ts +0 -120
  46. package/src/namespaces/CloneNamespace/index.ts +0 -3
  47. package/src/namespaces/CloneNamespace/types.ts +0 -42
  48. package/src/namespaces/DependenciesNamespace/DependenciesNamespace.ts +0 -248
  49. package/src/namespaces/RoutesNamespace/stateBuilder.ts +0 -70
package/README.md CHANGED
@@ -29,13 +29,16 @@ const routes = [
29
29
 
30
30
  const router = createRouter(routes);
31
31
 
32
- router.start();
33
- router.navigate("users.profile", { id: "123" });
32
+ await router.start("/");
33
+ await router.navigate("users.profile", { id: "123" });
34
34
  ```
35
35
 
36
36
  ---
37
37
 
38
- ## Essential API
38
+ ## Router API
39
+
40
+ The Router class provides core lifecycle, navigation, state, and subscription methods.
41
+ Domain-specific operations (routes, dependencies, guards, plugin infrastructure, cloning) are available through standalone API functions for tree-shaking.
39
42
 
40
43
  ### `createRouter(routes?, options?, dependencies?)`
41
44
 
@@ -53,23 +56,23 @@ const router = createRouter(
53
56
 
54
57
  ### Lifecycle
55
58
 
56
- #### `router.start(startPath?, done?)`
59
+ #### `router.start(path): Promise<State>`
57
60
 
58
- Starts the router. [Wiki](https://github.com/greydragon888/real-router/wiki/start)
61
+ Starts the router with an initial path. Returns a Promise that resolves with the matched state. [Wiki](https://github.com/greydragon888/real-router/wiki/start)
59
62
 
60
63
  ```typescript
61
- router.start();
62
- router.start("/users/123");
63
- router.start("/users/123", (err, state) => {
64
- if (err) console.error(err);
65
- });
64
+ const state = await router.start("/users/123");
66
65
  ```
67
66
 
68
- #### `router.stop()`
67
+ #### `router.stop(): this`
68
+
69
+ Stops the router. Cancels any in-progress transition. [Wiki](https://github.com/greydragon888/real-router/wiki/stop)
70
+
71
+ #### `router.dispose(): void`
69
72
 
70
- Stops the router. [Wiki](https://github.com/greydragon888/real-router/wiki/stop)
73
+ Permanently terminates the router. Unlike `stop()`, it cannot be restarted. All mutating methods throw `RouterError(DISPOSED)` after disposal. Idempotent. [Wiki](https://github.com/greydragon888/real-router/wiki/dispose)
71
74
 
72
- #### `router.isActive()`
75
+ #### `router.isActive(): boolean`
73
76
 
74
77
  Returns whether the router is active (started and has current state). [Wiki](https://github.com/greydragon888/real-router/wiki/isActive)
75
78
 
@@ -77,400 +80,246 @@ Returns whether the router is active (started and has current state). [Wiki](htt
77
80
 
78
81
  ### Navigation
79
82
 
80
- #### `router.navigate(name, params?, options?, done?)`
81
-
82
- Navigates to a route by name. Returns a cancel function. [Wiki](https://github.com/greydragon888/real-router/wiki/navigate)
83
-
84
- ```typescript
85
- router.navigate("users");
86
- router.navigate("users.profile", { id: "123" });
87
- router.navigate("users.profile", { id: "123" }, { replace: true });
88
-
89
- // With callback
90
- router.navigate("users", {}, {}, (err, state) => {
91
- if (err) console.error(err);
92
- });
83
+ All navigation methods return `Promise<State>`.
93
84
 
94
- // Cancellation
95
- const cancel = router.navigate("users.profile", { id: "123" });
96
- cancel(); // abort navigation
97
- ```
85
+ #### `router.navigate(name, params?, options?): Promise<State>`
98
86
 
99
- #### `router.getState()`
100
-
101
- Returns the current router state. [Wiki](https://github.com/greydragon888/real-router/wiki/getState)
87
+ Navigates to a route by name. [Wiki](https://github.com/greydragon888/real-router/wiki/navigate)
102
88
 
103
89
  ```typescript
104
- const state = router.getState();
105
- // { name: "users.profile", params: { id: "123" }, path: "/users/123" }
106
- ```
107
-
108
- #### `router.navigateToDefault(options?, done?)`
90
+ const state = await router.navigate("users.profile", { id: "123" });
109
91
 
110
- Navigates to the default route. [Wiki](https://github.com/greydragon888/real-router/wiki/navigateToDefault)
92
+ // With navigation options
93
+ await router.navigate("users", {}, { replace: true });
111
94
 
112
- ---
95
+ // Concurrent navigation cancels previous
96
+ router.navigate("slow-route");
97
+ router.navigate("fast-route"); // Previous rejects with TRANSITION_CANCELLED
113
98
 
114
- ### Guards
99
+ // Cancel via AbortController
100
+ const controller = new AbortController();
101
+ router.navigate("route", {}, { signal: controller.signal });
102
+ controller.abort(); // rejects with TRANSITION_CANCELLED
115
103
 
116
- #### `router.addActivateGuard(name, guardFactory)`
104
+ // Timeout
105
+ router.navigate("route", {}, { signal: AbortSignal.timeout(5000) });
117
106
 
118
- Registers a guard for route activation. [Wiki](https://github.com/greydragon888/real-router/wiki/canActivate)
119
-
120
- ```typescript
121
- router.addActivateGuard("admin", () => (toState, fromState, done) => {
122
- if (!isAuthenticated()) {
123
- done({ redirect: { name: "login" } });
124
- } else {
125
- done();
107
+ // Error handling
108
+ try {
109
+ await router.navigate("admin");
110
+ } catch (err) {
111
+ if (err instanceof RouterError) {
112
+ // ROUTE_NOT_FOUND, CANNOT_ACTIVATE, CANNOT_DEACTIVATE,
113
+ // TRANSITION_CANCELLED, SAME_STATES, ROUTER_DISPOSED
126
114
  }
127
- });
115
+ }
128
116
  ```
129
117
 
130
- #### `router.addDeactivateGuard(name, guardFactory)`
118
+ **Fire-and-forget safe:** calling `router.navigate(...)` without `await` suppresses expected errors (`SAME_STATES`, `TRANSITION_CANCELLED`).
131
119
 
132
- Registers a guard for route deactivation. [Wiki](https://github.com/greydragon888/real-router/wiki/canDeactivate)
133
-
134
- ```typescript
135
- router.addDeactivateGuard("editor", () => (toState, fromState, done) => {
136
- if (hasUnsavedChanges()) {
137
- done({ error: new Error("Unsaved changes") });
138
- } else {
139
- done();
140
- }
141
- });
142
- ```
120
+ #### `router.navigateToDefault(options?): Promise<State>`
143
121
 
144
- ---
122
+ Navigates to the default route. [Wiki](https://github.com/greydragon888/real-router/wiki/navigateToDefault)
145
123
 
146
- ### Events
124
+ #### `router.canNavigateTo(name, params?): boolean`
147
125
 
148
- #### `router.subscribe(listener)`
126
+ Synchronously checks if navigation to a route would be allowed by guards. [Wiki](https://github.com/greydragon888/real-router/wiki/canNavigateTo)
149
127
 
150
- Subscribes to successful transitions. [Wiki](https://github.com/greydragon888/real-router/wiki/subscribe)
128
+ ---
151
129
 
152
- ```typescript
153
- const unsubscribe = router.subscribe(({ route, previousRoute }) => {
154
- console.log("Navigation:", previousRoute?.name, "→", route.name);
155
- });
156
- ```
130
+ ### State
157
131
 
158
- #### `router.addEventListener(event, listener)`
132
+ #### `router.getState(): State | undefined`
159
133
 
160
- Adds an event listener. Returns an unsubscribe function. [Wiki](https://github.com/greydragon888/real-router/wiki/addEventListener)
134
+ Returns the current router state. [Wiki](https://github.com/greydragon888/real-router/wiki/getState)
161
135
 
162
136
  ```typescript
163
- import { events } from "@real-router/core";
164
-
165
- const unsubscribe = router.addEventListener(
166
- events.TRANSITION_START,
167
- (toState, fromState) => {
168
- console.log("Starting:", toState.name);
169
- },
170
- );
171
-
172
- // To remove listener, call the returned unsubscribe function
173
- unsubscribe();
174
-
175
- // Available events:
176
- // ROUTER_START, ROUTER_STOP
177
- // TRANSITION_START, TRANSITION_SUCCESS, TRANSITION_ERROR, TRANSITION_CANCEL
137
+ const state = router.getState();
138
+ // { name: "users.profile", params: { id: "123" }, path: "/users/123" }
178
139
  ```
179
140
 
180
- ---
141
+ #### `router.getPreviousState(): State | undefined`
181
142
 
182
- ### Plugins
143
+ Returns the previous router state. [Wiki](https://github.com/greydragon888/real-router/wiki/getPreviousState)
183
144
 
184
- #### `router.usePlugin(pluginFactory)`
145
+ #### `router.areStatesEqual(state1, state2, ignoreQueryParams?): boolean`
185
146
 
186
- Registers a plugin. Returns an unsubscribe function. [Wiki](https://github.com/greydragon888/real-router/wiki/usePlugin)
147
+ Compare two states for equality. Query params ignored by default. [Wiki](https://github.com/greydragon888/real-router/wiki/areStatesEqual)
187
148
 
188
- ```typescript
189
- import { browserPluginFactory } from "@real-router/browser-plugin";
149
+ #### `router.shouldUpdateNode(nodeName): (toState, fromState?) => boolean`
190
150
 
191
- const unsubscribe = router.usePlugin(browserPluginFactory());
192
- ```
151
+ Create a predicate to check if a route node should update during transition. [Wiki](https://github.com/greydragon888/real-router/wiki/shouldUpdateNode)
193
152
 
194
153
  ---
195
154
 
196
- ## Advanced API
197
-
198
- ### Routes
199
-
200
- #### `router.addRoute(route, options?): void`
201
-
202
- Add a route definition at runtime.
203
-
204
- **Parameters:**
155
+ ### Path Operations
205
156
 
206
- - `route: Route | Route[]` — route configuration object(s)
207
- - `options?: { parent?: string }` — optional parent route fullName
157
+ #### `router.buildPath(name, params?): string`
208
158
 
209
- **Examples:**
159
+ Build URL path from route name. [Wiki](https://github.com/greydragon888/real-router/wiki/buildPath)
210
160
 
211
161
  ```typescript
212
- // Add route with children syntax
213
- router.addRoute({
214
- name: "users",
215
- path: "/users",
216
- children: [{ name: "profile", path: "/:id" }],
217
- });
218
-
219
- // Add route under existing parent using { parent } option
220
- router.addRoute({ name: "profile", path: "/:id" }, { parent: "users" });
221
- // Creates "users.profile" fullName
222
-
223
- // Batch add under same parent
224
- router.addRoute(
225
- [
226
- { name: "profile", path: "/:id" },
227
- { name: "settings", path: "/settings" },
228
- ],
229
- { parent: "users" },
230
- );
162
+ const path = router.buildPath("users.profile", { id: "123" });
163
+ // "/users/123"
231
164
  ```
232
165
 
233
- Returns: `void`\
234
- [Wiki](https://github.com/greydragon888/real-router/wiki/addRoute)
235
-
236
- #### `router.removeRoute(name: string): void`
237
-
238
- Remove a route by name.\
239
- `name: string` — route name to remove\
240
- Returns: `void`\
241
- [Wiki](https://github.com/greydragon888/real-router/wiki/removeRoute)
166
+ #### `router.isActiveRoute(name, params?, strictEquality?, ignoreQueryParams?): boolean`
242
167
 
243
- #### `router.getRoute(name: string): Route | undefined`
168
+ Check if route is currently active. [Wiki](https://github.com/greydragon888/real-router/wiki/isActiveRoute)
244
169
 
245
- Get route definition by name.\
246
- `name: string` — route name\
247
- Returns: `Route | undefined`\
248
- [Wiki](https://github.com/greydragon888/real-router/wiki/getRoute)
249
-
250
- #### `router.hasRoute(name: string): boolean`
251
-
252
- Check if a route exists.\
253
- `name: string` — route name\
254
- Returns: `boolean`\
255
- [Wiki](https://github.com/greydragon888/real-router/wiki/hasRoute)
256
-
257
- #### `router.clearRoutes(): void`
258
-
259
- Remove all routes.
260
- Returns: `void`\
261
- [Wiki](https://github.com/greydragon888/real-router/wiki/clearRoutes)
170
+ ---
262
171
 
263
- #### `router.updateRoute(name: string, updates: Partial<Route>): void`
172
+ ### Events
264
173
 
265
- Update route configuration.\
266
- `name: string` — route name\
267
- `updates: Partial<Route>` — properties to update\
268
- Returns: `void`\
269
- [Wiki](https://github.com/greydragon888/real-router/wiki/updateRoute)
174
+ #### `router.subscribe(listener): Unsubscribe`
270
175
 
271
- **Note:** To set up route forwarding (redirect), use the `forwardTo` option in route configuration:
176
+ Subscribes to successful transitions. [Wiki](https://github.com/greydragon888/real-router/wiki/subscribe)
272
177
 
273
178
  ```typescript
274
- router.addRoute({ name: "old-url", path: "/old", forwardTo: "new-url" });
275
- // Or update existing route
276
- router.updateRoute("old-url", { forwardTo: "new-url" });
179
+ const unsubscribe = router.subscribe(({ route, previousRoute }) => {
180
+ console.log("Navigation:", previousRoute?.name, "->", route.name);
181
+ });
277
182
  ```
278
183
 
279
184
  ---
280
185
 
281
- ### State Utilities
282
-
283
- #### `router.getPreviousState(): State | undefined`
284
-
285
- Get previous router state.\
286
- Returns: `State | undefined`\
287
- [Wiki](https://github.com/greydragon888/real-router/wiki/getPreviousState)
288
-
289
- #### `router.shouldUpdateNode(nodeName: string): (toState, fromState?) => boolean`
290
-
291
- Create a predicate to check if a route node should update during transition.\
292
- `nodeName: string` — route node name\
293
- Returns: predicate function\
294
- [Wiki](https://github.com/greydragon888/real-router/wiki/shouldUpdateNode)
295
-
296
- #### `router.areStatesEqual(state1: State, state2: State, ignoreQueryParams?: boolean): boolean`
297
-
298
- Compare two states for equality.\
299
- `state1: State` — first state\
300
- `state2: State` — second state\
301
- `ignoreQueryParams?: boolean` — ignore query params (default: true)\
302
- Returns: `boolean`\
303
- [Wiki](https://github.com/greydragon888/real-router/wiki/areStatesEqual)
304
-
305
- ---
306
-
307
- ### Path Operations
308
-
309
- #### `router.buildPath(name: string, params?: Params): string`
310
-
311
- Build URL path from route name.\
312
- `name: string` — route name\
313
- `params?: Params` — route parameters\
314
- Returns: `string`\
315
- [Wiki](https://github.com/greydragon888/real-router/wiki/buildPath)
186
+ ### Plugins
316
187
 
317
- #### `router.buildUrl(name: string, params?: Params, options?: object): string`
188
+ #### `router.usePlugin(...plugins): Unsubscribe`
318
189
 
319
- Build full URL from route name (includes base path and query string).\
320
- `name: string` — route name\
321
- `params?: Params` — route parameters\
322
- `options?: object` — URL building options\
323
- Returns: `string`\
324
- [Wiki](https://github.com/greydragon888/real-router/wiki/buildUrl)
190
+ Registers one or more plugins. Returns an unsubscribe function. [Wiki](https://github.com/greydragon888/real-router/wiki/usePlugin)
325
191
 
326
- #### `router.isActiveRoute(name: string, params?: Params, strictEquality?: boolean, ignoreQueryParams?: boolean): boolean`
192
+ ```typescript
193
+ import { browserPluginFactory } from "@real-router/browser-plugin";
327
194
 
328
- Check if route is currently active.\
329
- `name: string` — route name\
330
- `params?: Params` — route parameters\
331
- `strictEquality?: boolean` — exact match (default: false)\
332
- `ignoreQueryParams?: boolean` — ignore query params (default: true)\
333
- Returns: `boolean`\
334
- [Wiki](https://github.com/greydragon888/real-router/wiki/isActiveRoute)
195
+ const unsubscribe = router.usePlugin(browserPluginFactory());
196
+ ```
335
197
 
336
198
  ---
337
199
 
338
- ### Dependencies
200
+ ## Standalone API
339
201
 
340
- #### `router.getDependency(name: string): unknown`
202
+ Standalone functions provide domain-specific operations. They are tree-shakeable — only imported functions are bundled.
341
203
 
342
- Get a dependency by name.\
343
- `name: string` — dependency name\
344
- Returns: `unknown`\
345
- [Wiki](https://github.com/greydragon888/real-router/wiki/getDependency)
204
+ ### Routes `getRoutesApi(router)`
346
205
 
347
- #### `router.getDependencies(): Dependencies`
206
+ Runtime route management. [Wiki](https://github.com/greydragon888/real-router/wiki/getRoutesApi)
348
207
 
349
- Get all dependencies.\
350
- Returns: `Dependencies`\
351
- [Wiki](https://github.com/greydragon888/real-router/wiki/getDependencies)
352
-
353
- #### `router.setDependency(name: string, value: unknown): void`
208
+ ```typescript
209
+ import { getRoutesApi } from "@real-router/core";
354
210
 
355
- Set a dependency.\
356
- `name: string` — dependency name\
357
- `value: unknown` — dependency value\
358
- Returns: `void`\
359
- [Wiki](https://github.com/greydragon888/real-router/wiki/setDependency)
211
+ const routes = getRoutesApi(router);
360
212
 
361
- #### `router.setDependencies(deps: Dependencies): void`
213
+ // Add routes
214
+ routes.add({ name: "settings", path: "/settings" });
215
+ routes.add({ name: "profile", path: "/:id" }, { parent: "users" });
362
216
 
363
- Set multiple dependencies.\
364
- `deps: Dependencies` — dependencies object\
365
- Returns: `void`\
366
- [Wiki](https://github.com/greydragon888/real-router/wiki/setDependencies)
217
+ // Query
218
+ routes.has("users"); // true
219
+ routes.get("users"); // Route | undefined
367
220
 
368
- #### `router.hasDependency(name: string): boolean`
221
+ // Modify
222
+ routes.update("users", { forwardTo: "users.list" });
223
+ routes.remove("settings");
224
+ routes.clear();
225
+ ```
369
226
 
370
- Check if dependency exists.\
371
- `name: string` — dependency name\
372
- Returns: `boolean`\
373
- [Wiki](https://github.com/greydragon888/real-router/wiki/hasDependency)
227
+ **Methods:** `add`, `remove`, `update`, `clear`, `has`, `get`, `getConfig`
374
228
 
375
- #### `router.removeDependency(name: string): void`
229
+ ### Dependencies — `getDependenciesApi(router)`
376
230
 
377
- Remove a dependency.\
378
- `name: string` — dependency name\
379
- Returns: `void`\
380
- [Wiki](https://github.com/greydragon888/real-router/wiki/removeDependency)
231
+ Dependency injection container. [Wiki](https://github.com/greydragon888/real-router/wiki/getDependenciesApi)
381
232
 
382
- #### `router.resetDependencies(): void`
233
+ ```typescript
234
+ import { getDependenciesApi } from "@real-router/core";
383
235
 
384
- Remove all dependencies.\
385
- Returns: `void`\
386
- [Wiki](https://github.com/greydragon888/real-router/wiki/resetDependencies)
236
+ const deps = getDependenciesApi(router);
387
237
 
388
- ---
238
+ deps.set("authService", authService);
239
+ deps.get("authService"); // authService
240
+ deps.has("authService"); // true
241
+ deps.getAll(); // { authService: ... }
242
+ deps.remove("authService");
243
+ deps.reset();
244
+ ```
389
245
 
390
- ### Options
246
+ **Methods:** `get`, `getAll`, `set`, `setAll`, `remove`, `reset`, `has`
391
247
 
392
- #### `router.getOptions(): Options`
248
+ ### Guards — `getLifecycleApi(router)`
393
249
 
394
- Get all router options.\
395
- Returns: `Options`\
396
- [Wiki](https://github.com/greydragon888/real-router/wiki/getOptions)
250
+ Route activation/deactivation guards. [Wiki](https://github.com/greydragon888/real-router/wiki/getLifecycleApi)
397
251
 
398
- ---
252
+ ```typescript
253
+ import { getLifecycleApi } from "@real-router/core";
399
254
 
400
- ### Other
255
+ const lifecycle = getLifecycleApi(router);
401
256
 
402
- #### `router.clone(dependencies?: Dependencies): Router`
257
+ // Guard returns boolean or Promise<boolean> (true = allow, false = block)
258
+ lifecycle.addActivateGuard("admin", () => (toState, fromState) => {
259
+ return isAuthenticated(); // sync
260
+ });
403
261
 
404
- Clone router for SSR.\
405
- `dependencies?: Dependencies` override dependencies\
406
- Returns: `Router`\
407
- [Wiki](https://github.com/greydragon888/real-router/wiki/clone)
262
+ lifecycle.addDeactivateGuard("editor", () => async (toState, fromState) => {
263
+ return !(await checkUnsavedChanges()); // async
264
+ });
408
265
 
409
- ---
266
+ // Guards receive AbortSignal for cooperative cancellation
267
+ lifecycle.addActivateGuard("dashboard", () => async (toState, fromState, signal) => {
268
+ const res = await fetch("/api/auth", { signal }); // auto-cancelled on abort
269
+ return res.ok;
270
+ });
410
271
 
411
- ## Plugin Development API
272
+ // Remove guards
273
+ lifecycle.removeActivateGuard("admin");
274
+ lifecycle.removeDeactivateGuard("editor");
275
+ ```
412
276
 
413
- The following methods are designed for **plugin authors**. They provide low-level access for advanced use cases like browser history integration, persistent parameters, and custom navigation sources.
277
+ **Methods:** `addActivateGuard`, `addDeactivateGuard`, `removeActivateGuard`, `removeDeactivateGuard`
414
278
 
415
- These methods are stable but intended for plugin development, not application code.
279
+ ### Plugin Infrastructure `getPluginApi(router)`
416
280
 
417
- #### `router.matchPath(path: string): State | undefined`
281
+ Low-level API for plugin authors. Provides access to state building, path matching, event system, and navigation. [Wiki](https://github.com/greydragon888/real-router/wiki/getPluginApi)
418
282
 
419
- Match URL path to route state.\
420
- `path: string` URL path to match\
421
- Returns: `State | undefined`\
422
- [Wiki](https://github.com/greydragon888/real-router/wiki/matchPath)
283
+ ```typescript
284
+ import { getPluginApi } from "@real-router/core";
423
285
 
424
- #### `router.makeState(name, params?, path?, meta?, forceId?): State`
286
+ const api = getPluginApi(router);
425
287
 
426
- Create State with custom `meta.id` for history restoration.\
427
- `name: string` route name\
428
- `params?: Params` route parameters\
429
- `path?: string` — URL path\
430
- `meta?: object` — metadata\
431
- `forceId?: number` — force specific `meta.id` value\
432
- Returns: `State`\
433
- [Wiki](https://github.com/greydragon888/real-router/wiki/makeState)
288
+ // State building
289
+ const state = api.matchPath("/users/123");
290
+ const builtState = api.makeState("users.profile", { id: "123" });
434
291
 
435
- #### `router.buildState(name: string, params?: Params): State | undefined`
292
+ // Event system
293
+ const unsub = api.addEventListener(events.TRANSITION_START, (toState, fromState) => {
294
+ console.log("Starting:", toState.name);
295
+ });
436
296
 
437
- Validate route and build state with segment metadata.\
438
- `name: string` route name\
439
- `params?: Params` — route parameters\
440
- Returns: `State | undefined`\
441
- [Wiki](https://github.com/greydragon888/real-router/wiki/buildState)
297
+ // Navigation with pre-built state
298
+ await api.navigateToState(toState, fromState, opts);
442
299
 
443
- #### `router.forwardState(name: string, params: Params): { name: string; params: Params }`
300
+ // Root path management
301
+ api.setRootPath("/app");
302
+ api.getRootPath(); // "/app"
444
303
 
445
- Resolve route forwarding and merge default params.\
446
- `name: string` route name\
447
- `params: Params` — route parameters\
448
- Returns: `{ name, params }` — resolved route name and merged params\
449
- [Wiki](https://github.com/greydragon888/real-router/wiki/forwardState)
304
+ // Forward state interception (used by persistent-params-plugin)
305
+ api.setForwardState((name, params) => ({ name, params: withPersistent(params) }));
306
+ ```
450
307
 
451
- #### `router.navigateToState(toState, fromState, opts, done, emitSuccess): CancelFn`
308
+ **Methods:** `makeState`, `buildState`, `buildNavigationState`, `forwardState`, `matchPath`, `setRootPath`, `getRootPath`, `navigateToState`, `addEventListener`, `getOptions`, `getTree`, `getForwardState`, `setForwardState`
452
309
 
453
- Navigate with pre-built State object.\
454
- `toState: State` — target state\
455
- `fromState: State | undefined` — current state\
456
- `opts: NavigationOptions` — navigation options\
457
- `done: DoneFn` — callback\
458
- `emitSuccess: boolean` — whether to emit TRANSITION_SUCCESS\
459
- Returns: `CancelFn`\
460
- [Wiki](https://github.com/greydragon888/real-router/wiki/navigateToState)
310
+ ### SSR Cloning `cloneRouter(router, deps?)`
461
311
 
462
- #### `router.setRootPath(rootPath: string): void`
312
+ Clone router for server-side rendering. [Wiki](https://github.com/greydragon888/real-router/wiki/cloneRouter)
463
313
 
464
- Dynamically modify router base path.\
465
- `rootPath: string` new root path prefix\
466
- Returns: `void`\
467
- [Wiki](https://github.com/greydragon888/real-router/wiki/setRootPath)
314
+ ```typescript
315
+ import { cloneRouter } from "@real-router/core";
468
316
 
469
- #### `router.getRootPath(): string`
317
+ // Server: clone router per request
318
+ const serverRouter = cloneRouter(router, { request: req });
319
+ await serverRouter.start(req.url);
320
+ ```
470
321
 
471
- Read current base path.\
472
- Returns: `string`\
473
- [Wiki](https://github.com/greydragon888/real-router/wiki/getRootPath)
322
+ Shares immutable route tree (O(1)), copies mutable state (dependencies, options, plugins, guards).
474
323
 
475
324
  ---
476
325
 
@@ -508,22 +357,25 @@ Navigation errors are instances of `RouterError`:
508
357
  ```typescript
509
358
  import { RouterError, errorCodes } from "@real-router/core";
510
359
 
511
- router.navigate("users", {}, {}, (err, state) => {
360
+ try {
361
+ await router.navigate("users");
362
+ } catch (err) {
512
363
  if (err instanceof RouterError) {
513
364
  console.log(err.code, err.message);
514
365
  }
515
- });
366
+ }
516
367
  ```
517
368
 
518
- | Code | Description |
519
- | ------------------- | ------------------------------ |
520
- | `ROUTE_NOT_FOUND` | Route doesn't exist |
521
- | `CANNOT_ACTIVATE` | Blocked by canActivate guard |
522
- | `CANNOT_DEACTIVATE` | Blocked by canDeactivate guard |
523
- | `CANCELLED` | Navigation was cancelled |
524
- | `SAME_STATES` | Already at target route |
525
- | `NOT_STARTED` | Router not started |
526
- | `ALREADY_STARTED` | Router already started |
369
+ | Code | Description |
370
+ | --------------------- | ------------------------------ |
371
+ | `ROUTE_NOT_FOUND` | Route doesn't exist |
372
+ | `CANNOT_ACTIVATE` | Blocked by canActivate guard |
373
+ | `CANNOT_DEACTIVATE` | Blocked by canDeactivate guard |
374
+ | `CANCELLED` | Navigation was cancelled |
375
+ | `SAME_STATES` | Already at target route |
376
+ | `NOT_STARTED` | Router not started |
377
+ | `ALREADY_STARTED` | Router already started |
378
+ | `DISPOSED` | Router has been disposed |
527
379
 
528
380
  See [RouterError](https://github.com/greydragon888/real-router/wiki/RouterError) and [Error Codes](https://github.com/greydragon888/real-router/wiki/error-codes) for details.
529
381