vue-micro-router 1.0.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Danh Nguyen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,617 @@
1
+ # vue-micro-router
2
+
3
+ **Mobile-app-style navigation for Vue 3 — no URL paths, just screens.**
4
+
5
+ Build apps that feel like native mobile — pages slide in/out with smooth transitions, dialogs stack as modals, and persistent HUD controls float above everything. No `/users/:id` URL routing. Just `push('profile')` and watch it animate.
6
+
7
+ ### Why not vue-router?
8
+
9
+ | | vue-router | vue-micro-router |
10
+ |---|---|---|
11
+ | Navigation model | URL-based (`/path/:param`) | Segment stack (`home → home/menu → home/menu/settings`) |
12
+ | Page transitions | Manual (TransitionGroup) | Built-in slide/fade animations + per-route customization |
13
+ | Multiple visible pages | No (one route = one view) | Yes — stacked pages all render simultaneously |
14
+ | Modal dialogs | DIY | First-class with stacking, backdrop, focus trap |
15
+ | GUI overlays / HUD | DIY | First-class controls with auto-show/hide |
16
+ | Route guards | `beforeRouteEnter` etc. | `beforeEach`, `beforeEnter`, `beforeLeave` + async |
17
+ | State passing | Query params / route params | Reactive `useMicroState()` bridge |
18
+ | Navigation history | Browser history API | Built-in `canGoBack` / `historyBack()` |
19
+ | Gesture navigation | None | Swipe-back from left edge |
20
+ | Type safety | Route params typing | Auto-typed via `Register` + `useMicroRouter()` |
21
+ | State persistence | None | `serialize()` / `restore()` |
22
+ | Nested routers | Nested `<router-view>` | Independent `<MicroRouterView nested>` |
23
+ | Lifecycle hooks | `beforeRouteEnter` etc. | `onRouteEnter`, `onDialogEnter`, `onControlEnter` |
24
+ | Use case | Websites, SPAs | Games, mobile-style apps, kiosks, wizards |
25
+
26
+ ### How it works
27
+
28
+ Pages stack as path segments — `push('menu')` slides a new page on top. `push(-1)` slides it back. No browser URL changes. Just screens animating like a native app.
29
+
30
+ ```
31
+ push('menu') → [home] ← [menu slides in]
32
+ push('settings') → [home] [menu] ← [settings slides in]
33
+ push(-1) → [home] [menu slides out →]
34
+ ```
35
+
36
+ Dialogs and controls layer on top — independently managed with their own lifecycle, stacking, and transitions.
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ bun add vue-micro-router
42
+ # or
43
+ npm install vue-micro-router
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ```vue
49
+ <!-- app-plugin.ts — declare once, typed everywhere -->
50
+ <script lang="ts">
51
+ import { defineFeaturePlugin } from 'vue-micro-router';
52
+ import HomePage from './pages/HomePage.vue';
53
+ import MenuPage from './pages/MenuPage.vue';
54
+ import SettingsPage from './pages/SettingsPage.vue';
55
+ import ConfirmDialog from './dialogs/ConfirmDialog.vue';
56
+ import MainGUI from './controls/MainGUI.vue';
57
+
58
+ export const appPlugin = defineFeaturePlugin({
59
+ name: 'app',
60
+ routes: [
61
+ { path: 'home', component: HomePage },
62
+ { path: 'menu', component: MenuPage, transition: 'fade', transitionDuration: 300 },
63
+ { path: 'settings', component: SettingsPage, preload: 'adjacent',
64
+ beforeLeave: () => confirm('Leave settings?') },
65
+ ],
66
+ dialogs: [
67
+ { path: 'confirm', component: ConfirmDialog, activated: false },
68
+ ],
69
+ controls: [
70
+ { name: 'main_gui', component: MainGUI, activated: false },
71
+ ],
72
+ } as const);
73
+
74
+ // Register plugin type globally — no generics needed in useMicroRouter()
75
+ declare module 'vue-micro-router' {
76
+ interface Register {
77
+ plugin: typeof appPlugin;
78
+ }
79
+ }
80
+ </script>
81
+ ```
82
+
83
+ ```vue
84
+ <!-- App.vue — fully typed without generics -->
85
+ <script setup>
86
+ import { MicroRouterView } from 'vue-micro-router';
87
+ import 'vue-micro-router/styles';
88
+ import { appPlugin } from './app-plugin';
89
+
90
+ const config = {
91
+ defaultPath: 'home',
92
+ defaultControlName: 'main_gui',
93
+ history: { enabled: true, maxEntries: 50 },
94
+ gesture: { enabled: true },
95
+ guards: {
96
+ beforeEach: [(to, from) => {
97
+ console.log(`Navigating: ${from} → ${to}`);
98
+ return true;
99
+ }],
100
+ },
101
+ };
102
+ </script>
103
+
104
+ <template>
105
+ <MicroRouterView :config :plugins="[appPlugin]" />
106
+ </template>
107
+ ```
108
+
109
+ ## Core Concepts
110
+
111
+ ### Segment-Based Paths
112
+
113
+ Pages stack as path segments. `"home/menu/settings"` renders 3 pages simultaneously — each page slides in on top of the previous one.
114
+
115
+ ```ts
116
+ const { push } = useMicroRouter();
117
+
118
+ // Push a new page on top
119
+ await push('menu'); // home → home/menu
120
+
121
+ // Push with absolute path
122
+ await push('/home/settings'); // → home/settings
123
+
124
+ // Go back one step
125
+ await push(-1); // home/settings → home
126
+
127
+ // Go back with props (forces remount)
128
+ await push(-1, { reset: true });
129
+ ```
130
+
131
+ ### Navigation with Props
132
+
133
+ Pass data to pages via `push()` and read it with `useMicroState()`:
134
+
135
+ ```ts
136
+ // Parent — push with props
137
+ await push('profile', { userId: 42 });
138
+ ```
139
+
140
+ ```vue
141
+ <!-- ProfilePage.vue -->
142
+ <script setup>
143
+ import { useMicroState } from 'vue-micro-router';
144
+
145
+ // Read props passed via push() — auto-syncs mutations back to store
146
+ const { userId } = useMicroState<{ userId: number }>();
147
+
148
+ // With defaults for optional props
149
+ const { tab } = useMicroState({ tab: 'overview' });
150
+ </script>
151
+ ```
152
+
153
+ ### Route Guards
154
+
155
+ Prevent or control navigation with sync/async guards:
156
+
157
+ ```ts
158
+ // Global guards — run on every navigation
159
+ const config = {
160
+ guards: {
161
+ beforeEach: [
162
+ async (to, from) => {
163
+ if (to.includes('admin') && !isAuthenticated()) return false;
164
+ return true;
165
+ }
166
+ ],
167
+ afterEach: [(to, from) => analytics.track('navigate', { to, from })],
168
+ }
169
+ };
170
+
171
+ // Per-route guards — on specific routes
172
+ defineFeaturePlugin({
173
+ routes: [
174
+ {
175
+ path: 'admin',
176
+ component: AdminPage,
177
+ beforeEnter: async (to, from) => {
178
+ const user = await fetchUser();
179
+ return user.isAdmin;
180
+ },
181
+ },
182
+ {
183
+ path: 'editor',
184
+ component: EditorPage,
185
+ beforeLeave: (to, from) => {
186
+ if (hasUnsavedChanges()) return confirm('Discard changes?');
187
+ return true;
188
+ },
189
+ },
190
+ ],
191
+ });
192
+ ```
193
+
194
+ Guard execution order: `global beforeEach → target beforeEnter → source beforeLeave → global afterEach`
195
+
196
+ Guards have a 5s timeout — if a guard doesn't resolve, navigation is cancelled.
197
+
198
+ ### Per-Route Transitions
199
+
200
+ Each route can have its own transition style and duration:
201
+
202
+ ```ts
203
+ defineFeaturePlugin({
204
+ routes: [
205
+ { path: 'home', component: HomePage }, // default: slide 500ms
206
+ { path: 'settings', component: SettingsPage, transition: 'fade', transitionDuration: 300 },
207
+ { path: 'modal-page', component: ModalPage, transition: 'none' }, // instant, no animation
208
+ ],
209
+ });
210
+ ```
211
+
212
+ Supported: `'slide'` (default), `'fade'`, `'none'`
213
+
214
+ ### Navigation History
215
+
216
+ Opt-in browser-like history with back/forward:
217
+
218
+ ```ts
219
+ const config = {
220
+ history: { enabled: true, maxEntries: 50 },
221
+ };
222
+
223
+ // In any component:
224
+ const router = useMicroRouter();
225
+ router.canGoBack?.value; // reactive boolean
226
+ router.canGoForward?.value; // reactive boolean
227
+ await router.historyBack?.();
228
+ await router.historyForward?.();
229
+ await router.historyGo?.(-2); // go back 2 entries
230
+ ```
231
+
232
+ History truncates forward entries on new push (browser behavior).
233
+
234
+ ### Type-Safe Routes
235
+
236
+ #### Register Pattern (Recommended)
237
+
238
+ Use `Register` module augmentation to declare your plugin type once. Then `useMicroRouter()` auto-infers everywhere — no generics needed.
239
+
240
+ ```ts
241
+ // app-plugin.ts — declare once with `as const`
242
+ export const appPlugin = defineFeaturePlugin({
243
+ name: 'my-app',
244
+ routes: [
245
+ { path: 'home', component: HomePage },
246
+ { path: 'profile', component: ProfilePage },
247
+ ],
248
+ dialogs: [{ path: 'confirm', component: ConfirmDialog, activated: false }],
249
+ controls: [{ name: 'main_hud', component: MainHUD, activated: false }],
250
+ } as const);
251
+
252
+ // Declare module once — this is the ONLY place you write the type
253
+ declare module 'vue-micro-router' {
254
+ interface Register {
255
+ plugin: typeof appPlugin;
256
+ }
257
+ }
258
+ ```
259
+
260
+ Then everywhere:
261
+
262
+ ```ts
263
+ // Any component — fully typed, zero generics
264
+ const { push, openDialog, toggleControl } = useMicroRouter();
265
+ push('profile'); // ✅ OK
266
+ push('typo'); // ❌ TS Error
267
+ openDialog('confirm'); // ✅ OK
268
+ toggleControl('main_hud', true); // ✅ OK
269
+ ```
270
+
271
+ **Benefits:** Type-safe push/openDialog/closeDialog/toggleControl. No duplication. One declaration fixes all usages.
272
+
273
+ #### Manual Route Map (Alternative)
274
+
275
+ For simple cases or when you only need typed props (not route names):
276
+
277
+ ```ts
278
+ interface AppRoutes {
279
+ home: undefined;
280
+ profile: { userId: number };
281
+ }
282
+
283
+ const router = useMicroRouter<AppRoutes>();
284
+ router.push('profile', { userId: 42 }); // ✅ OK
285
+ router.push('profile'); // ❌ TS Error: missing props
286
+ router.push('unknown'); // ❌ TS Error: unknown route
287
+ ```
288
+
289
+ **Untyped:** `useMicroRouter()` without Register returns `MicroRouterStore` with untyped methods.
290
+
291
+ ### State Serialization
292
+
293
+ Save and restore full router state for session persistence:
294
+
295
+ ```ts
296
+ const router = useMicroRouter();
297
+
298
+ // Save state (e.g., on page hide)
299
+ const snapshot = router.serialize!();
300
+ localStorage.setItem('router-state', JSON.stringify(snapshot));
301
+
302
+ // Restore state (e.g., on page load)
303
+ const saved = localStorage.getItem('router-state');
304
+ if (saved) await router.restore!(JSON.parse(saved));
305
+ ```
306
+
307
+ Serializes: navigation path + attrs, dialog stack + attrs, control state + attrs.
308
+
309
+ ### Route Preloading
310
+
311
+ Preload async route components before they're needed:
312
+
313
+ ```ts
314
+ defineFeaturePlugin({
315
+ routes: [
316
+ { path: 'home', component: () => import('./Home.vue') },
317
+ { path: 'shop', component: () => import('./Shop.vue'), preload: 'eager' }, // loads on mount
318
+ { path: 'cart', component: () => import('./Cart.vue'), preload: 'adjacent' }, // loads after each nav
319
+ ],
320
+ });
321
+
322
+ // Manual preload
323
+ await router.preloadRoute('cart');
324
+ ```
325
+
326
+ ### Gesture Navigation (Swipe Back)
327
+
328
+ iOS-style swipe-back from the left edge:
329
+
330
+ ```ts
331
+ const config = {
332
+ gesture: {
333
+ enabled: true,
334
+ edgeWidth: 20, // px from left edge (default: 20)
335
+ threshold: 0.3, // 30% screen width to trigger (default: 0.3)
336
+ velocityThreshold: 0.5, // px/ms fast swipe (default: 0.5)
337
+ },
338
+ };
339
+ ```
340
+
341
+ ### Nested Routers
342
+
343
+ Independent router instances within the same component tree:
344
+
345
+ ```vue
346
+ <template>
347
+ <!-- Root router -->
348
+ <MicroRouterView :config="rootConfig" :plugins="[mainPlugin]">
349
+ <!-- Inside a page component: -->
350
+ <MicroRouterView nested :config="tabConfig" :plugins="[tabPlugin]" />
351
+ </MicroRouterView>
352
+ </template>
353
+ ```
354
+
355
+ ```ts
356
+ // Access root router from within nested router
357
+ const rootRouter = useMicroRouter({ root: true });
358
+ const localRouter = useMicroRouter(); // nearest parent
359
+ ```
360
+
361
+ ### Step-Wise Navigation
362
+
363
+ Animate through intermediate pages one-by-one:
364
+
365
+ ```ts
366
+ const { stepWisePush, stepWiseBack } = useMicroRouter();
367
+
368
+ // Walk through: home → home/onboarding → home/onboarding/step1
369
+ await stepWisePush('/home/onboarding/step1');
370
+
371
+ // Step back through each page with animation
372
+ await stepWiseBack(3);
373
+ ```
374
+
375
+ ### Dialogs
376
+
377
+ Open modal dialogs with props and handle close:
378
+
379
+ ```ts
380
+ const { openDialog, closeDialog, closeAllDialogs } = useMicroRouter();
381
+
382
+ // Open with props
383
+ openDialog('confirm', {
384
+ title: 'Delete item?',
385
+ onConfirm: () => handleDelete(),
386
+ });
387
+
388
+ // Close specific dialog
389
+ closeDialog('confirm');
390
+
391
+ // Close all open dialogs
392
+ closeAllDialogs();
393
+ ```
394
+
395
+ Dialog options:
396
+
397
+ ```ts
398
+ registerDialog({
399
+ path: 'settings-modal',
400
+ component: SettingsModal,
401
+ activated: false,
402
+ position: 'right', // 'standard' | 'top' | 'right' | 'bottom' | 'left'
403
+ transition: 'slide', // 'fade' | 'slide' | 'scale'
404
+ transitionDuration: 400,
405
+ fullscreen: false,
406
+ persistent: true, // prevent close on backdrop click / Escape
407
+ seamless: true, // transparent background, no shadow
408
+ });
409
+ ```
410
+
411
+ ### GUI Controls
412
+
413
+ Persistent overlay controls (HUDs, toolbars) that auto-manage visibility:
414
+
415
+ ```ts
416
+ const { toggleControl } = useMicroRouter();
417
+
418
+ // Show inventory HUD (auto-hides default main_gui)
419
+ toggleControl('inventory', true, { filter: 'weapons' });
420
+
421
+ // Hide inventory (auto-restores main_gui)
422
+ toggleControl('inventory', false);
423
+ ```
424
+
425
+ ### Lifecycle Hooks
426
+
427
+ iOS-style `viewWillAppear` / `viewWillDisappear` — available for routes, dialogs, and controls:
428
+
429
+ ```vue
430
+ <script setup>
431
+ import { useRouteLifecycle } from 'vue-micro-router';
432
+
433
+ useRouteLifecycle({
434
+ onRouteEnter: () => console.log('Page is now the active (top) page'),
435
+ onRouteLeave: () => console.log('Page is no longer the active page'),
436
+ });
437
+ </script>
438
+ ```
439
+
440
+ Also: `useDialogLifecycle({ onDialogEnter, onDialogLeave })` and `useControlLifecycle({ onControlEnter, onControlLeave })`.
441
+
442
+ ### Feature Plugins
443
+
444
+ Bundle routes, dialogs, and controls into feature modules:
445
+
446
+ ```ts
447
+ import { defineFeaturePlugin } from 'vue-micro-router';
448
+
449
+ export const shopPlugin = defineFeaturePlugin({
450
+ name: 'shop',
451
+ routes: [
452
+ { path: 'shop', component: () => import('./ShopPage.vue'), preload: 'eager' },
453
+ { path: 'cart', component: () => import('./CartPage.vue'), preload: 'adjacent' },
454
+ ],
455
+ dialogs: [
456
+ { path: 'buy-confirm', component: () => import('./BuyConfirm.vue'), activated: false },
457
+ ],
458
+ controls: [
459
+ { name: 'shop_hud', component: () => import('./ShopHUD.vue'), activated: false },
460
+ ],
461
+ });
462
+ ```
463
+
464
+ ### Analytics / Page Tracking
465
+
466
+ Hook into navigation events:
467
+
468
+ ```ts
469
+ const config: MicroRouterConfig = {
470
+ tracker: {
471
+ trackPageEnter: (page, from, to) => analytics.track('page_view', { page }),
472
+ trackPageLeave: (page, from, to) => analytics.track('page_leave', { page }),
473
+ trackDialogEnter: (dialog) => analytics.track('dialog_open', { dialog }),
474
+ trackDialogLeave: (dialog) => analytics.track('dialog_close', { dialog }),
475
+ trackGuiEnter: (name) => analytics.track('gui_show', { name }),
476
+ trackGuiLeave: (name) => analytics.track('gui_hide', { name }),
477
+ },
478
+ };
479
+ ```
480
+
481
+ ### Vue Devtools
482
+
483
+ Automatic in development — shows a "Micro Router" inspector tab with:
484
+ - Current path and page stack
485
+ - Open dialogs with attrs
486
+ - Active controls
487
+ - Navigation timeline events
488
+
489
+ Requires `@vue/devtools-api` (optional peer dependency). Zero cost in production builds.
490
+
491
+ ## Optional: Audio Manager
492
+
493
+ Background music tied to route BGM fields. Supports custom audio backends via `AudioAdapter`:
494
+
495
+ ```bash
496
+ bun add howler
497
+ ```
498
+
499
+ ```ts
500
+ import { useAudioManager, HowlerAdapter } from 'vue-micro-router/audio';
501
+
502
+ const audio = useAudioManager({
503
+ volumeRef: ref(80),
504
+ urlResolver: (name) => `/assets/audio/${name}.mp3`,
505
+ // adapter: new HowlerAdapter(), // default — or provide your own AudioAdapter
506
+ });
507
+ ```
508
+
509
+ Custom adapter:
510
+
511
+ ```ts
512
+ import type { AudioAdapter } from 'vue-micro-router/audio';
513
+
514
+ class WebAudioAdapter implements AudioAdapter {
515
+ async play(src, options) { /* Web Audio API */ }
516
+ stop() { /* ... */ }
517
+ pause() { /* ... */ }
518
+ resume() { /* ... */ }
519
+ fade(from, to, duration) { /* ... */ }
520
+ isPlaying() { return false; }
521
+ state() { return 'unloaded'; }
522
+ cleanup() { /* ... */ }
523
+ }
524
+
525
+ const audio = useAudioManager({ adapter: new WebAudioAdapter() });
526
+ ```
527
+
528
+ ## Styles
529
+
530
+ Import styles separately (not bundled with JS):
531
+
532
+ ```ts
533
+ import 'vue-micro-router/styles';
534
+ ```
535
+
536
+ Includes page slide/fade transitions, dialog animations, control fade transitions, and GUI layer positioning.
537
+
538
+ ## Development
539
+
540
+ ```bash
541
+ bun run lint # ESLint check
542
+ bun run lint:fix # Auto-fix lint issues
543
+ bun test # Run all tests
544
+ bun run typecheck # TypeScript strict check
545
+ bun run build # Build package
546
+ bun run dev:example # Run example app
547
+ ```
548
+
549
+ ## API Reference
550
+
551
+ ### Composables
552
+
553
+ | Composable | Description |
554
+ |-----------|-------------|
555
+ | `useGlobalMicroRouter(config?)` | Create & provide store. Call once in root. |
556
+ | `useMicroRouter(options?)` | Inject store. Pass `{ root: true }` for root in nested setups. |
557
+ | `useMicroRouter()` | Auto-typed via `Register` augmentation. Explicit `<T>` generic also supported. |
558
+ | `useMicroState<T>(defaults?)` | Reactive attrs bridge — read/write props in routes, dialogs, controls. |
559
+ | `useRouteLifecycle(hooks)` | `onRouteEnter` / `onRouteLeave` — fires when page becomes/stops being top. |
560
+ | `useDialogLifecycle(hooks)` | `onDialogEnter` / `onDialogLeave` — fires when dialog becomes/stops being topmost. |
561
+ | `useControlLifecycle(hooks)` | `onControlEnter` / `onControlLeave` — fires when control activates/deactivates. |
562
+ | `usePageTracker(hooks?)` | Normalize tracker hooks with no-op fallbacks. |
563
+ | `useNavigation(config?, tracker?)` | Low-level page navigation (used internally). |
564
+ | `useDialogManager(tracker?)` | Low-level dialog management (used internally). |
565
+ | `useControlManager(config?, tracker?)` | Low-level control management (used internally). |
566
+ | `useGestureNavigation(config, ctx)` | Swipe-back gesture handler (used internally by MicroRouterView). |
567
+
568
+ ### Components
569
+
570
+ | Component | Description |
571
+ |-----------|-------------|
572
+ | `<MicroRouterView>` | Root wrapper — renders pages, dialogs, controls. Accepts `nested` prop. |
573
+ | `<RoutePage>` | Page slot wrapper — provides attrs injection. |
574
+ | `<MicroDialogComponent>` | Headless native `<dialog>` — focus trap, backdrop, escape key. |
575
+ | `<MicroControlWrapper>` | Control slot wrapper — provides attrs injection. |
576
+
577
+ ### Plugin Helpers
578
+
579
+ | Function | Description |
580
+ |----------|-------------|
581
+ | `defineFeaturePlugin(config)` | Create a feature plugin bundle. |
582
+ | `registerFeaturePlugins(plugins, store)` | Register all plugins with the store. |
583
+
584
+ ### Types
585
+
586
+ | Type | Description |
587
+ |------|-------------|
588
+ | `NavigationGuard` | `(to, from) => boolean \| Promise<boolean>` |
589
+ | `NavigationAfterHook` | `(to, from) => void` |
590
+ | `RouteMap` | `Record<string, Record<string, unknown> \| undefined>` |
591
+ | `TypedPush<T>` | Type-safe push overloads for a RouteMap |
592
+ | `SerializedState` | JSON-serializable router state snapshot |
593
+ | `AudioAdapter` | Abstract audio playback interface |
594
+ | `GestureConfig` | Gesture navigation configuration |
595
+
596
+ ## Performance
597
+
598
+ | Metric | Value |
599
+ |--------|-------|
600
+ | Core bundle (gzip) | 10.35 kB |
601
+ | Audio (gzip) | 1.07 kB |
602
+ | Styles (gzip) | 0.84 kB |
603
+ | Navigation latency (p50) | < 0.01 ms |
604
+ | Tests | 150 passing |
605
+ | Coverage | ~96% |
606
+
607
+ Run benchmarks: `bun run bench` (navigation timing) / `bun run bench:size` (bundle check)
608
+
609
+ ## Peer Dependencies
610
+
611
+ - `vue` >= 3.4.0
612
+ - `howler` >= 2.2.0 *(optional — only for `vue-micro-router/audio`)*
613
+ - `@vue/devtools-api` >= 6.0.0 *(optional — only for devtools inspector)*
614
+
615
+ ## License
616
+
617
+ MIT