@real-router/core 0.36.2 → 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 CHANGED
@@ -1,244 +1,220 @@
1
1
  # @real-router/core
2
2
 
3
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
3
+ [![npm](https://img.shields.io/npm/v/@real-router/core.svg?style=flat-square)](https://www.npmjs.com/package/@real-router/core)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@real-router/core.svg?style=flat-square)](https://www.npmjs.com/package/@real-router/core)
5
+ [![bundle size](https://deno.bundlejs.com/?q=@real-router/core&treeshake=[{createRouter}]&badge=detailed)](https://bundlejs.com/?q=@real-router/core&treeshake=[{createRouter}])
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](../../LICENSE)
5
7
 
6
- Core router implementation for Real-Router.
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: "users",
29
- path: "/users",
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
- #### `router.start(path): Promise<State>`
60
-
61
- Starts the router with an initial path. [Wiki](https://github.com/greydragon888/real-router/wiki/start)
62
-
63
- #### `router.stop(): this`
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
- #### `router.navigate(name, params?, options?): Promise<State>`
80
-
81
- Navigates to a route by name. Supports AbortController cancellation. Fire-and-forget safe. [Wiki](https://github.com/greydragon888/real-router/wiki/navigate)
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("users", {}, { replace: true });
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
- Checks if navigation would be allowed by guards. [Wiki](https://github.com/greydragon888/real-router/wiki/canNavigateTo)
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
- #### `router.getState(): State | undefined`
105
-
106
- Returns the current router state. [Wiki](https://github.com/greydragon888/real-router/wiki/getState)
107
-
108
- #### `router.getPreviousState(): State | undefined`
109
-
110
- Returns the previous router state. [Wiki](https://github.com/greydragon888/real-router/wiki/getPreviousState)
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
- #### `router.subscribe(listener): Unsubscribe`
137
-
138
- Subscribes to successful transitions. [Wiki](https://github.com/greydragon888/real-router/wiki/subscribe)
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, "", route.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
- Registers one or more plugins. [Wiki](https://github.com/greydragon888/real-router/wiki/usePlugin)
93
+ Tree-shakeable functions imported from `@real-router/core/api`. Only imported functions are bundled.
153
94
 
154
95
  ```typescript
155
- import { browserPluginFactory } from "@real-router/browser-plugin";
156
-
157
- router.usePlugin(browserPluginFactory());
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
- ## Standalone API
163
-
164
- Tree-shakeable functions for domain-specific operations.
165
-
166
- ### `getRoutesApi(router)` Route Management
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)` Navigator
113
+ ### `getNavigator(router)` (main entry)
191
114
 
192
- Frozen subset of router methods for view layers. Pre-bound, safe to destructure. [Wiki](https://github.com/greydragon888/real-router/wiki/getNavigator)
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
- ### `cloneRouter(router, deps?)` — SSR Cloning
117
+ ```typescript
118
+ import { getNavigator } from "@real-router/core";
119
+ ```
195
120
 
196
- Clone router for server-side rendering. [Wiki](https://github.com/greydragon888/real-router/wiki/cloneRouter)
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
- See [RouterOptions](https://github.com/greydragon888/real-router/wiki/RouterOptions) for all available options.
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`. See [RouterError](https://github.com/greydragon888/real-router/wiki/RouterError) and [Error Codes](https://github.com/greydragon888/real-router/wiki/error-codes) for details.
209
-
210
- ---
211
-
212
- ## Observable Support
174
+ Navigation errors are instances of `RouterError` with typed error codes:
213
175
 
214
- > Observable API has been moved to `@real-router/rx` package for zero bundle cost.
215
- > See [@real-router/rx](../rx/README.md) for reactive stream APIs.
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 available on the [Wiki](https://github.com/greydragon888/real-router/wiki):
193
+ Full documentation: [Wiki](https://github.com/greydragon888/real-router/wiki)
222
194
 
223
- - [Creating a Router](https://github.com/greydragon888/real-router/wiki/createRouter)
224
- - [Navigation](https://github.com/greydragon888/real-router/wiki/navigate)
225
- - [State](https://github.com/greydragon888/real-router/wiki/getState)
226
- - [Guards](https://github.com/greydragon888/real-router/wiki/getLifecycleApi)
227
- - [Plugins](https://github.com/greydragon888/real-router/wiki/usePlugin)
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
- - [@real-router/react](https://www.npmjs.com/package/@real-router/react) React integration
236
- - [@real-router/browser-plugin](https://www.npmjs.com/package/@real-router/browser-plugin) — Browser history
237
- - [@real-router/hash-plugin](https://www.npmjs.com/package/@real-router/hash-plugin) Hash-based routing
238
- - [@real-router/logger-plugin](https://www.npmjs.com/package/@real-router/logger-plugin) Debug logging
239
- - [@real-router/persistent-params-plugin](https://www.npmjs.com/package/@real-router/persistent-params-plugin) Persistent params
240
- - [@real-router/route-utils](https://www.npmjs.com/package/@real-router/route-utils) Route tree queries and segment testing utilities
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)