navgo 3.0.3 → 3.0.5

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.
Files changed (4) hide show
  1. package/index.d.ts +37 -37
  2. package/index.js +27 -26
  3. package/package.json +27 -17
  4. package/readme.md +90 -91
package/index.d.ts CHANGED
@@ -7,23 +7,23 @@ export type Params = Record<string, string | null | undefined>
7
7
 
8
8
  /** Built-in validator helpers shape. */
9
9
  export interface ValidatorHelpers {
10
- int(opts?: {
10
+ int(_opts?: {
11
11
  min?: number | null
12
12
  max?: number | null
13
- }): (value: string | null | undefined) => boolean
14
- oneOf(values: Iterable<string>): (value: string | null | undefined) => boolean
13
+ }): (_value: string | null | undefined) => boolean
14
+ one_of(_values: Iterable<string>): (_value: string | null | undefined) => boolean
15
15
  }
16
16
 
17
- /** Optional per-route hooks recognized by Navaid. */
17
+ /** Optional per-route hooks recognized by Navgo. */
18
18
  export interface Hooks {
19
19
  /** Validate params with custom per-param validators. Return `false` to skip a match. */
20
- param_validators?: Record<string, (value: string | null | undefined) => boolean>
20
+ param_validators?: Record<string, (_value: string | null | undefined) => boolean>
21
21
  /** Load data for a route before navigation. May return a Promise or an array of values/promises. */
22
- loaders?(params: Params): unknown | Promise<unknown> | Array<unknown | Promise<unknown>>
22
+ loaders?(_params: Params): unknown | Promise<unknown> | Array<unknown | Promise<unknown>>
23
23
  /** Predicate used during match(); may be async. If it returns `false`, the route is skipped. */
24
- validate?(params: Params): boolean | Promise<boolean>
24
+ validate?(_params: Params): boolean | Promise<boolean>
25
25
  /** Route-level navigation guard, called on the current route when leaving it. Synchronous only; call `nav.cancel()` to prevent navigation. */
26
- beforeRouteLeave?(nav: Navigation): void
26
+ before_route_leave?(_nav: Navigation): void
27
27
  }
28
28
 
29
29
  export interface NavigationTarget {
@@ -39,7 +39,7 @@ export interface Navigation {
39
39
  type: 'link' | 'goto' | 'popstate' | 'leave'
40
40
  from: NavigationTarget | null
41
41
  to: NavigationTarget | null
42
- willUnload: boolean
42
+ will_unload: boolean
43
43
  cancelled: boolean
44
44
  /** The original browser event that initiated navigation, when available. */
45
45
  event?: Event
@@ -55,30 +55,14 @@ export interface MatchResult<T = unknown> {
55
55
  params: Params
56
56
  }
57
57
 
58
- export interface Router<T = unknown> {
59
- /** Format `url` relative to the configured base. */
60
- format(url: string): string | false
61
- /** SvelteKit-like navigation that runs loaders before updating the URL. */
62
- goto(url: string, opts?: { replace?: boolean }): Promise<void>
63
- /** Shallow push — updates URL/state without triggering handlers. */
64
- pushState(url?: string | URL, state?: any): void
65
- /** Shallow replace — updates URL/state without triggering handlers. */
66
- replaceState(url?: string | URL, state?: any): void
67
- /** Manually preload loaders for a URL (deduped). */
68
- preload(url: string): Promise<unknown | void>
69
- /** Try to match `url`; returns route tuple and params or `null`. Supports async `validate`. */
70
- match(url: string): Promise<MatchResult<T> | null>
71
- /** Attach history + click listeners and immediately process current location. */
72
- init(): Promise<void>
73
- /** Remove listeners installed by `init()`. */
74
- destroy(): void
75
- }
58
+ // For convenience in docs/types, alias the class instance type
59
+ export type Router<T = unknown> = Navgo<T>
76
60
 
77
- /** Router metadata stored under `history.state.__navaid`. */
78
- export interface NavaidHistoryMeta {
61
+ /** Router metadata stored under `history.state.__navgo`. */
62
+ export interface NavgoHistoryMeta {
79
63
  /** Monotonic index of the current history entry for scroll restoration. */
80
64
  idx: number
81
- /** Present when the entry was created via shallow `pushState`/`replaceState`. */
65
+ /** Present when the entry was created via shallow `push_state`/`replace_state`. */
82
66
  shallow?: boolean
83
67
  /** Origin of the navigation that created this entry. */
84
68
  type?: 'link' | 'goto' | 'popstate'
@@ -91,20 +75,36 @@ export interface Options {
91
75
  preload_delay?: number
92
76
  /** Disable hover/touch preloading when `false`. Default true. */
93
77
  preload_on_hover?: boolean
94
- /** Global hook fired after per-route `beforeRouteLeave`, before loaders/history change. Can cancel. */
95
- before_navigate?(nav: Navigation): void
78
+ /** Global hook fired after per-route `before_route_leave`, before loaders/history change. Can cancel. */
79
+ before_navigate?(_nav: Navigation): void
96
80
  /** Global hook fired after routing completes (data loaded, URL updated, handlers run). */
97
- after_navigate?(nav: Navigation): void
81
+ after_navigate?(_nav: Navigation): void
98
82
  /** Global hook fired whenever the URL changes.
99
83
  * Triggers for shallow pushes/replaces, hash changes, popstate-shallow, 404s, and full navigations.
100
84
  * Receives the router's current snapshot (eg `{ url: URL, route: RouteTuple|null, params: Params }`).
101
85
  */
102
- url_changed?(payload: any): void
86
+ url_changed?(_payload: any): void
103
87
  }
104
88
 
105
- /** Navaid default export: class-based router. */
106
- export default class Navgo<T = unknown> implements Router<T> {
107
- constructor(routes?: Array<RouteTuple<T>>, opts?: Options)
89
+ /** Navgo default export: class-based router. */
90
+ export default class Navgo<T = unknown> {
91
+ constructor(_routes?: Array<RouteTuple<T>>, _opts?: Options)
92
+ /** Format `url` relative to the configured base. */
93
+ format(_url: string): string | false
94
+ /** SvelteKit-like navigation that runs loaders before updating the URL. */
95
+ goto(_url: string, _opts?: { replace?: boolean }): Promise<void>
96
+ /** Shallow push — updates URL/state without triggering handlers. */
97
+ push_state(_url?: string | URL, _state?: any): void
98
+ /** Shallow replace — updates URL/state without triggering handlers. */
99
+ replace_state(_url?: string | URL, _state?: any): void
100
+ /** Manually preload loaders for a URL (deduped). */
101
+ preload(_url: string): Promise<unknown | void>
102
+ /** Try to match `url`; returns route tuple and params or `null`. Supports async `validate`. */
103
+ match(_url: string): Promise<MatchResult<T> | null>
104
+ /** Attach history + click listeners and immediately process current location. */
105
+ init(): Promise<void>
106
+ /** Remove listeners installed by `init()`. */
107
+ destroy(): void
108
108
  /** Built-in validator helpers (namespaced). */
109
109
  static validators: ValidatorHelpers
110
110
  }
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { parse } from 'regexparam'
2
2
 
3
- const ℹ = (...args) => console.debug(...args)
3
+ const ℹ = (...args) => {}
4
4
 
5
5
  export default class Navgo {
6
6
  #opts = {
@@ -67,7 +67,7 @@ export default class Navgo {
67
67
  // ignore popstate while a hash-originating nav is in flight
68
68
  if (this.#hash_navigating) return
69
69
 
70
- const st = ev?.state?.__navaid
70
+ const st = ev?.state?.__navgo
71
71
  ℹ('[🧭 event:popstate]', st)
72
72
  // Hash-only or state-only change: pathname+search unchanged -> skip loaders
73
73
  const cur = this.#current.url
@@ -97,7 +97,7 @@ export default class Navgo {
97
97
  this.#hash_navigating = false
98
98
  const prev = history.state && typeof history.state == 'object' ? history.state : {}
99
99
  const next_idx = this.#route_idx + 1
100
- const next_state = { ...prev, __navaid: { ...prev.__navaid, idx: next_idx } }
100
+ const next_state = { ...prev, __navgo: { ...prev.__navgo, idx: next_idx } }
101
101
  history.replaceState(next_state, '', location.href)
102
102
  this.#route_idx = next_idx
103
103
  ℹ('[🧭 event:hashchange]', { idx: next_idx, href: location.href })
@@ -125,7 +125,7 @@ export default class Navgo {
125
125
  // persist scroll for refresh / session restore
126
126
  try {
127
127
  sessionStorage.setItem(
128
- `__navaid_scroll:${location.href}`,
128
+ `__navgo_scroll:${location.href}`,
129
129
  JSON.stringify({ x: scrollX, y: scrollY }),
130
130
  )
131
131
  } catch {}
@@ -135,12 +135,12 @@ export default class Navgo {
135
135
  const nav = this.#make_nav({
136
136
  type: 'leave',
137
137
  to: null,
138
- willUnload: true,
138
+ will_unload: true,
139
139
  event: ev,
140
140
  })
141
- this.#current.route?.[1]?.beforeRouteLeave?.(nav)
141
+ this.#current.route?.[1]?.before_route_leave?.(nav)
142
142
  if (nav.cancelled) {
143
- ℹ('[🧭 navigate]', 'cancelled by beforeRouteLeave during unload')
143
+ ℹ('[🧭 navigate]', 'cancelled by before_route_leave during unload')
144
144
  ev.preventDefault()
145
145
  ev.returnValue = ''
146
146
  }
@@ -163,7 +163,7 @@ export default class Navgo {
163
163
  #resolve_url_and_path(url_raw) {
164
164
  if (url_raw[0] == '/' && !this.#base_rgx.test(url_raw)) url_raw = this.#base + url_raw
165
165
  const url = new URL(url_raw, location.href)
166
- const path = this.format(url.pathname)?.match(/[^?#]*/)?.[0]
166
+ const path = this.format(url.pathname).match?.(/[^?#]*/)?.[0]
167
167
  ℹ('[🧭 resolve]', { url_in: url_raw, url: url.href, path })
168
168
  return path ? { url, path } : null
169
169
  }
@@ -178,7 +178,8 @@ export default class Navgo {
178
178
  !a.target &&
179
179
  !a.download &&
180
180
  a.host === location.host &&
181
- (href[0] != '/' || this.#base_rgx.test(href))
181
+ href[0] != '#' &&
182
+ this.#base_rgx.test(a.pathname)
182
183
  ? { a, href }
183
184
  : null
184
185
  }
@@ -200,7 +201,7 @@ export default class Navgo {
200
201
  /**
201
202
  * @returns {Navigation}
202
203
  */
203
- #make_nav({ type, from = undefined, to = undefined, willUnload = false, event = undefined }) {
204
+ #make_nav({ type, from = undefined, to = undefined, will_unload = false, event = undefined }) {
204
205
  const from_obj =
205
206
  from !== undefined
206
207
  ? from
@@ -215,7 +216,7 @@ export default class Navgo {
215
216
  type, // 'link' | 'goto' | 'popstate' | 'leave'
216
217
  from: from_obj,
217
218
  to,
218
- willUnload,
219
+ will_unload,
219
220
  cancelled: false,
220
221
  event,
221
222
  cancel() {
@@ -253,13 +254,13 @@ export default class Navgo {
253
254
  })
254
255
 
255
256
  //
256
- // beforeRouteLeave
257
+ // before_route_leave
257
258
  //
258
- this.#current.route?.[1]?.beforeRouteLeave?.(nav)
259
+ this.#current.route?.[1]?.before_route_leave?.(nav)
259
260
  if (nav.cancelled) {
260
261
  // use history.go to cancel the nav, and jump back to where we are
261
262
  if (is_popstate) {
262
- const new_idx = ev_param?.state?.__navaid?.idx
263
+ const new_idx = ev_param?.state?.__navgo?.idx
263
264
  if (new_idx != null) {
264
265
  const delta = new_idx - this.#route_idx
265
266
  if (delta) {
@@ -270,7 +271,7 @@ export default class Navgo {
270
271
  }
271
272
  }
272
273
  }
273
- ℹ('[🧭 goto]', 'cancelled by beforeRouteLeave')
274
+ ℹ('[🧭 goto]', 'cancelled by before_route_leave')
274
275
  return
275
276
  }
276
277
 
@@ -315,7 +316,7 @@ export default class Navgo {
315
316
  history.state && typeof history.state == 'object' ? history.state : {}
316
317
  const next_state = {
317
318
  ...prev_state,
318
- __navaid: { ...prev_state.__navaid, idx: next_idx, type: nav_type },
319
+ __navgo: { ...prev_state.__navgo, idx: next_idx, type: nav_type },
319
320
  }
320
321
  history[(opts.replace ? 'replace' : 'push') + 'State'](next_state, null, url.href)
321
322
  ℹ('[🧭 history]', opts.replace ? 'replaceState' : 'pushState', {
@@ -370,13 +371,13 @@ export default class Navgo {
370
371
  // save scroll for current index before shallow change
371
372
  this.#save_scroll()
372
373
  const idx = this.#route_idx + (replace ? 0 : 1)
373
- const st = { ...state, __navaid: { ...state?.__navaid, shallow: true, idx } }
374
+ const st = { ...state, __navgo: { ...state?.__navgo, shallow: true, idx } }
374
375
  history[(replace ? 'replace' : 'push') + 'State'](st, '', u.href)
375
- ℹ('[🧭 history]', replace ? 'replaceState(shallow)' : 'pushState(shallow)', {
376
+ ℹ('[🧭 history]', replace ? 'replace_state(shallow)' : 'push_state(shallow)', {
376
377
  idx,
377
378
  href: u.href,
378
379
  })
379
- // Popstate handler checks state.__navaid.shallow and skips router processing
380
+ // Popstate handler checks state.__navgo.shallow and skips router processing
380
381
  this.#route_idx = idx
381
382
  // carry forward current scroll position for the shallow entry so Forward restores correctly
382
383
  this.#scroll.set(idx, { x: scrollX, y: scrollY })
@@ -387,11 +388,11 @@ export default class Navgo {
387
388
  }
388
389
 
389
390
  /** @param {string|URL} [url] @param {any} [state] */
390
- pushState(url, state) {
391
+ push_state(url, state) {
391
392
  this.#commit_shallow(url, state, false)
392
393
  }
393
394
  /** @param {string|URL} [url] @param {any} [state] */
394
- replaceState(url, state) {
395
+ replace_state(url, state) {
395
396
  this.#commit_shallow(url, state, true)
396
397
  }
397
398
 
@@ -522,10 +523,10 @@ export default class Navgo {
522
523
  }
523
524
 
524
525
  // ensure current history state carries our index
525
- const cur_idx = history.state?.__navaid?.idx
526
+ const cur_idx = history.state?.__navgo?.idx
526
527
  if (cur_idx == null) {
527
528
  const prev = history.state && typeof history.state == 'object' ? history.state : {}
528
- const next_state = { ...prev, __navaid: { ...prev.__navaid, idx: this.#route_idx } }
529
+ const next_state = { ...prev, __navgo: { ...prev.__navgo, idx: this.#route_idx } }
529
530
  history.replaceState(next_state, '', location.href)
530
531
  ℹ('[🧭 history]', 'init idx', { idx: this.#route_idx })
531
532
  } else {
@@ -567,7 +568,7 @@ export default class Navgo {
567
568
  const is_initial = ctx && 'from' in ctx ? ctx.from == null : !t
568
569
  if (is_initial) {
569
570
  try {
570
- const k = `__navaid_scroll:${location.href}`
571
+ const k = `__navgo_scroll:${location.href}`
571
572
  const { x, y } = JSON.parse(sessionStorage.getItem(k))
572
573
  sessionStorage.removeItem(k)
573
574
  scrollTo(x, y)
@@ -578,7 +579,7 @@ export default class Navgo {
578
579
  // 1) On back/forward, restore saved position if available
579
580
  if (t === 'popstate') {
580
581
  const ev_state = ctx?.state ?? ctx?.event?.state
581
- const idx = ev_state?.__navaid?.idx
582
+ const idx = ev_state?.__navgo?.idx
582
583
  const target_idx = typeof idx === 'number' ? idx : this.#route_idx - 1
583
584
  this.#route_idx = target_idx
584
585
  const pos = this.#scroll.get(target_idx)
@@ -627,7 +628,7 @@ export default class Navgo {
627
628
  return true
628
629
  }
629
630
  },
630
- oneOf(values) {
631
+ one_of(values) {
631
632
  const set = new Set(values)
632
633
  return v => set.has(v)
633
634
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "navgo",
3
- "version": "3.0.3",
3
+ "version": "3.0.5",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/mustafa0x/navgo.git"
@@ -15,11 +15,11 @@
15
15
  "name": "mustafa j"
16
16
  },
17
17
  "scripts": {
18
- "pretest": "pnpm run build",
19
- "test": "vitest run",
18
+ "build": "perl -0777 -i -pe 's/console.debug\\(...args\\)/{}/g' index.js",
19
+ "prepublishOnly": "pnpm run build",
20
+ "test": "vitest run index.test.js",
20
21
  "test:e2e": "playwright test",
21
- "test:all": "pnpm run build && vitest run && playwright test",
22
- "types": "pnpm --package=typescript@5.6.3 dlx tsc -p test/types"
22
+ "types": "tsc -p test/types"
23
23
  },
24
24
  "files": [
25
25
  "*.d.ts",
@@ -34,26 +34,27 @@
34
34
  "regexparam": "^3.0.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@eslint/js": "^9.36.0",
38
- "@playwright/test": "^1.55.1",
37
+ "@eslint/js": "^9.37.0",
38
+ "@playwright/test": "^1.56.0",
39
39
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
40
- "@tailwindcss/vite": "^4.1.13",
40
+ "@tailwindcss/vite": "^4.1.14",
41
41
  "@tsconfig/svelte": "^5.0.5",
42
- "@types/node": "^24.6.0",
43
- "eslint": "^9.36.0",
42
+ "@types/node": "^24.7.2",
43
+ "eslint": "^9.37.0",
44
44
  "eslint-plugin-svelte": "^3.12.4",
45
45
  "globals": "^16.4.0",
46
- "jiti": "^2.6.0",
46
+ "jiti": "^2.6.1",
47
47
  "lightningcss": "^1.30.2",
48
48
  "prettier": "^3.6.2",
49
49
  "prettier-plugin-svelte": "^3.4.0",
50
50
  "prettier-plugin-tailwindcss": "^0.6.14",
51
- "svelte": "^5.39.7",
52
- "tailwindcss": "^4.1.13",
51
+ "svelte": "^5.39.11",
52
+ "tailwindcss": "^4.1.14",
53
53
  "terser": "5.44.0",
54
- "typescript-eslint": "^8.45.0",
55
- "vite": "^7.1.7",
56
- "vitest": "^2.1.3"
54
+ "typescript": "5.9.3",
55
+ "typescript-eslint": "^8.46.0",
56
+ "vite": "^7.1.9",
57
+ "vitest": "^3.2.4"
57
58
  },
58
59
  "pnpm": {
59
60
  "onlyBuiltDependencies": [
@@ -69,6 +70,15 @@
69
70
  "quoteProps": "as-needed",
70
71
  "bracketSpacing": true,
71
72
  "arrowParens": "avoid",
72
- "useTabs": true
73
+ "useTabs": true,
74
+ "overrides": [
75
+ {
76
+ "files": "*.md",
77
+ "options": {
78
+ "tabWidth": 2,
79
+ "useTabs": false
80
+ }
81
+ }
82
+ ]
73
83
  }
74
84
  }
package/readme.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ## Install
2
2
 
3
3
  ```
4
- $ pnpm install --dev navaid
4
+ $ pnpm install --dev navgo
5
5
  ```
6
6
 
7
7
  ## Usage
@@ -11,51 +11,51 @@ import Navgo from 'navgo'
11
11
 
12
12
  // Define routes up front (strings or RegExp)
13
13
  const routes = [
14
- ['/', {}],
15
- ['/users/:username', {}],
16
- ['/books/*', {}],
17
- [/articles\/(?<year>[0-9]{4})/, {}],
18
- [/privacy|privacy-policy/, {}],
19
- [
20
- '/admin',
21
- {
22
- // constrain params with built-ins or your own
23
- param_validators: {
24
- /* id: Navaid.validators.int({ min: 1 }) */
25
- },
26
- // load data before URL changes; result goes to after_navigate(...)
27
- loaders: params => fetch('/api/admin').then(r => r.json()),
28
- // per-route guard; cancel synchronously to block nav
29
- beforeRouteLeave(nav) {
30
- if ((nav.type === 'link' || nav.type === 'nav') && !confirm('Enter admin?')) {
31
- nav.cancel()
32
- }
33
- },
34
- },
35
- ],
14
+ ['/', {}],
15
+ ['/users/:username', {}],
16
+ ['/books/*', {}],
17
+ [/articles\/(?<year>[0-9]{4})/, {}],
18
+ [/privacy|privacy-policy/, {}],
19
+ [
20
+ '/admin',
21
+ {
22
+ // constrain params with built-ins or your own
23
+ param_validators: {
24
+ /* id: Navgo.validators.int({ min: 1 }) */
25
+ },
26
+ // load data before URL changes; result goes to after_navigate(...)
27
+ loaders: params => fetch('/api/admin').then(r => r.json()),
28
+ // per-route guard; cancel synchronously to block nav
29
+ before_route_leave(nav) {
30
+ if ((nav.type === 'link' || nav.type === 'nav') && !confirm('Enter admin?')) {
31
+ nav.cancel()
32
+ }
33
+ },
34
+ },
35
+ ],
36
36
  ]
37
37
 
38
38
  // Create router with options + callbacks
39
39
  const router = new Navgo(routes, {
40
- base: '/',
41
- before_navigate(nav) {
42
- // app-level hook before loaders/URL update; may cancel
43
- console.log('before_navigate', nav.type, '→', nav.to?.url.pathname)
44
- },
45
- after_navigate(nav) {
46
- // called after routing completes; nav.to.data holds loader result
47
- if (nav.to?.data?.__error?.status === 404) {
48
- console.log('404 for', nav.to.url.pathname)
49
- return
50
- }
51
-
52
- console.log('after_navigate', nav.to?.url.pathname, nav.to?.data)
53
- },
54
- url_changed(cur) {
55
- // fires on shallow/hash/popstate-shallow/404 and full navigations
56
- // `cur` is the router snapshot: { url: URL, route, params }
57
- console.log('url_changed', cur.url.href)
58
- },
40
+ base: '/',
41
+ before_navigate(nav) {
42
+ // app-level hook before loaders/URL update; may cancel
43
+ console.log('before_navigate', nav.type, '→', nav.to?.url.pathname)
44
+ },
45
+ after_navigate(nav) {
46
+ // called after routing completes; nav.to.data holds loader result
47
+ if (nav.to?.data?.__error?.status === 404) {
48
+ console.log('404 for', nav.to.url.pathname)
49
+ return
50
+ }
51
+
52
+ console.log('after_navigate', nav.to?.url.pathname, nav.to?.data)
53
+ },
54
+ url_changed(cur) {
55
+ // fires on shallow/hash/popstate-shallow/404 and full navigations
56
+ // `cur` is the router snapshot: { url: URL, route, params }
57
+ console.log('url_changed', cur.url.href)
58
+ },
59
59
  })
60
60
 
61
61
  // Long-lived router: history + <a> bindings
@@ -92,32 +92,32 @@ Notes:
92
92
  #### `options`
93
93
 
94
94
  - `base`: `string` (default `'/'`)
95
- - App base pathname. With or without leading/trailing slashes is accepted.
95
+ - App base pathname. With or without leading/trailing slashes is accepted.
96
96
  - `before_navigate`: `(nav: Navigation) => void`
97
- - App-level hook called once per navigation attempt after the per-route guard and before loaders/URL update. May call `nav.cancel()` synchronously to prevent navigation.
97
+ - App-level hook called once per navigation attempt after the per-route guard and before loaders/URL update. May call `nav.cancel()` synchronously to prevent navigation.
98
98
  - `after_navigate`: `(nav: Navigation) => void`
99
- - App-level hook called after routing completes (URL updated, data loaded). `nav.to.data` holds any loader data.
99
+ - App-level hook called after routing completes (URL updated, data loaded). `nav.to.data` holds any loader data.
100
100
  - `url_changed`: `(snapshot: any) => void`
101
- - Fires on every URL change — shallow `pushState`/`replaceState`, hash changes, `popstate` shallow entries, 404s, and full navigations.
102
- - Receives the router's current snapshot: an object like `{ url: URL, route: RouteTuple|null, params: Params }`.
103
- - The snapshot type is intentionally `any` and may evolve without a breaking change.
101
+ - Fires on every URL change — shallow `push_state`/`replace_state`, hash changes, `popstate` shallow entries, 404s, and full navigations.
102
+ - Receives the router's current snapshot: an object like `{ url: URL, route: RouteTuple|null, params: Params }`.
103
+ - The snapshot type is intentionally `any` and may evolve without a breaking change.
104
104
  - `preload_delay`: `number` (default `20`)
105
- - Delay in ms before hover preloading triggers.
105
+ - Delay in ms before hover preloading triggers.
106
106
  - `preload_on_hover`: `boolean` (default `true`)
107
- - When `false`, disables hover/touch preloading.
107
+ - When `false`, disables hover/touch preloading.
108
108
 
109
- Important: Navaid only processes routes that match your `base` path.
109
+ Important: Navgo only processes routes that match your `base` path.
110
110
 
111
111
  ### Route Hooks
112
112
 
113
113
  - param_validators?: `Record<string, (value: string|null|undefined) => boolean>`
114
- - Validate params (e.g., `id: Navaid.validators.int({ min: 1 })`). Any `false` result skips the route.
114
+ - Validate params (e.g., `id: Navgo.validators.int({ min: 1 })`). Any `false` result skips the route.
115
115
  - loaders?(params): `unknown | Promise | Array<unknown|Promise>`
116
- - Run before URL changes on `link`/`nav`. Results are cached per formatted path and forwarded to `after_navigate`.
116
+ - Run before URL changes on `link`/`nav`. Results are cached per formatted path and forwarded to `after_navigate`.
117
117
  - validate?(params): `boolean | Promise<boolean>`
118
- - Predicate called during matching. If it returns or resolves to `false`, the route is skipped.
119
- - beforeRouteLeave?(nav): `(nav: Navigation) => void`
120
- - Guard called once per navigation attempt on the current route (leave). Call `nav.cancel()` synchronously to prevent navigation. For `popstate`, cancellation auto-reverts the history jump.
118
+ - Predicate called during matching. If it returns or resolves to `false`, the route is skipped.
119
+ - before_route_leave?(nav): `(nav: Navigation) => void`
120
+ - Guard called once per navigation attempt on the current route (leave). Call `nav.cancel()` synchronously to prevent navigation. For `popstate`, cancellation auto-reverts the history jump.
121
121
 
122
122
  The `Navigation` object contains:
123
123
 
@@ -126,7 +126,7 @@ The `Navigation` object contains:
126
126
  type: 'link' | 'nav' | 'popstate' | 'leave',
127
127
  from: { url, params, route } | null,
128
128
  to: { url, params, route } | null,
129
- willUnload: boolean,
129
+ will_unload: boolean,
130
130
  cancelled: boolean,
131
131
  event?: Event,
132
132
  cancel(): void
@@ -137,29 +137,29 @@ The `Navigation` object contains:
137
137
 
138
138
  - Router calls `before_navigate` on the current route (leave).
139
139
  - Call `nav.cancel()` synchronously to cancel.
140
- - For `link`/`nav`, it stops before URL change.
141
- - For `popstate`, cancellation causes an automatic `history.go(...)` to revert to the previous index.
142
- - For `leave`, cancellation triggers the native “Leave site?” dialog (behavior is browser-controlled).
140
+ - For `link`/`nav`, it stops before URL change.
141
+ - For `popstate`, cancellation causes an automatic `history.go(...)` to revert to the previous index.
142
+ - For `leave`, cancellation triggers the native “Leave site?” dialog (behavior is browser-controlled).
143
143
 
144
144
  Example:
145
145
 
146
146
  ```js
147
147
  const routes = [
148
- [
149
- '/admin',
150
- {
151
- param_validators: {
152
- /* ... */
153
- },
154
- loaders: params => fetch('/api/admin/stats').then(r => r.json()),
155
- beforeRouteLeave(nav) {
156
- if (nav.type === 'link' || nav.type === 'nav') {
157
- if (!confirm('Enter admin area?')) nav.cancel()
158
- }
159
- },
160
- },
161
- ],
162
- ['/', {}],
148
+ [
149
+ '/admin',
150
+ {
151
+ param_validators: {
152
+ /* ... */
153
+ },
154
+ loaders: params => fetch('/api/admin/stats').then(r => r.json()),
155
+ before_route_leave(nav) {
156
+ if (nav.type === 'link' || nav.type === 'nav') {
157
+ if (!confirm('Enter admin area?')) nav.cancel()
158
+ }
159
+ },
160
+ },
161
+ ],
162
+ ['/', {}],
163
163
  ]
164
164
 
165
165
  const router = new Navgo(routes, { base: '/app' })
@@ -204,7 +204,7 @@ The desired path to navigate. If it begins with `/` and does not match the confi
204
204
  Type: `Object`
205
205
 
206
206
  - replace: `Boolean` (default `false`)
207
- - When `true`, uses `history.replaceState`; otherwise `history.pushState`.
207
+ - When `true`, uses `history.replaceState`; otherwise `history.pushState`.
208
208
 
209
209
  ### init()
210
210
 
@@ -240,13 +240,13 @@ Returns: `Promise<unknown | void>`
240
240
  Preload a route's `loaders` data for a given `uri` without navigating. Concurrent calls for the same path are deduped.
241
241
  Note: Resolves to `undefined` when the matched route has no `loaders`.
242
242
 
243
- ### pushState(url?, state?)
243
+ ### push_state(url?, state?)
244
244
 
245
245
  Returns: `void`
246
246
 
247
247
  Perform a shallow history push: updates the URL/state without triggering route processing.
248
248
 
249
- ### replaceState(url?, state?)
249
+ ### replace_state(url?, state?)
250
250
 
251
251
  Returns: `void`
252
252
 
@@ -266,9 +266,8 @@ This section explains, in detail, how navigation is processed: matching, hooks,
266
266
  - `goto` — programmatic navigation via `router.goto(...)`.
267
267
  - `popstate` — browser back/forward.
268
268
  - `leave` — page is unloading (refresh, external navigation, tab close) via `beforeunload`.
269
- - `pushState` (shallow)?
270
269
 
271
- The router passes the type to your route-level `beforeRouteLeave(nav)` hook.
270
+ The router passes the type to your route-level `before_route_leave(nav)` hook.
272
271
 
273
272
  ### Matching and Params
274
273
 
@@ -286,7 +285,7 @@ For `link` and `goto` navigations that match a route:
286
285
 
287
286
  ```
288
287
  [click <a>] or [router.goto()]
289
- beforeRouteLeave({ type }) // per-route guard
288
+ before_route_leave({ type }) // per-route guard
290
289
  → before_navigate(nav) // app-level start
291
290
  → cancelled? yes → stop
292
291
  → no → run loaders(params) // may be value, Promise, or Promise[]
@@ -300,10 +299,10 @@ For `link` and `goto` navigations that match a route:
300
299
 
301
300
  ### Shallow Routing
302
301
 
303
- Use `pushState(url, state?)` or `replaceState(url, state?)` to update the URL/state without re-running routing logic.
302
+ Use `push_state(url, state?)` or `replace_state(url, state?)` to update the URL/state without re-running routing logic.
304
303
 
305
304
  ```
306
- pushState/replaceState (shallow)
305
+ push_state/replace_state (shallow)
307
306
  → updates history.state and URL
308
307
  → router does not process routing on shallow operations
309
308
  ```
@@ -312,7 +311,7 @@ This lets you reflect UI state in the URL while deferring route transitions unti
312
311
 
313
312
  ### History Index & popstate Cancellation
314
313
 
315
- To enable `popstate` cancellation, Navaid stores a monotonic `idx` in `history.state.__navaid.idx`. On `popstate`, a cancelled navigation computes the delta between the target and current `idx` and calls `history.go(-delta)` to return to the prior entry.
314
+ To enable `popstate` cancellation, Navgo stores a monotonic `idx` in `history.state.__navgo.idx`. On `popstate`, a cancelled navigation computes the delta between the target and current `idx` and calls `history.go(-delta)` to return to the prior entry.
316
315
 
317
316
  ### Scroll Restoration
318
317
 
@@ -320,10 +319,10 @@ Navgo manages scroll manually (sets `history.scrollRestoration = 'manual'`) and
320
319
 
321
320
  - Saves the current scroll position for the active history index.
322
321
  - On `link`/`nav` (after route commit):
323
- - If the URL has a `#hash`, scroll to the matching element `id` or `[name="..."]`.
324
- - Otherwise, scroll to the top `(0, 0)`.
322
+ - If the URL has a `#hash`, scroll to the matching element `id` or `[name="..."]`.
323
+ - Otherwise, scroll to the top `(0, 0)`.
325
324
  - On `popstate`: restore the saved position for the target history index; if not found but there is a `#hash`, scroll to the anchor instead.
326
- - Shallow `pushState`/`replaceState` never adjust scroll (routing is skipped).
325
+ - Shallow `push_state`/`replace_state` never adjust scroll (routing is skipped).
327
326
 
328
327
  ```
329
328
  scroll flow
@@ -336,16 +335,16 @@ scroll flow
336
335
 
337
336
  - `format(uri)` — normalizes a path relative to `base`. Returns `false` when `uri` is outside of `base`.
338
337
  - `match(uri)` — returns a Promise of `{ route, params } | null` using string/RegExp patterns and validators. Awaits an async `validate(params)` if provided.
339
- - `goto(uri, { replace? })` — fires route-level `beforeRouteLeave('goto')`, calls global `before_navigate`, saves scroll, runs loaders, pushes/replaces, and completes via `after_navigate`.
338
+ - `goto(uri, { replace? })` — fires route-level `before_route_leave('goto')`, calls global `before_navigate`, saves scroll, runs loaders, pushes/replaces, and completes via `after_navigate`.
340
339
  - `init()` — wires global listeners (`popstate`, `pushstate`, `replacestate`, click) and optional hover/tap preloading; immediately processes the current location.
341
340
  - `destroy()` — removes listeners added by `init()`.
342
341
  - `preload(uri)` — pre-executes a route's `loaders` for a path and caches the result; concurrent calls are deduped.
343
- - `pushState(url?, state?)` — shallow push that updates the URL and `history.state` without route processing.
344
- - `replaceState(url?, state?)` — shallow replace that updates the URL and `history.state` without route processing.
342
+ - `push_state(url?, state?)` — shallow push that updates the URL and `history.state` without route processing.
343
+ - `replace_state(url?, state?)` — shallow replace that updates the URL and `history.state` without route processing.
345
344
 
346
345
  ### Built-in Validators
347
346
 
348
347
  - `Navgo.validators.int({ min?, max? })` — `true` iff the value is an integer within optional bounds.
349
- - `Navgo.validators.oneOf(iterable)` — `true` iff the value is in the provided set.
348
+ - `Navgo.validators.one_of(iterable)` — `true` iff the value is in the provided set.
350
349
 
351
350
  Attach validators via a route tuple's `data.param_validators` to constrain matches.