@real-router/core 0.36.1 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -168
- package/dist/cjs/api.js +1 -1
- package/dist/cjs/api.js.map +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/metafile-cjs.json +1 -1
- package/dist/esm/api.mjs +1 -1
- package/dist/esm/api.mjs.map +1 -1
- package/dist/esm/chunk-CG7TKDP3.mjs +1 -0
- package/dist/esm/chunk-CG7TKDP3.mjs.map +1 -0
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/metafile-esm.json +1 -1
- package/package.json +7 -7
- package/src/Router.ts +33 -12
- package/src/api/getPluginApi.ts +19 -4
- package/src/api/getRoutesApi.ts +0 -20
- package/src/constants.ts +2 -0
- package/src/fsm/routerFSM.ts +2 -21
- package/src/internals.ts +21 -2
- package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +40 -37
- package/src/namespaces/NavigationNamespace/NavigationNamespace.ts +221 -153
- package/src/namespaces/NavigationNamespace/constants.ts +55 -0
- package/src/namespaces/NavigationNamespace/transition/completeTransition.ts +100 -0
- package/src/namespaces/NavigationNamespace/transition/errorHandling.ts +34 -0
- package/src/namespaces/NavigationNamespace/transition/guardPhase.ts +214 -0
- package/src/namespaces/NavigationNamespace/types.ts +14 -30
- package/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts +6 -1
- package/src/namespaces/RoutesNamespace/RoutesNamespace.ts +36 -35
- package/src/namespaces/RoutesNamespace/forwardToValidation.ts +2 -5
- package/src/namespaces/RoutesNamespace/types.ts +1 -2
- package/src/namespaces/StateNamespace/StateNamespace.ts +13 -17
- package/src/transitionPath.ts +68 -39
- package/src/wiring/RouterWiringBuilder.ts +16 -15
- package/dist/esm/chunk-HZ7RFKT5.mjs +0 -1
- package/dist/esm/chunk-HZ7RFKT5.mjs.map +0 -1
- package/src/namespaces/NavigationNamespace/transition/executeLifecycleGuards.ts +0 -52
- package/src/namespaces/NavigationNamespace/transition/index.ts +0 -93
package/README.md
CHANGED
|
@@ -1,244 +1,220 @@
|
|
|
1
1
|
# @real-router/core
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@real-router/core)
|
|
4
|
+
[](https://www.npmjs.com/package/@real-router/core)
|
|
5
|
+
[](https://bundlejs.com/?q=@real-router/core&treeshake=[{createRouter}])
|
|
6
|
+
[](../../LICENSE)
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
> Simple, powerful, view-agnostic, modular and extensible router for JavaScript applications.
|
|
9
|
+
|
|
10
|
+
This is the core package of the [Real-Router](https://github.com/greydragon888/real-router) monorepo. It provides the router implementation, lifecycle management, navigation pipeline, and tree-shakeable standalone API modules.
|
|
7
11
|
|
|
8
12
|
## Installation
|
|
9
13
|
|
|
10
14
|
```bash
|
|
11
15
|
npm install @real-router/core
|
|
12
|
-
# or
|
|
13
|
-
pnpm add @real-router/core
|
|
14
|
-
# or
|
|
15
|
-
yarn add @real-router/core
|
|
16
|
-
# or
|
|
17
|
-
bun add @real-router/core
|
|
18
16
|
```
|
|
19
17
|
|
|
20
18
|
## Quick Start
|
|
21
19
|
|
|
22
20
|
```typescript
|
|
23
21
|
import { createRouter } from "@real-router/core";
|
|
22
|
+
import { browserPluginFactory } from "@real-router/browser-plugin";
|
|
24
23
|
|
|
25
24
|
const routes = [
|
|
26
25
|
{ name: "home", path: "/" },
|
|
27
|
-
{
|
|
28
|
-
name: "
|
|
29
|
-
|
|
30
|
-
children: [{ name: "profile", path: "/:id" }],
|
|
31
|
-
},
|
|
26
|
+
{ name: "users", path: "/users", children: [
|
|
27
|
+
{ name: "profile", path: "/:id" },
|
|
28
|
+
]},
|
|
32
29
|
];
|
|
33
30
|
|
|
34
31
|
const router = createRouter(routes);
|
|
32
|
+
router.usePlugin(browserPluginFactory());
|
|
35
33
|
|
|
36
34
|
await router.start("/");
|
|
37
35
|
await router.navigate("users.profile", { id: "123" });
|
|
38
36
|
```
|
|
39
37
|
|
|
40
|
-
---
|
|
41
|
-
|
|
42
38
|
## Router API
|
|
43
39
|
|
|
44
|
-
The Router class provides core lifecycle, navigation, state, and subscription methods.
|
|
45
|
-
Domain-specific operations (routes, dependencies, guards, plugin infrastructure, cloning) are available through [standalone API functions](#standalone-api) for tree-shaking.
|
|
46
|
-
|
|
47
|
-
### `createRouter(routes?, options?, dependencies?)`
|
|
48
|
-
|
|
49
|
-
Creates a new router instance. [Wiki](https://github.com/greydragon888/real-router/wiki/createRouter)
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
const router = createRouter(routes, options, dependencies);
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
40
|
### Lifecycle
|
|
58
41
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Stops the router. Cancels any in-progress transition. [Wiki](https://github.com/greydragon888/real-router/wiki/stop)
|
|
66
|
-
|
|
67
|
-
#### `router.dispose(): void`
|
|
68
|
-
|
|
69
|
-
Permanently terminates the router. Cannot be restarted. [Wiki](https://github.com/greydragon888/real-router/wiki/dispose)
|
|
70
|
-
|
|
71
|
-
#### `router.isActive(): boolean`
|
|
72
|
-
|
|
73
|
-
Returns whether the router is active. [Wiki](https://github.com/greydragon888/real-router/wiki/isActive)
|
|
74
|
-
|
|
75
|
-
---
|
|
42
|
+
| Method | Returns | Description |
|
|
43
|
+
|--------|---------|-------------|
|
|
44
|
+
| `start(path)` | `Promise<State>` | Start the router with an initial path |
|
|
45
|
+
| `stop()` | `this` | Stop the router, cancel in-progress transition |
|
|
46
|
+
| `dispose()` | `void` | Permanently terminate (cannot restart) |
|
|
47
|
+
| `isActive()` | `boolean` | Whether the router is started |
|
|
76
48
|
|
|
77
49
|
### Navigation
|
|
78
50
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
51
|
+
| Method | Returns | Description |
|
|
52
|
+
|--------|---------|-------------|
|
|
53
|
+
| `navigate(name, params?, options?)` | `Promise<State>` | Navigate to a route. Fire-and-forget safe |
|
|
54
|
+
| `navigateToDefault(options?)` | `Promise<State>` | Navigate to the default route |
|
|
55
|
+
| `navigateToNotFound(path?)` | `State` | Synchronously set UNKNOWN_ROUTE state |
|
|
56
|
+
| `canNavigateTo(name, params?)` | `boolean` | Check if guards allow navigation |
|
|
82
57
|
|
|
83
58
|
```typescript
|
|
84
59
|
await router.navigate("users.profile", { id: "123" });
|
|
85
|
-
await router.navigate("
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
#### `router.navigateToDefault(options?): Promise<State>`
|
|
89
|
-
|
|
90
|
-
Navigates to the default route. [Wiki](https://github.com/greydragon888/real-router/wiki/navigateToDefault)
|
|
91
|
-
|
|
92
|
-
#### `router.navigateToNotFound(path?): State`
|
|
93
|
-
|
|
94
|
-
Synchronously sets the router to UNKNOWN_ROUTE state. Bypasses the transition pipeline. [Wiki](https://github.com/greydragon888/real-router/wiki/navigateToNotFound)
|
|
95
|
-
|
|
96
|
-
#### `router.canNavigateTo(name, params?): boolean`
|
|
60
|
+
await router.navigate("dashboard", {}, { replace: true });
|
|
97
61
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
62
|
+
// Cancellable navigation
|
|
63
|
+
const controller = new AbortController();
|
|
64
|
+
router.navigate("users", {}, { signal: controller.signal });
|
|
65
|
+
controller.abort();
|
|
66
|
+
```
|
|
101
67
|
|
|
102
68
|
### State
|
|
103
69
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
#### `router.areStatesEqual(state1, state2, ignoreQueryParams?): boolean`
|
|
113
|
-
|
|
114
|
-
Compare two states for equality. [Wiki](https://github.com/greydragon888/real-router/wiki/areStatesEqual)
|
|
115
|
-
|
|
116
|
-
#### `router.shouldUpdateNode(nodeName): (toState, fromState?) => boolean`
|
|
117
|
-
|
|
118
|
-
Create a predicate to check if a route node should update. [Wiki](https://github.com/greydragon888/real-router/wiki/shouldUpdateNode)
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
|
|
122
|
-
### Path Operations
|
|
123
|
-
|
|
124
|
-
#### `router.buildPath(name, params?): string`
|
|
125
|
-
|
|
126
|
-
Build URL path from route name. [Wiki](https://github.com/greydragon888/real-router/wiki/buildPath)
|
|
127
|
-
|
|
128
|
-
#### `router.isActiveRoute(name, params?, strictEquality?, ignoreQueryParams?): boolean`
|
|
129
|
-
|
|
130
|
-
Check if route is currently active. [Wiki](https://github.com/greydragon888/real-router/wiki/isActiveRoute)
|
|
131
|
-
|
|
132
|
-
---
|
|
70
|
+
| Method | Returns | Description |
|
|
71
|
+
|--------|---------|-------------|
|
|
72
|
+
| `getState()` | `State \| undefined` | Current router state (deeply frozen) |
|
|
73
|
+
| `getPreviousState()` | `State \| undefined` | Previous router state |
|
|
74
|
+
| `areStatesEqual(s1, s2, ignoreQP?)` | `boolean` | Compare two states |
|
|
75
|
+
| `isActiveRoute(name, params?, strict?, ignoreQP?)` | `boolean` | Check if route is active |
|
|
76
|
+
| `buildPath(name, params?)` | `string` | Build URL path from route name |
|
|
133
77
|
|
|
134
|
-
### Events
|
|
78
|
+
### Events & Plugins
|
|
135
79
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
80
|
+
| Method | Returns | Description |
|
|
81
|
+
|--------|---------|-------------|
|
|
82
|
+
| `subscribe(listener)` | `Unsubscribe` | Listen to successful transitions |
|
|
83
|
+
| `usePlugin(...plugins)` | `Unsubscribe` | Register plugin factories |
|
|
139
84
|
|
|
140
85
|
```typescript
|
|
141
86
|
const unsub = router.subscribe(({ route, previousRoute }) => {
|
|
142
|
-
console.log(previousRoute?.name, "
|
|
87
|
+
console.log(previousRoute?.name, "->", route.name);
|
|
143
88
|
});
|
|
144
89
|
```
|
|
145
90
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
### Plugins
|
|
149
|
-
|
|
150
|
-
#### `router.usePlugin(...plugins): Unsubscribe`
|
|
91
|
+
## Standalone API
|
|
151
92
|
|
|
152
|
-
|
|
93
|
+
Tree-shakeable functions imported from `@real-router/core/api`. Only imported functions are bundled.
|
|
153
94
|
|
|
154
95
|
```typescript
|
|
155
|
-
import {
|
|
156
|
-
|
|
157
|
-
|
|
96
|
+
import {
|
|
97
|
+
getRoutesApi,
|
|
98
|
+
getDependenciesApi,
|
|
99
|
+
getLifecycleApi,
|
|
100
|
+
getPluginApi,
|
|
101
|
+
cloneRouter,
|
|
102
|
+
} from "@real-router/core/api";
|
|
158
103
|
```
|
|
159
104
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
Add, remove, replace, and query routes at runtime. [Wiki](https://github.com/greydragon888/real-router/wiki/getRoutesApi)
|
|
169
|
-
|
|
170
|
-
**Methods:** `add`, `remove`, `replace`, `update`, `clear`, `has`, `get`, `getConfig`
|
|
171
|
-
|
|
172
|
-
### `getDependenciesApi(router)` — Dependencies
|
|
173
|
-
|
|
174
|
-
Dependency injection container. [Wiki](https://github.com/greydragon888/real-router/wiki/getDependenciesApi)
|
|
175
|
-
|
|
176
|
-
**Methods:** `get`, `getAll`, `set`, `setAll`, `remove`, `reset`, `has`
|
|
177
|
-
|
|
178
|
-
### `getLifecycleApi(router)` — Guards
|
|
179
|
-
|
|
180
|
-
Route activation/deactivation guards. [Wiki](https://github.com/greydragon888/real-router/wiki/getLifecycleApi)
|
|
181
|
-
|
|
182
|
-
**Methods:** `addActivateGuard`, `addDeactivateGuard`, `removeActivateGuard`, `removeDeactivateGuard`
|
|
183
|
-
|
|
184
|
-
### `getPluginApi(router)` — Plugin Infrastructure
|
|
185
|
-
|
|
186
|
-
Low-level API for plugin authors. State building, path matching, event system, method interception, router extension. [Wiki](https://github.com/greydragon888/real-router/wiki/getPluginApi)
|
|
187
|
-
|
|
188
|
-
**Methods:** `makeState`, `buildState`, `buildNavigationState`, `forwardState`, `matchPath`, `setRootPath`, `getRootPath`, `addEventListener`, `getOptions`, `getTree`, `addInterceptor`, `extendRouter`
|
|
105
|
+
| Function | Purpose | Key methods |
|
|
106
|
+
|----------|---------|-------------|
|
|
107
|
+
| `getRoutesApi(router)` | Dynamic route CRUD | `add`, `remove`, `update`, `replace`, `has`, `get` |
|
|
108
|
+
| `getDependenciesApi(router)` | Dependency injection | `get`, `set`, `setAll`, `remove`, `has` |
|
|
109
|
+
| `getLifecycleApi(router)` | Guard registration | `addActivateGuard`, `addDeactivateGuard`, `remove*` |
|
|
110
|
+
| `getPluginApi(router)` | Plugin infrastructure | `makeState`, `matchPath`, `addInterceptor`, `extendRouter`, `getRouteConfig` |
|
|
111
|
+
| `cloneRouter(router, deps?)` | SSR cloning | Shares route definitions, independent state |
|
|
189
112
|
|
|
190
|
-
### `getNavigator(router)`
|
|
113
|
+
### `getNavigator(router)` (main entry)
|
|
191
114
|
|
|
192
|
-
Frozen subset of router methods for view layers. Pre-bound, safe to destructure.
|
|
115
|
+
Frozen read-only subset of router methods for view layers. Pre-bound, safe to destructure. Imported from `@real-router/core`, not `/api`.
|
|
193
116
|
|
|
194
|
-
|
|
117
|
+
```typescript
|
|
118
|
+
import { getNavigator } from "@real-router/core";
|
|
119
|
+
```
|
|
195
120
|
|
|
196
|
-
|
|
121
|
+
```typescript
|
|
122
|
+
// Dynamic route management
|
|
123
|
+
const routes = getRoutesApi(router);
|
|
124
|
+
routes.add({ name: "settings", path: "/settings" });
|
|
125
|
+
routes.replace(newRoutes); // atomic HMR-safe replacement
|
|
126
|
+
|
|
127
|
+
// Dependency injection for guards and plugins
|
|
128
|
+
const deps = getDependenciesApi(router);
|
|
129
|
+
deps.set("authService", authService);
|
|
130
|
+
|
|
131
|
+
// Global lifecycle guards
|
|
132
|
+
const lifecycle = getLifecycleApi(router);
|
|
133
|
+
lifecycle.addActivateGuard("admin", (router, getDep) => (toState) => {
|
|
134
|
+
return getDep("authService").isAuthenticated();
|
|
135
|
+
});
|
|
197
136
|
|
|
198
|
-
|
|
137
|
+
// SSR — clone with request-scoped deps
|
|
138
|
+
const requestRouter = cloneRouter(router, { store: requestStore });
|
|
139
|
+
await requestRouter.start(req.url);
|
|
140
|
+
```
|
|
199
141
|
|
|
200
|
-
## Configuration
|
|
142
|
+
## Route Configuration
|
|
201
143
|
|
|
202
|
-
|
|
144
|
+
```typescript
|
|
145
|
+
import type { Route } from "@real-router/core";
|
|
203
146
|
|
|
204
|
-
|
|
147
|
+
const routes: Route[] = [
|
|
148
|
+
{
|
|
149
|
+
name: "admin",
|
|
150
|
+
path: "/admin",
|
|
151
|
+
canActivate: (router, getDep) => (toState, fromState, signal) => {
|
|
152
|
+
return getDep("authService").isAdmin();
|
|
153
|
+
},
|
|
154
|
+
children: [
|
|
155
|
+
{ name: "dashboard", path: "/dashboard", defaultParams: { tab: "overview" } },
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: "legacy",
|
|
160
|
+
path: "/old-path",
|
|
161
|
+
forwardTo: "home", // URL alias — guards on source are NOT executed
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: "product",
|
|
165
|
+
path: "/product/:id",
|
|
166
|
+
encodeParams: ({ id }) => ({ id: String(id) }),
|
|
167
|
+
decodeParams: ({ id }) => ({ id: Number(id) }),
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
```
|
|
205
171
|
|
|
206
172
|
## Error Handling
|
|
207
173
|
|
|
208
|
-
Navigation errors are instances of `RouterError
|
|
209
|
-
|
|
210
|
-
---
|
|
211
|
-
|
|
212
|
-
## Observable Support
|
|
174
|
+
Navigation errors are instances of `RouterError` with typed error codes:
|
|
213
175
|
|
|
214
|
-
|
|
215
|
-
|
|
176
|
+
```typescript
|
|
177
|
+
import { RouterError, errorCodes } from "@real-router/core";
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
await router.navigate("admin");
|
|
181
|
+
} catch (err) {
|
|
182
|
+
if (err instanceof RouterError) {
|
|
183
|
+
// err.code: ROUTE_NOT_FOUND | CANNOT_ACTIVATE | CANNOT_DEACTIVATE
|
|
184
|
+
// | TRANSITION_CANCELLED | SAME_STATES | DISPOSED | ...
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
216
188
|
|
|
217
|
-
|
|
189
|
+
See [RouterError](https://github.com/greydragon888/real-router/wiki/RouterError) and [Error Codes](https://github.com/greydragon888/real-router/wiki/error-codes) for the full reference.
|
|
218
190
|
|
|
219
191
|
## Documentation
|
|
220
192
|
|
|
221
|
-
Full documentation
|
|
193
|
+
Full documentation: [Wiki](https://github.com/greydragon888/real-router/wiki)
|
|
222
194
|
|
|
223
|
-
- [
|
|
224
|
-
- [
|
|
225
|
-
- [
|
|
226
|
-
- [
|
|
227
|
-
- [
|
|
228
|
-
- [Error Codes](https://github.com/greydragon888/real-router/wiki/error-codes)
|
|
195
|
+
- [Core Concepts](https://github.com/greydragon888/real-router/wiki/core-concepts) — overview and mental model
|
|
196
|
+
- [Defining Routes](https://github.com/greydragon888/real-router/wiki/Route) — nesting, path syntax, guards
|
|
197
|
+
- [Navigation Lifecycle](https://github.com/greydragon888/real-router/wiki/navigation-lifecycle) — transitions, guards, hooks
|
|
198
|
+
- [RouterOptions](https://github.com/greydragon888/real-router/wiki/RouterOptions) — `defaultRoute`, `trailingSlash`, `allowNotFound`, and more
|
|
199
|
+
- [Plugin Architecture](https://github.com/greydragon888/real-router/wiki/plugin-architecture) — interception, extension, events
|
|
229
200
|
- [Migration from router5](https://github.com/greydragon888/real-router/wiki/migration-guide)
|
|
230
201
|
|
|
231
|
-
---
|
|
232
|
-
|
|
233
202
|
## Related Packages
|
|
234
203
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
204
|
+
| Package | Description |
|
|
205
|
+
|---------|-------------|
|
|
206
|
+
| [@real-router/react](https://www.npmjs.com/package/@real-router/react) | React integration (`RouterProvider`, hooks, `Link`, `RouteView`) |
|
|
207
|
+
| [@real-router/browser-plugin](https://www.npmjs.com/package/@real-router/browser-plugin) | Browser History API and URL synchronization |
|
|
208
|
+
| [@real-router/hash-plugin](https://www.npmjs.com/package/@real-router/hash-plugin) | Hash-based routing |
|
|
209
|
+
| [@real-router/rx](https://www.npmjs.com/package/@real-router/rx) | Observable API (`state$`, `events$`, TC39 Observable) |
|
|
210
|
+
| [@real-router/logger-plugin](https://www.npmjs.com/package/@real-router/logger-plugin) | Development logging |
|
|
211
|
+
| [@real-router/persistent-params-plugin](https://www.npmjs.com/package/@real-router/persistent-params-plugin) | Parameter persistence |
|
|
212
|
+
| [@real-router/route-utils](https://www.npmjs.com/package/@real-router/route-utils) | Route tree queries and segment testing |
|
|
213
|
+
|
|
214
|
+
## Contributing
|
|
215
|
+
|
|
216
|
+
See [contributing guidelines](../../CONTRIBUTING.md) for development setup and PR process.
|
|
241
217
|
|
|
242
218
|
## License
|
|
243
219
|
|
|
244
|
-
MIT © [Oleg Ivanov](https://github.com/greydragon888)
|
|
220
|
+
[MIT](../../LICENSE) © [Oleg Ivanov](https://github.com/greydragon888)
|