@vobs/router 0.3.0 → 1.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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 vobsjs
3
+ Copyright (c) 2026 vobs contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # @vobs/router
2
+
3
+ Signals-first client router for Vobs with guards, route loaders, and cancellation of superseded navigations.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @vobs/router
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```ts
14
+ import { createVobs, createText } from '@vobs/vobs'
15
+ import { createRouter, createMemoryHistory, RouterView, routerPlugin } from '@vobs/router'
16
+
17
+ const router = createRouter({
18
+ history: createMemoryHistory('/'),
19
+ routes: [
20
+ { path: '/', name: 'home', component: () => createText('home') },
21
+ { path: '/users/:id', name: 'user', component: () => createText('user') }
22
+ ]
23
+ })
24
+
25
+ // Guards return undefined (allow), false (cancel), or a redirect target.
26
+ router.beforeEach(to => (to.path.startsWith('/admin') ? '/login' : undefined))
27
+
28
+ const app = createVobs({
29
+ render: () => RouterView({ loading: () => createText('loading'), notFound: () => createText('not-found') }),
30
+ plugins: [routerPlugin({ router })]
31
+ })
32
+
33
+ await router.push({ name: 'user', params: { id: '7' }, query: { tab: 'activity' } })
34
+ router.currentRoute.value.fullPath // '/users/7?tab=activity'
35
+ ```
36
+
37
+ ## API
38
+
39
+ | Signature | Description |
40
+ | --- | --- |
41
+ | `createRouter(options: RouterOptions): Router` | Creates a router; defaults to browser history in the DOM and memory history otherwise. |
42
+ | `router.currentRoute: Signal<RouteLocation>` | Reactive current route with `path`, `params`, `query`, `hash`, `meta`, and `matched` records. |
43
+ | `router.push(to: RouteTarget)` | Navigates and resolves to the new `RouteLocation`, or `false` when a guard cancels. |
44
+ | `router.replace(to: RouteTarget)` | Like `push` but replaces the history entry. |
45
+ | `router.resolve(to: RouteTarget): RouteLocation` | Resolves a target to a location without navigating. |
46
+ | `router.back(): void` | Goes back in history. |
47
+ | `router.beforeEach(guard: NavigationGuard): () => void` | Registers a global guard and returns its unregister function. |
48
+ | `router.getViewState(route: RouteLocation): RouterViewState` | Resolves `ready`, `loading`, `error`, or `not-found` for the matched components. |
49
+ | `router.devtools: RouterDevToolsAPI` | Navigation traces, data-request tracking, errors, metrics, and revalidation. |
50
+ | `router.destroy(): void` | Stops history listening and disposes internal signals. |
51
+ | `routerPlugin(options?: RouterPluginOptions): VobsPlugin` | Provides the router through `ROUTER_KEY`. |
52
+ | `RouterView(props?: RouterViewProps): VobsNode` | Renders the matched component with nested layouts plus `loading`, `notFound`, and `error` slots. |
53
+ | `useRouter(): Router` | Injects the router inside components. |
54
+ | `useRoute(): Signal<RouteLocation>` | Injects the current-route signal. |
55
+ | `lazy(loader: RouteComponentLoader): LazyRouteComponent` | Wraps a dynamic import as a lazy route component. |
56
+ | `createMemoryHistory(initial?): RouterHistory` | In-memory history for tests and SSR. |
57
+ | `createBrowserHistory(base?): RouterHistory` | History API adapter with `popstate` support. |
58
+
59
+ Guards and route `loader`s run before a navigation commits. A newer navigation cancels the pending one: its promise rejects with `NavigationCancelledError`, and a late loader result cannot overwrite the current route or history. Guard redirects are capped at 10 hops (`NavigationRedirectError`).
60
+
61
+ ## Types
62
+
63
+ `RouteLocation`, `RouteRecord`, `RouteComponent`, `RouteComponentProps`, `RouteComponentModule`, `RouteComponentLoader`, `RouteComponentDefinition`, `LazyRouteComponent`, `RouteLoader`, `RouteLoaderContext`, `NavigationGuard`, `NavigationGuardResult`, `RouteTarget`, `RouteLocationRaw`, `RouteQueryInput`, `RouteParams`, `RouteQuery`, `RouteQueryValue`, `RouteMeta`, `RouterOptions`, `RouterHistory`, `RouterViewState`, `Router`, `RouterViewProps`, `RouterPluginOptions`, `NavigationState`, `NavigationTrace`, `RouteErrorTrace`, `NavigationCancelledError`, `NavigationRedirectError`, `RouterPerformanceMetrics`, `RouterDevToolsAPI`, `RouterDevToolsEvent`, `RouterDataRequestKind`, `RouterDataRequestTrace`, `RouterDataRequestOptions`, `RouteDebugNode`, `RouterDebugEvent`, `RouterDebugEventType`
package/package.json CHANGED
@@ -1,44 +1,21 @@
1
1
  {
2
- "name": "@vobs/router",
3
- "version": "0.3.0",
4
- "description": "Client and server route resolution for vobs pages.",
5
- "type": "module",
6
- "publishConfig": {
7
- "access": "public"
8
- },
9
2
  "license": "MIT",
10
- "author": "vobsjs",
11
- "repository": {
12
- "type": "git",
13
- "url": "git+https://github.com/vobsjs/vobs.git",
14
- "directory": "packages/features/router"
15
- },
16
- "bugs": {
17
- "url": "https://github.com/vobsjs/vobs/issues"
18
- },
19
- "homepage": "https://github.com/vobsjs/vobs#readme",
20
- "dependencies": {
21
- "@vobs/reactivity": "0.3.0",
22
- "@vobs/runtime-core": "0.3.0"
23
- },
24
3
  "files": [
25
- "dist"
4
+ "src",
5
+ "README.md",
6
+ "LICENSE"
26
7
  ],
8
+ "name": "@vobs/router",
9
+ "version": "1.1.0",
10
+ "type": "module",
11
+ "main": "src/index.ts",
12
+ "types": "src/index.ts",
27
13
  "exports": {
28
- ".": {
29
- "types": "./dist/index.d.ts",
30
- "import": "./dist/index.js"
31
- },
32
- "./package.json": "./package.json"
14
+ ".": "./src/index.ts"
33
15
  },
34
- "types": "./dist/index.d.ts",
35
- "module": "./dist/index.js",
36
- "main": "./dist/index.js",
37
- "sideEffects": false,
38
- "devDependencies": {
39
- "@vobs/resource": "0.3.0"
40
- },
41
- "engines": {
42
- "node": ">=22.12.0"
16
+ "dependencies": {
17
+ "@vobs/reactivity": "1.1.0",
18
+ "@vobs/vobs": "1.1.0",
19
+ "@vobs/runtime": "1.1.0"
43
20
  }
44
21
  }
package/src/debug.ts ADDED
@@ -0,0 +1,41 @@
1
+ import type { RuntimeDebugContext } from '@vobs/runtime'
2
+
3
+ export type RouterDebugEventType =
4
+ | 'navigation:start'
5
+ | 'navigation:end'
6
+ | 'route:update'
7
+ | 'data-request'
8
+ | 'error'
9
+
10
+ export interface RouterDebugEvent {
11
+ readonly routerId: string
12
+ readonly type: RouterDebugEventType
13
+ readonly payload: unknown
14
+ readonly context?: RuntimeDebugContext
15
+ }
16
+
17
+ type RouterDebugListener = (event: RouterDebugEvent) => void
18
+
19
+ const listeners = new Set<RouterDebugListener>()
20
+ let nextRouterId = 1
21
+
22
+ export function createRouterDebugId(): string {
23
+ return `router-${nextRouterId++}`
24
+ }
25
+
26
+ export function subscribeRouterDebug(listener: RouterDebugListener): () => void {
27
+ listeners.add(listener)
28
+ return () => listeners.delete(listener)
29
+ }
30
+
31
+ export function emitRouterDebug(
32
+ routerId: string,
33
+ type: RouterDebugEventType,
34
+ payload: unknown,
35
+ context?: RuntimeDebugContext
36
+ ): void {
37
+ const event: RouterDebugEvent = { routerId, type, payload, context }
38
+ for (const listener of [...listeners]) {
39
+ try { listener(event) } catch { /* diagnostics must not affect navigation */ }
40
+ }
41
+ }