@real-router/core 0.36.2 → 0.38.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 +155 -158
- 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/cjs/utils.d.ts +15 -0
- package/dist/cjs/utils.js +1 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/esm/api.mjs +1 -1
- package/dist/esm/api.mjs.map +1 -1
- package/dist/esm/{chunk-PKKD6URG.mjs → chunk-CG7TKDP3.mjs} +1 -1
- 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/dist/esm/utils.d.mts +15 -0
- package/dist/esm/utils.mjs +1 -0
- package/dist/esm/utils.mjs.map +1 -0
- package/package.json +19 -7
- package/src/api/getPluginApi.ts +9 -0
- package/src/api/getRoutesApi.ts +0 -20
- package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +8 -1
- package/src/namespaces/NavigationNamespace/constants.ts +3 -3
- package/src/namespaces/NavigationNamespace/transition/guardPhase.ts +3 -3
- package/src/utils/index.ts +1 -0
- package/src/utils/serializeState.ts +22 -0
- package/src/wiring/RouterWiringBuilder.ts +8 -4
- package/dist/esm/chunk-PKKD6URG.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,26 +1,25 @@
|
|
|
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: "/" },
|
|
@@ -32,213 +31,211 @@ const routes = [
|
|
|
32
31
|
];
|
|
33
32
|
|
|
34
33
|
const router = createRouter(routes);
|
|
34
|
+
router.usePlugin(browserPluginFactory());
|
|
35
35
|
|
|
36
36
|
await router.start("/");
|
|
37
37
|
await router.navigate("users.profile", { id: "123" });
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
---
|
|
41
|
-
|
|
42
40
|
## Router API
|
|
43
41
|
|
|
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
42
|
### Lifecycle
|
|
58
43
|
|
|
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
|
-
---
|
|
44
|
+
| Method | Returns | Description |
|
|
45
|
+
| ------------- | ---------------- | ---------------------------------------------- |
|
|
46
|
+
| `start(path)` | `Promise<State>` | Start the router with an initial path |
|
|
47
|
+
| `stop()` | `this` | Stop the router, cancel in-progress transition |
|
|
48
|
+
| `dispose()` | `void` | Permanently terminate (cannot restart) |
|
|
49
|
+
| `isActive()` | `boolean` | Whether the router is started |
|
|
76
50
|
|
|
77
51
|
### Navigation
|
|
78
52
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
53
|
+
| Method | Returns | Description |
|
|
54
|
+
| ----------------------------------- | ---------------- | ----------------------------------------- |
|
|
55
|
+
| `navigate(name, params?, options?)` | `Promise<State>` | Navigate to a route. Fire-and-forget safe |
|
|
56
|
+
| `navigateToDefault(options?)` | `Promise<State>` | Navigate to the default route |
|
|
57
|
+
| `navigateToNotFound(path?)` | `State` | Synchronously set UNKNOWN_ROUTE state |
|
|
58
|
+
| `canNavigateTo(name, params?)` | `boolean` | Check if guards allow navigation |
|
|
82
59
|
|
|
83
60
|
```typescript
|
|
84
61
|
await router.navigate("users.profile", { id: "123" });
|
|
85
|
-
await router.navigate("
|
|
86
|
-
```
|
|
62
|
+
await router.navigate("dashboard", {}, { replace: true });
|
|
87
63
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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`
|
|
97
|
-
|
|
98
|
-
Checks if navigation would be allowed by guards. [Wiki](https://github.com/greydragon888/real-router/wiki/canNavigateTo)
|
|
99
|
-
|
|
100
|
-
---
|
|
64
|
+
// Cancellable navigation
|
|
65
|
+
const controller = new AbortController();
|
|
66
|
+
router.navigate("users", {}, { signal: controller.signal });
|
|
67
|
+
controller.abort();
|
|
68
|
+
```
|
|
101
69
|
|
|
102
70
|
### State
|
|
103
71
|
|
|
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)
|
|
72
|
+
| Method | Returns | Description |
|
|
73
|
+
| -------------------------------------------------- | -------------------- | ------------------------------------ |
|
|
74
|
+
| `getState()` | `State \| undefined` | Current router state (deeply frozen) |
|
|
75
|
+
| `getPreviousState()` | `State \| undefined` | Previous router state |
|
|
76
|
+
| `areStatesEqual(s1, s2, ignoreQP?)` | `boolean` | Compare two states |
|
|
77
|
+
| `isActiveRoute(name, params?, strict?, ignoreQP?)` | `boolean` | Check if route is active |
|
|
78
|
+
| `buildPath(name, params?)` | `string` | Build URL path from route name |
|
|
131
79
|
|
|
132
|
-
|
|
80
|
+
### Events & Plugins
|
|
133
81
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
Subscribes to successful transitions. [Wiki](https://github.com/greydragon888/real-router/wiki/subscribe)
|
|
82
|
+
| Method | Returns | Description |
|
|
83
|
+
| ----------------------- | ------------- | -------------------------------- |
|
|
84
|
+
| `subscribe(listener)` | `Unsubscribe` | Listen to successful transitions |
|
|
85
|
+
| `usePlugin(...plugins)` | `Unsubscribe` | Register plugin factories |
|
|
139
86
|
|
|
140
87
|
```typescript
|
|
141
88
|
const unsub = router.subscribe(({ route, previousRoute }) => {
|
|
142
|
-
console.log(previousRoute?.name, "
|
|
89
|
+
console.log(previousRoute?.name, "->", route.name);
|
|
143
90
|
});
|
|
144
91
|
```
|
|
145
92
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
### Plugins
|
|
149
|
-
|
|
150
|
-
#### `router.usePlugin(...plugins): Unsubscribe`
|
|
93
|
+
## Standalone API
|
|
151
94
|
|
|
152
|
-
|
|
95
|
+
Tree-shakeable functions imported from `@real-router/core/api`. Only imported functions are bundled.
|
|
153
96
|
|
|
154
97
|
```typescript
|
|
155
|
-
import {
|
|
156
|
-
|
|
157
|
-
|
|
98
|
+
import {
|
|
99
|
+
getRoutesApi,
|
|
100
|
+
getDependenciesApi,
|
|
101
|
+
getLifecycleApi,
|
|
102
|
+
getPluginApi,
|
|
103
|
+
cloneRouter,
|
|
104
|
+
} from "@real-router/core/api";
|
|
158
105
|
```
|
|
159
106
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
107
|
+
| Function | Purpose | Key methods |
|
|
108
|
+
| ---------------------------- | --------------------- | ---------------------------------------------------------------------------- |
|
|
109
|
+
| `getRoutesApi(router)` | Dynamic route CRUD | `add`, `remove`, `update`, `replace`, `has`, `get` |
|
|
110
|
+
| `getDependenciesApi(router)` | Dependency injection | `get`, `set`, `setAll`, `remove`, `has` |
|
|
111
|
+
| `getLifecycleApi(router)` | Guard registration | `addActivateGuard`, `addDeactivateGuard`, `remove*` |
|
|
112
|
+
| `getPluginApi(router)` | Plugin infrastructure | `makeState`, `matchPath`, `addInterceptor`, `extendRouter`, `getRouteConfig` |
|
|
113
|
+
| `cloneRouter(router, deps?)` | SSR cloning | Shares route definitions, independent state |
|
|
165
114
|
|
|
166
|
-
|
|
115
|
+
## Utilities
|
|
167
116
|
|
|
168
|
-
|
|
117
|
+
SSR helpers imported from `@real-router/core/utils`.
|
|
169
118
|
|
|
170
|
-
|
|
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
|
|
119
|
+
```typescript
|
|
120
|
+
import { serializeState } from "@real-router/core/utils";
|
|
185
121
|
|
|
186
|
-
|
|
122
|
+
const json = serializeState({ name: "home", path: "/" });
|
|
123
|
+
const html = `<script>window.__STATE__=${json}</script>`;
|
|
124
|
+
```
|
|
187
125
|
|
|
188
|
-
|
|
126
|
+
| Function | Purpose |
|
|
127
|
+
| ---------------------- | ----------------------------------------------------------------- |
|
|
128
|
+
| `serializeState(data)` | XSS-safe JSON serialization for embedding in HTML `<script>` tags |
|
|
189
129
|
|
|
190
|
-
### `getNavigator(router)`
|
|
130
|
+
### `getNavigator(router)` (main entry)
|
|
191
131
|
|
|
192
|
-
Frozen subset of router methods for view layers. Pre-bound, safe to destructure.
|
|
132
|
+
Frozen read-only subset of router methods for view layers. Pre-bound, safe to destructure. Imported from `@real-router/core`, not `/api`.
|
|
193
133
|
|
|
194
|
-
|
|
134
|
+
```typescript
|
|
135
|
+
import { getNavigator } from "@real-router/core";
|
|
136
|
+
```
|
|
195
137
|
|
|
196
|
-
|
|
138
|
+
```typescript
|
|
139
|
+
// Dynamic route management
|
|
140
|
+
const routes = getRoutesApi(router);
|
|
141
|
+
routes.add({ name: "settings", path: "/settings" });
|
|
142
|
+
routes.replace(newRoutes); // atomic HMR-safe replacement
|
|
143
|
+
|
|
144
|
+
// Dependency injection for guards and plugins
|
|
145
|
+
const deps = getDependenciesApi(router);
|
|
146
|
+
deps.set("authService", authService);
|
|
147
|
+
|
|
148
|
+
// Global lifecycle guards
|
|
149
|
+
const lifecycle = getLifecycleApi(router);
|
|
150
|
+
lifecycle.addActivateGuard("admin", (router, getDep) => (toState) => {
|
|
151
|
+
return getDep("authService").isAuthenticated();
|
|
152
|
+
});
|
|
197
153
|
|
|
198
|
-
|
|
154
|
+
// SSR — clone with request-scoped deps
|
|
155
|
+
const requestRouter = cloneRouter(router, { store: requestStore });
|
|
156
|
+
await requestRouter.start(req.url);
|
|
157
|
+
```
|
|
199
158
|
|
|
200
|
-
## Configuration
|
|
159
|
+
## Route Configuration
|
|
201
160
|
|
|
202
|
-
|
|
161
|
+
```typescript
|
|
162
|
+
import type { Route } from "@real-router/core";
|
|
203
163
|
|
|
204
|
-
|
|
164
|
+
const routes: Route[] = [
|
|
165
|
+
{
|
|
166
|
+
name: "admin",
|
|
167
|
+
path: "/admin",
|
|
168
|
+
canActivate: (router, getDep) => (toState, fromState, signal) => {
|
|
169
|
+
return getDep("authService").isAdmin();
|
|
170
|
+
},
|
|
171
|
+
children: [
|
|
172
|
+
{
|
|
173
|
+
name: "dashboard",
|
|
174
|
+
path: "/dashboard",
|
|
175
|
+
defaultParams: { tab: "overview" },
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "legacy",
|
|
181
|
+
path: "/old-path",
|
|
182
|
+
forwardTo: "home", // URL alias — guards on source are NOT executed
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: "product",
|
|
186
|
+
path: "/product/:id",
|
|
187
|
+
encodeParams: ({ id }) => ({ id: String(id) }),
|
|
188
|
+
decodeParams: ({ id }) => ({ id: Number(id) }),
|
|
189
|
+
},
|
|
190
|
+
];
|
|
191
|
+
```
|
|
205
192
|
|
|
206
193
|
## Error Handling
|
|
207
194
|
|
|
208
|
-
Navigation errors are instances of `RouterError
|
|
209
|
-
|
|
210
|
-
---
|
|
211
|
-
|
|
212
|
-
## Observable Support
|
|
195
|
+
Navigation errors are instances of `RouterError` with typed error codes:
|
|
213
196
|
|
|
214
|
-
|
|
215
|
-
|
|
197
|
+
```typescript
|
|
198
|
+
import { RouterError, errorCodes } from "@real-router/core";
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
await router.navigate("admin");
|
|
202
|
+
} catch (err) {
|
|
203
|
+
if (err instanceof RouterError) {
|
|
204
|
+
// err.code: ROUTE_NOT_FOUND | CANNOT_ACTIVATE | CANNOT_DEACTIVATE
|
|
205
|
+
// | TRANSITION_CANCELLED | SAME_STATES | DISPOSED | ...
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
216
209
|
|
|
217
|
-
|
|
210
|
+
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
211
|
|
|
219
212
|
## Documentation
|
|
220
213
|
|
|
221
|
-
Full documentation
|
|
214
|
+
Full documentation: [Wiki](https://github.com/greydragon888/real-router/wiki)
|
|
222
215
|
|
|
223
|
-
- [
|
|
224
|
-
- [
|
|
225
|
-
- [
|
|
226
|
-
- [
|
|
227
|
-
- [
|
|
228
|
-
- [Error Codes](https://github.com/greydragon888/real-router/wiki/error-codes)
|
|
216
|
+
- [Core Concepts](https://github.com/greydragon888/real-router/wiki/core-concepts) — overview and mental model
|
|
217
|
+
- [Defining Routes](https://github.com/greydragon888/real-router/wiki/Route) — nesting, path syntax, guards
|
|
218
|
+
- [Navigation Lifecycle](https://github.com/greydragon888/real-router/wiki/navigation-lifecycle) — transitions, guards, hooks
|
|
219
|
+
- [RouterOptions](https://github.com/greydragon888/real-router/wiki/RouterOptions) — `defaultRoute`, `trailingSlash`, `allowNotFound`, and more
|
|
220
|
+
- [Plugin Architecture](https://github.com/greydragon888/real-router/wiki/plugin-architecture) — interception, extension, events
|
|
229
221
|
- [Migration from router5](https://github.com/greydragon888/real-router/wiki/migration-guide)
|
|
230
222
|
|
|
231
|
-
---
|
|
232
|
-
|
|
233
223
|
## Related Packages
|
|
234
224
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
225
|
+
| Package | Description |
|
|
226
|
+
| ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
|
|
227
|
+
| [@real-router/react](https://www.npmjs.com/package/@real-router/react) | React integration (`RouterProvider`, hooks, `Link`, `RouteView`) |
|
|
228
|
+
| [@real-router/browser-plugin](https://www.npmjs.com/package/@real-router/browser-plugin) | Browser History API and URL synchronization |
|
|
229
|
+
| [@real-router/hash-plugin](https://www.npmjs.com/package/@real-router/hash-plugin) | Hash-based routing |
|
|
230
|
+
| [@real-router/rx](https://www.npmjs.com/package/@real-router/rx) | Observable API (`state$`, `events$`, TC39 Observable) |
|
|
231
|
+
| [@real-router/logger-plugin](https://www.npmjs.com/package/@real-router/logger-plugin) | Development logging |
|
|
232
|
+
| [@real-router/persistent-params-plugin](https://www.npmjs.com/package/@real-router/persistent-params-plugin) | Parameter persistence |
|
|
233
|
+
| [@real-router/route-utils](https://www.npmjs.com/package/@real-router/route-utils) | Route tree queries and segment testing |
|
|
234
|
+
|
|
235
|
+
## Contributing
|
|
236
|
+
|
|
237
|
+
See [contributing guidelines](../../CONTRIBUTING.md) for development setup and PR process.
|
|
241
238
|
|
|
242
239
|
## License
|
|
243
240
|
|
|
244
|
-
MIT © [Oleg Ivanov](https://github.com/greydragon888)
|
|
241
|
+
[MIT](../../LICENSE) © [Oleg Ivanov](https://github.com/greydragon888)
|