@pyreon/router 0.11.5 → 0.11.6

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.
@@ -7,8 +7,8 @@ import {
7
7
  parseQueryMulti,
8
8
  resolveRoute,
9
9
  stringifyQuery,
10
- } from "../match"
11
- import type { RouteRecord } from "../types"
10
+ } from '../match'
11
+ import type { RouteRecord } from '../types'
12
12
 
13
13
  const Home = () => null
14
14
  const About = () => null
@@ -17,261 +17,261 @@ const NotFound = () => null
17
17
 
18
18
  // ─── parseQuery — edge cases ─────────────────────────────────────────────────
19
19
 
20
- describe("parseQuery — edge cases", () => {
21
- test("handles URI-encoded keys", () => {
22
- expect(parseQuery("hello%20world=value")).toEqual({ "hello world": "value" })
20
+ describe('parseQuery — edge cases', () => {
21
+ test('handles URI-encoded keys', () => {
22
+ expect(parseQuery('hello%20world=value')).toEqual({ 'hello world': 'value' })
23
23
  })
24
24
 
25
- test("handles multiple equals signs in value", () => {
25
+ test('handles multiple equals signs in value', () => {
26
26
  // Only the first `=` is the delimiter
27
- expect(parseQuery("expr=a=b")).toEqual({ expr: "a=b" })
27
+ expect(parseQuery('expr=a=b')).toEqual({ expr: 'a=b' })
28
28
  })
29
29
 
30
- test("last value wins for duplicate keys", () => {
31
- expect(parseQuery("a=1&a=2")).toEqual({ a: "2" })
30
+ test('last value wins for duplicate keys', () => {
31
+ expect(parseQuery('a=1&a=2')).toEqual({ a: '2' })
32
32
  })
33
33
 
34
- test("handles empty key (skipped)", () => {
34
+ test('handles empty key (skipped)', () => {
35
35
  // "=value" has empty key, should be skipped
36
- expect(parseQuery("=value")).toEqual({})
36
+ expect(parseQuery('=value')).toEqual({})
37
37
  })
38
38
 
39
- test("handles key-only entry with no equals", () => {
40
- expect(parseQuery("active")).toEqual({ active: "" })
39
+ test('handles key-only entry with no equals', () => {
40
+ expect(parseQuery('active')).toEqual({ active: '' })
41
41
  })
42
42
 
43
- test("handles mixed entries", () => {
44
- expect(parseQuery("a=1&flag&b=2")).toEqual({ a: "1", flag: "", b: "2" })
43
+ test('handles mixed entries', () => {
44
+ expect(parseQuery('a=1&flag&b=2')).toEqual({ a: '1', flag: '', b: '2' })
45
45
  })
46
46
 
47
- test("decodes both keys and values", () => {
48
- expect(parseQuery("na%2Fme=val%26ue")).toEqual({ "na/me": "val&ue" })
47
+ test('decodes both keys and values', () => {
48
+ expect(parseQuery('na%2Fme=val%26ue')).toEqual({ 'na/me': 'val&ue' })
49
49
  })
50
50
  })
51
51
 
52
52
  // ─── parseQueryMulti ─────────────────────────────────────────────────────────
53
53
 
54
- describe("parseQueryMulti", () => {
55
- test("returns empty object for empty string", () => {
56
- expect(parseQueryMulti("")).toEqual({})
54
+ describe('parseQueryMulti', () => {
55
+ test('returns empty object for empty string', () => {
56
+ expect(parseQueryMulti('')).toEqual({})
57
57
  })
58
58
 
59
- test("single value stays as string", () => {
60
- expect(parseQueryMulti("color=red")).toEqual({ color: "red" })
59
+ test('single value stays as string', () => {
60
+ expect(parseQueryMulti('color=red')).toEqual({ color: 'red' })
61
61
  })
62
62
 
63
- test("duplicate keys become arrays", () => {
64
- expect(parseQueryMulti("color=red&color=blue")).toEqual({ color: ["red", "blue"] })
63
+ test('duplicate keys become arrays', () => {
64
+ expect(parseQueryMulti('color=red&color=blue')).toEqual({ color: ['red', 'blue'] })
65
65
  })
66
66
 
67
- test("triple duplicate keys become array of three", () => {
68
- expect(parseQueryMulti("a=1&a=2&a=3")).toEqual({ a: ["1", "2", "3"] })
67
+ test('triple duplicate keys become array of three', () => {
68
+ expect(parseQueryMulti('a=1&a=2&a=3')).toEqual({ a: ['1', '2', '3'] })
69
69
  })
70
70
 
71
- test("mixed single and multi values", () => {
72
- expect(parseQueryMulti("color=red&color=blue&size=lg")).toEqual({
73
- color: ["red", "blue"],
74
- size: "lg",
71
+ test('mixed single and multi values', () => {
72
+ expect(parseQueryMulti('color=red&color=blue&size=lg')).toEqual({
73
+ color: ['red', 'blue'],
74
+ size: 'lg',
75
75
  })
76
76
  })
77
77
 
78
- test("key without value", () => {
79
- expect(parseQueryMulti("flag")).toEqual({ flag: "" })
78
+ test('key without value', () => {
79
+ expect(parseQueryMulti('flag')).toEqual({ flag: '' })
80
80
  })
81
81
 
82
- test("key without value duplicated", () => {
83
- expect(parseQueryMulti("flag&flag")).toEqual({ flag: ["", ""] })
82
+ test('key without value duplicated', () => {
83
+ expect(parseQueryMulti('flag&flag')).toEqual({ flag: ['', ''] })
84
84
  })
85
85
 
86
- test("empty key is skipped", () => {
87
- expect(parseQueryMulti("=value")).toEqual({})
86
+ test('empty key is skipped', () => {
87
+ expect(parseQueryMulti('=value')).toEqual({})
88
88
  })
89
89
 
90
- test("decodes URI-encoded keys and values", () => {
91
- expect(parseQueryMulti("na%2Fme=val%26ue")).toEqual({ "na/me": "val&ue" })
90
+ test('decodes URI-encoded keys and values', () => {
91
+ expect(parseQueryMulti('na%2Fme=val%26ue')).toEqual({ 'na/me': 'val&ue' })
92
92
  })
93
93
  })
94
94
 
95
95
  // ─── stringifyQuery — edge cases ─────────────────────────────────────────────
96
96
 
97
- describe("stringifyQuery — edge cases", () => {
98
- test("encodes special characters", () => {
99
- const result = stringifyQuery({ "key with space": "value&more" })
100
- expect(result).toBe("?key%20with%20space=value%26more")
97
+ describe('stringifyQuery — edge cases', () => {
98
+ test('encodes special characters', () => {
99
+ const result = stringifyQuery({ 'key with space': 'value&more' })
100
+ expect(result).toBe('?key%20with%20space=value%26more')
101
101
  })
102
102
 
103
- test("handles single key-value pair", () => {
104
- expect(stringifyQuery({ page: "1" })).toBe("?page=1")
103
+ test('handles single key-value pair', () => {
104
+ expect(stringifyQuery({ page: '1' })).toBe('?page=1')
105
105
  })
106
106
 
107
- test("handles key with empty value", () => {
108
- expect(stringifyQuery({ debug: "" })).toBe("?debug")
107
+ test('handles key with empty value', () => {
108
+ expect(stringifyQuery({ debug: '' })).toBe('?debug')
109
109
  })
110
110
  })
111
111
 
112
112
  // ─── matchPath — edge cases ──────────────────────────────────────────────────
113
113
 
114
- describe("matchPath — edge cases", () => {
115
- test("splat param captures remaining path", () => {
116
- const result = matchPath("/files/:path*", "/files/a/b/c")
117
- expect(result).toEqual({ path: "a/b/c" })
114
+ describe('matchPath — edge cases', () => {
115
+ test('splat param captures remaining path', () => {
116
+ const result = matchPath('/files/:path*', '/files/a/b/c')
117
+ expect(result).toEqual({ path: 'a/b/c' })
118
118
  })
119
119
 
120
- test("splat param captures single segment", () => {
121
- const result = matchPath("/files/:path*", "/files/readme.txt")
122
- expect(result).toEqual({ path: "readme.txt" })
120
+ test('splat param captures single segment', () => {
121
+ const result = matchPath('/files/:path*', '/files/readme.txt')
122
+ expect(result).toEqual({ path: 'readme.txt' })
123
123
  })
124
124
 
125
- test("optional param matches with value", () => {
126
- const result = matchPath("/user/:id?", "/user/42")
127
- expect(result).toEqual({ id: "42" })
125
+ test('optional param matches with value', () => {
126
+ const result = matchPath('/user/:id?', '/user/42')
127
+ expect(result).toEqual({ id: '42' })
128
128
  })
129
129
 
130
- test("optional param matches without value", () => {
131
- const result = matchPath("/user/:id?", "/user")
130
+ test('optional param matches without value', () => {
131
+ const result = matchPath('/user/:id?', '/user')
132
132
  expect(result).toEqual({})
133
133
  })
134
134
 
135
- test("returns null for too many path segments", () => {
136
- expect(matchPath("/a/b", "/a/b/c")).toBeNull()
135
+ test('returns null for too many path segments', () => {
136
+ expect(matchPath('/a/b', '/a/b/c')).toBeNull()
137
137
  })
138
138
 
139
- test("exact static match returns empty params", () => {
140
- expect(matchPath("/about", "/about")).toEqual({})
139
+ test('exact static match returns empty params', () => {
140
+ expect(matchPath('/about', '/about')).toEqual({})
141
141
  })
142
142
 
143
- test("root path matches root pattern", () => {
144
- expect(matchPath("/", "/")).toEqual({})
143
+ test('root path matches root pattern', () => {
144
+ expect(matchPath('/', '/')).toEqual({})
145
145
  })
146
146
 
147
- test("mismatched static segment returns null", () => {
148
- expect(matchPath("/foo", "/bar")).toBeNull()
147
+ test('mismatched static segment returns null', () => {
148
+ expect(matchPath('/foo', '/bar')).toBeNull()
149
149
  })
150
150
 
151
- test("decodes URI-encoded segments", () => {
152
- const result = matchPath("/user/:name", "/user/hello%20world")
153
- expect(result).toEqual({ name: "hello world" })
151
+ test('decodes URI-encoded segments', () => {
152
+ const result = matchPath('/user/:name', '/user/hello%20world')
153
+ expect(result).toEqual({ name: 'hello world' })
154
154
  })
155
155
 
156
- test("multiple params in a row", () => {
157
- const result = matchPath("/:a/:b/:c", "/x/y/z")
158
- expect(result).toEqual({ a: "x", b: "y", c: "z" })
156
+ test('multiple params in a row', () => {
157
+ const result = matchPath('/:a/:b/:c', '/x/y/z')
158
+ expect(result).toEqual({ a: 'x', b: 'y', c: 'z' })
159
159
  })
160
160
  })
161
161
 
162
162
  // ─── resolveRoute — edge cases ───────────────────────────────────────────────
163
163
 
164
- describe("resolveRoute — edge cases", () => {
164
+ describe('resolveRoute — edge cases', () => {
165
165
  const routes: RouteRecord[] = [
166
- { path: "/", component: Home },
167
- { path: "/about", component: About },
168
- { path: "/user/:id", component: User },
166
+ { path: '/', component: Home },
167
+ { path: '/about', component: About },
168
+ { path: '/user/:id', component: User },
169
169
  {
170
- path: "/admin",
170
+ path: '/admin',
171
171
  component: Home,
172
172
  meta: { requiresAuth: true },
173
173
  children: [
174
- { path: "users", component: User },
175
- { path: "settings", component: About },
174
+ { path: 'users', component: User },
175
+ { path: 'settings', component: About },
176
176
  ],
177
177
  },
178
- { path: "*", component: NotFound },
178
+ { path: '*', component: NotFound },
179
179
  ]
180
180
 
181
- test("resolves root path with empty query", () => {
182
- const r = resolveRoute("/", routes)
183
- expect(r.path).toBe("/")
181
+ test('resolves root path with empty query', () => {
182
+ const r = resolveRoute('/', routes)
183
+ expect(r.path).toBe('/')
184
184
  expect(r.params).toEqual({})
185
185
  expect(r.query).toEqual({})
186
- expect(r.hash).toBe("")
186
+ expect(r.hash).toBe('')
187
187
  })
188
188
 
189
- test("resolves path with query and hash in path portion", () => {
189
+ test('resolves path with query and hash in path portion', () => {
190
190
  // Hash in the path portion (before ?) is extracted from pathAndHash
191
- const r = resolveRoute("/about#section?key=val", routes)
192
- expect(r.path).toBe("/about")
193
- expect(r.hash).toBe("section")
194
- expect(r.query).toEqual({ key: "val" })
191
+ const r = resolveRoute('/about#section?key=val', routes)
192
+ expect(r.path).toBe('/about')
193
+ expect(r.hash).toBe('section')
194
+ expect(r.query).toEqual({ key: 'val' })
195
195
  })
196
196
 
197
- test("resolves path with query containing hash (hash after query)", () => {
197
+ test('resolves path with query containing hash (hash after query)', () => {
198
198
  // When hash follows query: /about?key=val#section
199
199
  // The # is part of the query value since ? comes first
200
- const r = resolveRoute("/about?key=val#section", routes)
201
- expect(r.path).toBe("/about")
200
+ const r = resolveRoute('/about?key=val#section', routes)
201
+ expect(r.path).toBe('/about')
202
202
  // The hash ends up in the query value since it's after the ?
203
- expect(r.query.key).toContain("val")
203
+ expect(r.query.key).toContain('val')
204
204
  })
205
205
 
206
- test("resolves nested route with merged meta", () => {
207
- const r = resolveRoute("/admin/users", routes)
206
+ test('resolves nested route with merged meta', () => {
207
+ const r = resolveRoute('/admin/users', routes)
208
208
  expect(r.matched.length).toBe(2)
209
209
  expect(r.meta.requiresAuth).toBe(true)
210
210
  })
211
211
 
212
- test("resolves dynamic param route", () => {
213
- const r = resolveRoute("/user/123", routes)
214
- expect(r.params.id).toBe("123")
212
+ test('resolves dynamic param route', () => {
213
+ const r = resolveRoute('/user/123', routes)
214
+ expect(r.params.id).toBe('123')
215
215
  expect(r.matched.length).toBeGreaterThan(0)
216
216
  })
217
217
 
218
- test("wildcard catches unmatched paths", () => {
219
- const r = resolveRoute("/totally/unknown/path", routes)
218
+ test('wildcard catches unmatched paths', () => {
219
+ const r = resolveRoute('/totally/unknown/path', routes)
220
220
  expect(r.matched.length).toBeGreaterThan(0)
221
221
  expect(r.matched[r.matched.length - 1]?.component).toBe(NotFound)
222
222
  })
223
223
 
224
- test("returns empty matched for no match without wildcard", () => {
224
+ test('returns empty matched for no match without wildcard', () => {
225
225
  const simpleRoutes: RouteRecord[] = [
226
- { path: "/", component: Home },
227
- { path: "/about", component: About },
226
+ { path: '/', component: Home },
227
+ { path: '/about', component: About },
228
228
  ]
229
- const r = resolveRoute("/nonexistent", simpleRoutes)
229
+ const r = resolveRoute('/nonexistent', simpleRoutes)
230
230
  expect(r.matched).toHaveLength(0)
231
231
  })
232
232
 
233
- test("resolves path with hash before query (edge case)", () => {
233
+ test('resolves path with hash before query (edge case)', () => {
234
234
  // hash in the path portion (before ?), query is separate
235
- const r = resolveRoute("/#anchor?key=val", routes)
236
- expect(r.hash).toBe("anchor")
235
+ const r = resolveRoute('/#anchor?key=val', routes)
236
+ expect(r.hash).toBe('anchor')
237
237
  })
238
238
 
239
- test("resolves deeply nested routes", () => {
239
+ test('resolves deeply nested routes', () => {
240
240
  const deepRoutes: RouteRecord[] = [
241
241
  {
242
- path: "/a",
242
+ path: '/a',
243
243
  component: Home,
244
244
  children: [
245
245
  {
246
- path: "b",
246
+ path: 'b',
247
247
  component: About,
248
- children: [{ path: "c", component: User }],
248
+ children: [{ path: 'c', component: User }],
249
249
  },
250
250
  ],
251
251
  },
252
252
  ]
253
- const r = resolveRoute("/a/b/c", deepRoutes)
253
+ const r = resolveRoute('/a/b/c', deepRoutes)
254
254
  expect(r.matched.length).toBe(3)
255
255
  })
256
256
 
257
- test("optional param route matches with and without param", () => {
258
- const optRoutes: RouteRecord[] = [{ path: "/page/:slug?", component: Home }]
259
- const withParam = resolveRoute("/page/about", optRoutes)
260
- expect(withParam.params.slug).toBe("about")
257
+ test('optional param route matches with and without param', () => {
258
+ const optRoutes: RouteRecord[] = [{ path: '/page/:slug?', component: Home }]
259
+ const withParam = resolveRoute('/page/about', optRoutes)
260
+ expect(withParam.params.slug).toBe('about')
261
261
 
262
- const withoutParam = resolveRoute("/page", optRoutes)
262
+ const withoutParam = resolveRoute('/page', optRoutes)
263
263
  expect(withoutParam.matched.length).toBeGreaterThan(0)
264
264
  })
265
265
 
266
- test("splat route captures all remaining segments", () => {
267
- const splatRoutes: RouteRecord[] = [{ path: "/docs/:rest*", component: Home }]
268
- const r = resolveRoute("/docs/api/reference/types", splatRoutes)
269
- expect(r.params.rest).toBe("api/reference/types")
266
+ test('splat route captures all remaining segments', () => {
267
+ const splatRoutes: RouteRecord[] = [{ path: '/docs/:rest*', component: Home }]
268
+ const r = resolveRoute('/docs/api/reference/types', splatRoutes)
269
+ expect(r.params.rest).toBe('api/reference/types')
270
270
  })
271
271
 
272
- test("caches compiled routes (same reference gives same result)", () => {
273
- const r1 = resolveRoute("/about", routes)
274
- const r2 = resolveRoute("/about", routes)
272
+ test('caches compiled routes (same reference gives same result)', () => {
273
+ const r1 = resolveRoute('/about', routes)
274
+ const r2 = resolveRoute('/about', routes)
275
275
  expect(r1.path).toBe(r2.path)
276
276
  expect(r1.matched.length).toBe(r2.matched.length)
277
277
  })
@@ -279,189 +279,189 @@ describe("resolveRoute — edge cases", () => {
279
279
 
280
280
  // ─── resolveRoute — alias support ────────────────────────────────────────────
281
281
 
282
- describe("resolveRoute — alias", () => {
283
- test("alias string resolves to same component", () => {
282
+ describe('resolveRoute — alias', () => {
283
+ test('alias string resolves to same component', () => {
284
284
  const aliasRoutes: RouteRecord[] = [
285
- { path: "/user/:id", alias: "/profile/:id", component: User },
285
+ { path: '/user/:id', alias: '/profile/:id', component: User },
286
286
  ]
287
- const r = resolveRoute("/profile/42", aliasRoutes)
287
+ const r = resolveRoute('/profile/42', aliasRoutes)
288
288
  expect(r.matched.length).toBeGreaterThan(0)
289
289
  expect(r.matched[0]?.component).toBe(User)
290
- expect(r.params.id).toBe("42")
290
+ expect(r.params.id).toBe('42')
291
291
  })
292
292
 
293
- test("alias array resolves multiple paths to same component", () => {
293
+ test('alias array resolves multiple paths to same component', () => {
294
294
  const aliasRoutes: RouteRecord[] = [
295
- { path: "/home", alias: ["/index", "/main"], component: Home },
295
+ { path: '/home', alias: ['/index', '/main'], component: Home },
296
296
  ]
297
- const r1 = resolveRoute("/index", aliasRoutes)
298
- const r2 = resolveRoute("/main", aliasRoutes)
297
+ const r1 = resolveRoute('/index', aliasRoutes)
298
+ const r2 = resolveRoute('/main', aliasRoutes)
299
299
  expect(r1.matched[0]?.component).toBe(Home)
300
300
  expect(r2.matched[0]?.component).toBe(Home)
301
301
  })
302
302
 
303
- test("primary path still works with alias defined", () => {
304
- const aliasRoutes: RouteRecord[] = [{ path: "/home", alias: "/index", component: Home }]
305
- const r = resolveRoute("/home", aliasRoutes)
303
+ test('primary path still works with alias defined', () => {
304
+ const aliasRoutes: RouteRecord[] = [{ path: '/home', alias: '/index', component: Home }]
305
+ const r = resolveRoute('/home', aliasRoutes)
306
306
  expect(r.matched[0]?.component).toBe(Home)
307
307
  })
308
308
  })
309
309
 
310
310
  // ─── buildPath — edge cases ──────────────────────────────────────────────────
311
311
 
312
- describe("buildPath — edge cases", () => {
313
- test("omits segment for missing optional param", () => {
314
- const result = buildPath("/user/:id?", {})
315
- expect(result).toBe("/user")
312
+ describe('buildPath — edge cases', () => {
313
+ test('omits segment for missing optional param', () => {
314
+ const result = buildPath('/user/:id?', {})
315
+ expect(result).toBe('/user')
316
316
  })
317
317
 
318
- test("includes segment for provided optional param", () => {
319
- const result = buildPath("/user/:id?", { id: "42" })
320
- expect(result).toBe("/user/42")
318
+ test('includes segment for provided optional param', () => {
319
+ const result = buildPath('/user/:id?', { id: '42' })
320
+ expect(result).toBe('/user/42')
321
321
  })
322
322
 
323
- test("splat param preserves slashes", () => {
323
+ test('splat param preserves slashes', () => {
324
324
  // buildPath regex captures the full param name including * via [^/]+
325
325
  // so the key in params must match what the regex captures
326
- const result = buildPath("/docs/:path*", { "path*": "api/reference/types" })
327
- expect(result).toBe("/docs/api/reference/types")
326
+ const result = buildPath('/docs/:path*', { 'path*': 'api/reference/types' })
327
+ expect(result).toBe('/docs/api/reference/types')
328
328
  })
329
329
 
330
- test("encodes special characters in params", () => {
331
- const result = buildPath("/user/:name", { name: "hello world" })
332
- expect(result).toBe("/user/hello%20world")
330
+ test('encodes special characters in params', () => {
331
+ const result = buildPath('/user/:name', { name: 'hello world' })
332
+ expect(result).toBe('/user/hello%20world')
333
333
  })
334
334
 
335
- test("handles path with no params", () => {
336
- const result = buildPath("/about", {})
337
- expect(result).toBe("/about")
335
+ test('handles path with no params', () => {
336
+ const result = buildPath('/about', {})
337
+ expect(result).toBe('/about')
338
338
  })
339
339
 
340
- test("handles root path", () => {
341
- const result = buildPath("/", {})
342
- expect(result).toBe("/")
340
+ test('handles root path', () => {
341
+ const result = buildPath('/', {})
342
+ expect(result).toBe('/')
343
343
  })
344
344
 
345
- test("encodes splat param segments individually", () => {
345
+ test('encodes splat param segments individually', () => {
346
346
  // buildPath regex captures full param name including * via [^/]+
347
- const result = buildPath("/files/:path*", { "path*": "dir/my file.txt" })
348
- expect(result).toBe("/files/dir/my%20file.txt")
347
+ const result = buildPath('/files/:path*', { 'path*': 'dir/my file.txt' })
348
+ expect(result).toBe('/files/dir/my%20file.txt')
349
349
  })
350
350
  })
351
351
 
352
352
  // ─── findRouteByName — edge cases ────────────────────────────────────────────
353
353
 
354
- describe("findRouteByName — edge cases", () => {
355
- test("finds deeply nested route", () => {
354
+ describe('findRouteByName — edge cases', () => {
355
+ test('finds deeply nested route', () => {
356
356
  const routes: RouteRecord[] = [
357
357
  {
358
- path: "/a",
358
+ path: '/a',
359
359
  component: Home,
360
360
  children: [
361
361
  {
362
- path: "b",
362
+ path: 'b',
363
363
  component: About,
364
- children: [{ path: "c", component: User, name: "deep" }],
364
+ children: [{ path: 'c', component: User, name: 'deep' }],
365
365
  },
366
366
  ],
367
367
  },
368
368
  ]
369
- const found = findRouteByName("deep", routes)
369
+ const found = findRouteByName('deep', routes)
370
370
  expect(found).not.toBeNull()
371
- expect(found?.path).toBe("c")
371
+ expect(found?.path).toBe('c')
372
372
  })
373
373
 
374
- test("returns first match in definition order", () => {
374
+ test('returns first match in definition order', () => {
375
375
  const routes: RouteRecord[] = [
376
- { path: "/first", component: Home, name: "dup" },
377
- { path: "/second", component: About, name: "dup" },
376
+ { path: '/first', component: Home, name: 'dup' },
377
+ { path: '/second', component: About, name: 'dup' },
378
378
  ]
379
- const found = findRouteByName("dup", routes)
380
- expect(found?.path).toBe("/first")
379
+ const found = findRouteByName('dup', routes)
380
+ expect(found?.path).toBe('/first')
381
381
  })
382
382
 
383
- test("returns null for empty routes array", () => {
384
- expect(findRouteByName("anything", [])).toBeNull()
383
+ test('returns null for empty routes array', () => {
384
+ expect(findRouteByName('anything', [])).toBeNull()
385
385
  })
386
386
  })
387
387
 
388
388
  // ─── buildNameIndex — edge cases ─────────────────────────────────────────────
389
389
 
390
- describe("buildNameIndex — edge cases", () => {
391
- test("handles empty routes", () => {
390
+ describe('buildNameIndex — edge cases', () => {
391
+ test('handles empty routes', () => {
392
392
  const index = buildNameIndex([])
393
393
  expect(index.size).toBe(0)
394
394
  })
395
395
 
396
- test("does not index routes without names", () => {
396
+ test('does not index routes without names', () => {
397
397
  const routes: RouteRecord[] = [
398
- { path: "/", component: Home },
399
- { path: "/about", component: About },
398
+ { path: '/', component: Home },
399
+ { path: '/about', component: About },
400
400
  ]
401
401
  const index = buildNameIndex(routes)
402
402
  expect(index.size).toBe(0)
403
403
  })
404
404
 
405
- test("indexes deeply nested named routes", () => {
405
+ test('indexes deeply nested named routes', () => {
406
406
  const routes: RouteRecord[] = [
407
407
  {
408
- path: "/a",
408
+ path: '/a',
409
409
  component: Home,
410
- name: "a",
410
+ name: 'a',
411
411
  children: [
412
412
  {
413
- path: "b",
413
+ path: 'b',
414
414
  component: About,
415
- name: "b",
416
- children: [{ path: "c", component: User, name: "c" }],
415
+ name: 'b',
416
+ children: [{ path: 'c', component: User, name: 'c' }],
417
417
  },
418
418
  ],
419
419
  },
420
420
  ]
421
421
  const index = buildNameIndex(routes)
422
422
  expect(index.size).toBe(3)
423
- expect(index.get("c")?.path).toBe("c")
423
+ expect(index.get('c')?.path).toBe('c')
424
424
  })
425
425
  })
426
426
 
427
427
  // ─── resolveRoute — dynamic first segment ────────────────────────────────────
428
428
 
429
- describe("resolveRoute — dynamic first segment routing", () => {
430
- test("matches route where first segment is a param", () => {
431
- const routes: RouteRecord[] = [{ path: "/:lang/about", component: About }]
432
- const r = resolveRoute("/en/about", routes)
429
+ describe('resolveRoute — dynamic first segment routing', () => {
430
+ test('matches route where first segment is a param', () => {
431
+ const routes: RouteRecord[] = [{ path: '/:lang/about', component: About }]
432
+ const r = resolveRoute('/en/about', routes)
433
433
  expect(r.matched.length).toBeGreaterThan(0)
434
- expect(r.params.lang).toBe("en")
434
+ expect(r.params.lang).toBe('en')
435
435
  })
436
436
 
437
- test("static routes take priority over dynamic first segment", () => {
437
+ test('static routes take priority over dynamic first segment', () => {
438
438
  const routes: RouteRecord[] = [
439
- { path: "/about", component: About },
440
- { path: "/:slug", component: User },
439
+ { path: '/about', component: About },
440
+ { path: '/:slug', component: User },
441
441
  ]
442
- const r = resolveRoute("/about", routes)
442
+ const r = resolveRoute('/about', routes)
443
443
  expect(r.matched[0]?.component).toBe(About)
444
444
  })
445
445
  })
446
446
 
447
447
  // ─── resolveRoute — wildcard children ────────────────────────────────────────
448
448
 
449
- describe("resolveRoute — wildcard patterns", () => {
450
- test("(.*) catches any path", () => {
449
+ describe('resolveRoute — wildcard patterns', () => {
450
+ test('(.*) catches any path', () => {
451
451
  const routes: RouteRecord[] = [
452
- { path: "/", component: Home },
453
- { path: "(.*)", component: NotFound },
452
+ { path: '/', component: Home },
453
+ { path: '(.*)', component: NotFound },
454
454
  ]
455
- const r = resolveRoute("/any/path/here", routes)
455
+ const r = resolveRoute('/any/path/here', routes)
456
456
  expect(r.matched[r.matched.length - 1]?.component).toBe(NotFound)
457
457
  })
458
458
 
459
- test("* catches any path", () => {
459
+ test('* catches any path', () => {
460
460
  const routes: RouteRecord[] = [
461
- { path: "/", component: Home },
462
- { path: "*", component: NotFound },
461
+ { path: '/', component: Home },
462
+ { path: '*', component: NotFound },
463
463
  ]
464
- const r = resolveRoute("/any/path/here", routes)
464
+ const r = resolveRoute('/any/path/here', routes)
465
465
  expect(r.matched[r.matched.length - 1]?.component).toBe(NotFound)
466
466
  })
467
467
  })