@keenmate/svelte-spa-router 5.0.0-rc10 → 5.0.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/CHANGELOG.md +767 -654
- package/README.md +278 -57
- package/package.json +6 -2
- package/src/lib/Router.svelte +275 -74
- package/src/lib/Router.svelte.d.ts +3 -3
- package/src/lib/helpers/ErrorDisplay.svelte +384 -384
- package/src/lib/helpers/GlobalErrorHandler.svelte +316 -315
- package/src/lib/helpers/error-handler.svelte.js +252 -251
- package/src/lib/helpers/filters.svelte.js +292 -291
- package/src/lib/helpers/hierarchy.d.ts +98 -0
- package/src/lib/helpers/hierarchy.svelte.js +197 -177
- package/src/lib/helpers/navigation-guard.svelte.js +177 -177
- package/src/lib/helpers/permissions.svelte.js +450 -351
- package/src/lib/helpers/querystring-helpers.svelte.js +316 -316
- package/src/lib/helpers/querystring.svelte.js +74 -74
- package/src/lib/helpers/route-metadata.svelte.js +339 -340
- package/src/lib/helpers/url-helpers.svelte.js +48 -48
- package/src/lib/index.js +2 -3
- package/src/lib/logger.ts +246 -0
- package/src/lib/utils.d.ts +0 -39
- package/src/lib/utils.svelte.js +952 -908
- package/src/lib/vendor/loglevel/index.js +9 -0
- package/src/lib/vendor/loglevel/loglevel-esm.js +349 -0
- package/src/lib/vendor/loglevel/loglevel-plugin-prefix-esm.js +132 -0
- package/src/lib/vendor/loglevel/loglevel-plugin-prefix.js +149 -0
- package/src/lib/vendor/loglevel/loglevel.js +357 -0
- package/src/lib/vendor/loglevel/prefix.js +9 -0
- package/src/lib/internal/logging.js +0 -121
package/CHANGELOG.md
CHANGED
|
@@ -1,654 +1,767 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [Unreleased]
|
|
9
|
-
|
|
10
|
-
## [5.0.0
|
|
11
|
-
|
|
12
|
-
###
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
- `
|
|
91
|
-
- `
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
- `
|
|
95
|
-
- `
|
|
96
|
-
- `
|
|
97
|
-
- `
|
|
98
|
-
|
|
99
|
-
- `
|
|
100
|
-
- `
|
|
101
|
-
- `
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
- **
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
- `
|
|
141
|
-
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
-
|
|
152
|
-
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
-
|
|
161
|
-
-
|
|
162
|
-
-
|
|
163
|
-
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
-
|
|
192
|
-
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
-
|
|
196
|
-
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
- **
|
|
202
|
-
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
- **
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
- **
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
-
|
|
257
|
-
-
|
|
258
|
-
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
-
|
|
265
|
-
-
|
|
266
|
-
-
|
|
267
|
-
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
-
|
|
273
|
-
-
|
|
274
|
-
-
|
|
275
|
-
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
- `
|
|
280
|
-
-
|
|
281
|
-
|
|
282
|
-
-
|
|
283
|
-
- `
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
-
|
|
289
|
-
-
|
|
290
|
-
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
-
|
|
295
|
-
-
|
|
296
|
-
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
-
|
|
300
|
-
-
|
|
301
|
-
|
|
302
|
-
-
|
|
303
|
-
|
|
304
|
-
-
|
|
305
|
-
-
|
|
306
|
-
-
|
|
307
|
-
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
**
|
|
315
|
-
-
|
|
316
|
-
-
|
|
317
|
-
-
|
|
318
|
-
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
-
|
|
326
|
-
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
-
|
|
336
|
-
|
|
337
|
-
-
|
|
338
|
-
- `
|
|
339
|
-
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
- `
|
|
343
|
-
|
|
344
|
-
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
- `
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
-
|
|
355
|
-
|
|
356
|
-
-
|
|
357
|
-
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
-
|
|
365
|
-
-
|
|
366
|
-
-
|
|
367
|
-
- `
|
|
368
|
-
- `
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
- **
|
|
372
|
-
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
-
|
|
377
|
-
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
-
|
|
388
|
-
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
-
|
|
397
|
-
-
|
|
398
|
-
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
-
|
|
403
|
-
-
|
|
404
|
-
-
|
|
405
|
-
|
|
406
|
-
-
|
|
407
|
-
-
|
|
408
|
-
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
-
|
|
413
|
-
-
|
|
414
|
-
-
|
|
415
|
-
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
- **
|
|
420
|
-
-
|
|
421
|
-
-
|
|
422
|
-
-
|
|
423
|
-
-
|
|
424
|
-
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
-
|
|
439
|
-
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
-
|
|
446
|
-
-
|
|
447
|
-
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
-
|
|
455
|
-
-
|
|
456
|
-
|
|
457
|
-
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
- **
|
|
464
|
-
-
|
|
465
|
-
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
-
|
|
471
|
-
-
|
|
472
|
-
-
|
|
473
|
-
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
-
|
|
479
|
-
-
|
|
480
|
-
|
|
481
|
-
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
- `
|
|
489
|
-
- `
|
|
490
|
-
- `
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
- `
|
|
495
|
-
- `
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
- `
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
-
|
|
508
|
-
-
|
|
509
|
-
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
-
|
|
515
|
-
-
|
|
516
|
-
- `
|
|
517
|
-
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
-
|
|
521
|
-
-
|
|
522
|
-
- `
|
|
523
|
-
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
-
|
|
528
|
-
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
-
|
|
534
|
-
-
|
|
535
|
-
-
|
|
536
|
-
|
|
537
|
-
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
- `
|
|
541
|
-
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
-
|
|
547
|
-
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
-
|
|
552
|
-
-
|
|
553
|
-
-
|
|
554
|
-
-
|
|
555
|
-
|
|
556
|
-
-
|
|
557
|
-
-
|
|
558
|
-
-
|
|
559
|
-
-
|
|
560
|
-
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
- `
|
|
568
|
-
-
|
|
569
|
-
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
-
|
|
578
|
-
-
|
|
579
|
-
|
|
580
|
-
###
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
-
|
|
584
|
-
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
-
|
|
629
|
-
-
|
|
630
|
-
-
|
|
631
|
-
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
- `
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
-
|
|
647
|
-
-
|
|
648
|
-
-
|
|
649
|
-
|
|
650
|
-
- **
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [5.0.0] - 2025-01-17
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **Code Quality:** Major ESLint cleanup - reduced linting issues from 161 to 11 (93% reduction)
|
|
14
|
+
- Removed unused imports across multiple modules (hierarchyLogger, location, untrack, hasRoute, etc.)
|
|
15
|
+
- Removed unused function parameters in test files
|
|
16
|
+
- Replaced unused catch error variables with bare catch blocks
|
|
17
|
+
- Added `src/lib/vendor/**` to eslint ignore list (third-party loglevel library)
|
|
18
|
+
- Remaining 11 warnings are false positives from ESLint not understanding Svelte 5 runes
|
|
19
|
+
- **Note:** If issues arise, this cleanup touched error-handler, hierarchy, navigation-guard, permissions, querystring-helpers, route-metadata, utils, and all test files
|
|
20
|
+
- **Logger API:** Renamed `enableCategory()` to `setCategoryLevel()` for better clarity
|
|
21
|
+
- Old name was confusing: `enableCategory('ROUTER:SCROLL', 'silent')` reads as "enable to disable"
|
|
22
|
+
- New name is explicit: `setCategoryLevel('ROUTER:SCROLL', 'silent')` clearly sets the level
|
|
23
|
+
- Added `'silent'` to TypeScript type definitions for level parameter
|
|
24
|
+
- No backward compatibility - clean break for clearer API
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- **Breaking:** Standardized Router callback prop naming to camelCase (JavaScript convention)
|
|
28
|
+
- `onrouteLoading` → `onRouteLoading`
|
|
29
|
+
- `onrouteLoaded` → `onRouteLoaded`
|
|
30
|
+
- `onconditionsFailed` → `onConditionsFailed`
|
|
31
|
+
- `onNotFound` remains unchanged (already correct)
|
|
32
|
+
- Updated all documentation, examples, tests, and showcase site
|
|
33
|
+
- **Migration:** Update your Router component props to use camelCase naming
|
|
34
|
+
- **Permissions:** Completely redesigned unauthorized handling system
|
|
35
|
+
- Previously required manual `/unauthorized` route definition and `onUnauthorized` callback with hash-based navigation
|
|
36
|
+
- New system treats unauthorized state as special router state (like 404), not a regular route
|
|
37
|
+
- `/unauthorized` route no longer needs to be defined in routes object
|
|
38
|
+
- Respects configured routing mode (hash/history) instead of forcing hash navigation
|
|
39
|
+
- Two behavior modes available:
|
|
40
|
+
- `unauthorizedBehavior: 'component'` - Shows unauthorized component without changing URL (default)
|
|
41
|
+
- `unauthorizedBehavior: 'navigate'` - Navigates to configured unauthorized route
|
|
42
|
+
- Configure via `configurePermissions()` with new options: `unauthorizedBehavior`, `unauthorizedRoute`, `unauthorizedComponent`
|
|
43
|
+
- Router automatically detects permission failures by checking `routeContext.permissions`
|
|
44
|
+
- `createPermissionCondition()` only calls `onUnauthorized` handler if explicitly configured (backward compatibility)
|
|
45
|
+
- Added internal `hasExplicitHandler()` tracking to distinguish explicit callbacks from defaults
|
|
46
|
+
- **Migration:** Old `onUnauthorized` callback approach still works, new declarative config recommended
|
|
47
|
+
- **Referrer Tracking:** Fixed referrer not being preserved on browser back/forward navigation
|
|
48
|
+
- Previously, pressing back button would show chronological previous route as referrer instead of original referrer
|
|
49
|
+
- Example: `/` → `/links` (referrer: `/`) → `/query` (referrer: `/links`) → [BACK] → `/links` showed referrer `/query` (wrong!) instead of `/` (correct)
|
|
50
|
+
- Root cause: navigationContext with referrer was calculated by router but never saved to history.state
|
|
51
|
+
- Solution: Router now saves calculated navigationContext (with referrer) back to history.state after route loads
|
|
52
|
+
- Added serialization handling for Proxy objects in params (uses JSON serialization fallback when structuredClone fails)
|
|
53
|
+
- Applied to both hash mode and history mode navigation
|
|
54
|
+
- Navigation sequence tracking now correctly increments on forward and decrements on back
|
|
55
|
+
- `goBack()` function simplified to use native browser back (`window.history.back()`) instead of manual push
|
|
56
|
+
- Referrer and scroll position now automatically restored from history.state on back/forward navigation
|
|
57
|
+
- **Permissions:** Fixed `createProtectedRoute()` failing with synchronous component imports
|
|
58
|
+
- Error: "Cannot read properties of undefined (reading 'before')" when using sync imports like `component: AdminPanel`
|
|
59
|
+
- Root cause: `createProtectedRouteDefinition()` always treated components as async, causing Router to call component constructor as function returning `undefined`
|
|
60
|
+
- Solution: Detect sync vs async components - use `component` key for sync (let wrap() handle Promise wrapping) and `asyncComponent` key for async
|
|
61
|
+
- Now supports both patterns: `component: AdminPanel` (sync) and `component: () => import('./Admin.svelte')` (async)
|
|
62
|
+
- Async detection: `typeof component === 'function' && component.length === 0`
|
|
63
|
+
|
|
64
|
+
## [5.0.0-rc12] - 2025-02-12 ✅ Published
|
|
65
|
+
|
|
66
|
+
### Fixed
|
|
67
|
+
- **Critical:** Fixed missing TypeScript source files in published npm package
|
|
68
|
+
- Added `src/lib/**/*.ts` to `package.json` files field
|
|
69
|
+
- Fixes build error: `Rollup failed to resolve import "@keenmate/svelte-spa-router/logger"`
|
|
70
|
+
- The `logger.ts` file was missing from rc11 npm package, breaking production builds
|
|
71
|
+
- Removed deprecated `setDebugLoggingEnabled()` and `getDebugLoggingEnabled()` type declarations from `utils.d.ts`
|
|
72
|
+
- These functions were removed in rc11 but TypeScript definitions remained, causing confusion
|
|
73
|
+
- Use new API: `import { enableLogging, disableLogging } from '@keenmate/svelte-spa-router/logger'`
|
|
74
|
+
- Added missing TypeScript definitions for `helpers/hierarchy` module
|
|
75
|
+
- Created `src/lib/helpers/hierarchy.d.ts` with full type support for `createHierarchy()`
|
|
76
|
+
- Includes `HierarchyNode`, `HierarchyTree`, and `CreateHierarchyOptions` interfaces
|
|
77
|
+
- Fixed logger type resolution in package.json exports
|
|
78
|
+
- Changed `"types": "./src/lib/logger.d.ts"` to `"types": "./src/lib/logger.ts"`
|
|
79
|
+
- TypeScript now correctly resolves types from the source file
|
|
80
|
+
|
|
81
|
+
## [5.0.0-rc11] - 2025-02-01
|
|
82
|
+
|
|
83
|
+
### Changed
|
|
84
|
+
|
|
85
|
+
#### BREAKING: Logging System Migration to loglevel
|
|
86
|
+
- **Migrated from custom logging to loglevel library** (~1KB) with hierarchical categories
|
|
87
|
+
- Consistent with `@keenmate/web-multiselect` logging implementation
|
|
88
|
+
- Uses vendored `loglevel` and `loglevel-plugin-prefix` libraries (ESM versions)
|
|
89
|
+
- **Breaking API change**: `setDebugLoggingEnabled()` replaced with new API
|
|
90
|
+
- Old: `import { setDebugLoggingEnabled } from '@keenmate/svelte-spa-router/utils'`
|
|
91
|
+
- New: `import { enableLogging, disableLogging, setLogLevel, setCategoryLevel } from '@keenmate/svelte-spa-router/logger'`
|
|
92
|
+
- **12 hierarchical categories** for granular control:
|
|
93
|
+
- `ROUTER` - Core routing pipeline, route matching
|
|
94
|
+
- `ROUTER:NAVIGATION` - push, pop, replace, goBack
|
|
95
|
+
- `ROUTER:SCROLL` - Scroll restoration
|
|
96
|
+
- `ROUTER:GUARDS` - Navigation guards
|
|
97
|
+
- `ROUTER:CONDITIONS` - Route condition checks
|
|
98
|
+
- `ROUTER:HIERARCHY` - Hierarchical route inheritance
|
|
99
|
+
- `ROUTER:PERMISSIONS` - Permission checking
|
|
100
|
+
- `ROUTER:ROUTES` - Named routes and URL building
|
|
101
|
+
- `ROUTER:ZONES` - Multi-zone routing
|
|
102
|
+
- `ROUTER:METADATA` - Breadcrumbs and route metadata
|
|
103
|
+
- `ROUTER:ERROR_HANDLER` - Global error handling
|
|
104
|
+
- `ROUTER:FILTERS` - Filter parsing
|
|
105
|
+
- **Enhanced output format**: \`[HH:MM:SS.mmm] [LEVEL] [CATEGORY] message\`
|
|
106
|
+
- **Color-coded by log level**: Blue (debug), Green (info), Orange (warn), Red (error)
|
|
107
|
+
- **Per-category control**: Enable specific categories at different log levels
|
|
108
|
+
\`\`\`javascript
|
|
109
|
+
disableLogging() // Disable all
|
|
110
|
+
setCategoryLevel('ROUTER:SCROLL', 'debug') // Enable only scroll logs
|
|
111
|
+
setCategoryLevel('ROUTER:NAVIGATION', 'info') // Navigation at info level
|
|
112
|
+
\`\`\`
|
|
113
|
+
- **Global level control**: \`setLogLevel('warn')\` to set all categories at once
|
|
114
|
+
- Removed \`src/lib/internal/logging.js\` (custom implementation)
|
|
115
|
+
- Added \`src/lib/logger.ts\` (loglevel-based implementation)
|
|
116
|
+
- Added \`src/lib/vendor/loglevel/\` with ESM library files
|
|
117
|
+
|
|
118
|
+
### Fixed
|
|
119
|
+
- Fixed ESM import error with loglevel library (\`Cannot set properties of undefined\`)
|
|
120
|
+
- Switched from UMD to ESM versions of vendored libraries
|
|
121
|
+
- Added wrapper files (\`index.js\`, \`prefix.js\`) for proper ES module imports
|
|
122
|
+
|
|
123
|
+
## [5.0.0-rc10] - 2025-02-01
|
|
124
|
+
|
|
125
|
+
### Added
|
|
126
|
+
|
|
127
|
+
#### Debug Logging System
|
|
128
|
+
- **Category-based debug logging** - Built-in debug logging to troubleshoot routing issues during development
|
|
129
|
+
- **`setDebugLoggingEnabled()` function** - Enable/disable debug logging with a single call
|
|
130
|
+
- Simple on/off control - no complex configuration needed
|
|
131
|
+
- Disabled by default to keep production consoles clean
|
|
132
|
+
- Example: `setDebugLoggingEnabled(import.meta.env.DEV)`
|
|
133
|
+
- **`getDebugLoggingEnabled()` function** - Check current debug logging state
|
|
134
|
+
- **Color-coded console output** - Easy visual distinction between log categories
|
|
135
|
+
- `[Router]` logs in orange (#ff3e00) - Route matching, component loading, guard execution, metadata updates
|
|
136
|
+
- `[Router:Utils]` logs in green (#10b981) - Navigation functions (push, pop, replace, goBack), scroll restoration
|
|
137
|
+
- **Multiple log levels** - debug, info, warn, error for different severity
|
|
138
|
+
- **Zero overhead when disabled** - If checks can be eliminated by bundlers in production
|
|
139
|
+
- **Internal architecture** - Generic logging utility (`src/lib/internal/logging.js`) not exposed to users
|
|
140
|
+
- `createLogger(category, prefix, color)` factory function
|
|
141
|
+
- `enableLoggingCategory()` / `disableLoggingCategory()` for granular control
|
|
142
|
+
- Logger instances: `routerLogger`, `utilsLogger`
|
|
143
|
+
- **Configuration warnings unaffected** - Critical configuration errors always show regardless of debug setting
|
|
144
|
+
- **Example output**:
|
|
145
|
+
```
|
|
146
|
+
[Router] Running pipeline for: /document/123
|
|
147
|
+
[Router] Route loaded successfully: /document/:id
|
|
148
|
+
[Router:Utils] Called - navigationContext: { source: 'menu' }
|
|
149
|
+
[Router] Scroll effect triggered - restoreScrollState: true
|
|
150
|
+
```
|
|
151
|
+
- **TypeScript support** - Full type definitions with comprehensive JSDoc
|
|
152
|
+
- Package export: `@keenmate/svelte-spa-router/utils` (setDebugLoggingEnabled, getDebugLoggingEnabled)
|
|
153
|
+
|
|
154
|
+
## [5.0.0-rc09] - 2025-01-30
|
|
155
|
+
|
|
156
|
+
### Added
|
|
157
|
+
|
|
158
|
+
#### Tree/Nested Route Structure (Alternative API)
|
|
159
|
+
- **`createHierarchy()` helper** - Define routes in a tree structure as an alternative to flat definitions
|
|
160
|
+
- Child paths automatically concatenated to parent paths (relative paths)
|
|
161
|
+
- Routes automatically inherit breadcrumbs, permissions, conditions, and authorization from parents
|
|
162
|
+
- Optional route names - only add when needed for programmatic navigation
|
|
163
|
+
- Coexists seamlessly with flat route definitions
|
|
164
|
+
- Example:
|
|
165
|
+
```javascript
|
|
166
|
+
import { createHierarchy } from '@keenmate/svelte-spa-router/helpers/hierarchy'
|
|
167
|
+
|
|
168
|
+
const routes = createHierarchy({
|
|
169
|
+
'/admin': {
|
|
170
|
+
name: 'admin',
|
|
171
|
+
component: AdminLayout,
|
|
172
|
+
breadcrumbs: [{ label: 'Admin' }],
|
|
173
|
+
permissions: { any: ['admin'] },
|
|
174
|
+
children: {
|
|
175
|
+
'users': {
|
|
176
|
+
name: 'adminUsers',
|
|
177
|
+
component: AdminUsers,
|
|
178
|
+
breadcrumbs: [{ label: 'Users' }],
|
|
179
|
+
children: {
|
|
180
|
+
':id': {
|
|
181
|
+
name: 'adminUserDetail',
|
|
182
|
+
component: AdminUserDetail
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
// Results in: /admin, /admin/users, /admin/users/:id
|
|
190
|
+
```
|
|
191
|
+
- Requires hierarchical mode enabled: `setHierarchicalRoutesEnabled(true)`
|
|
192
|
+
- Package export: `@keenmate/svelte-spa-router/helpers/hierarchy`
|
|
193
|
+
|
|
194
|
+
#### Named Routes Enhancement
|
|
195
|
+
- **`getRouteByName()` function** - Get route pattern by name
|
|
196
|
+
- Returns the path pattern for a registered route name
|
|
197
|
+
- Useful for testing and debugging
|
|
198
|
+
- Example: `getRouteByName('documentDetail')` returns `'/documents/:documentId'`
|
|
199
|
+
|
|
200
|
+
#### Automatic Referrer Tracking System
|
|
201
|
+
- **Configurable referrer tracking** - Router automatically tracks previous route information and injects it into navigationContext
|
|
202
|
+
- **`setIncludeReferrer()` configuration** - Control when referrer info is injected
|
|
203
|
+
- `'never'` - Disable referrer tracking (default)
|
|
204
|
+
- `'notfound'` - Only inject referrer for 404/NotFound routes
|
|
205
|
+
- `'always'` - Inject referrer for all route navigations
|
|
206
|
+
- **Referrer object structure** - Complete information about the previous route:
|
|
207
|
+
- `location` - Previous route path (e.g., '/documents/123')
|
|
208
|
+
- `querystring` - Previous route query string (e.g., 'tab=info&view=grid')
|
|
209
|
+
- `params` - Previous route parameters object (e.g., { id: '123' })
|
|
210
|
+
- `routeName` - Previous route name if it was registered (e.g., 'documentDetail'), or URL path as fallback
|
|
211
|
+
- **NotFound integration** - When catch-all route (`'*'`) matches, router automatically injects:
|
|
212
|
+
- `attemptedRoute` - The URL that was not found
|
|
213
|
+
- `attemptedQuerystring` - Query string from the attempted route
|
|
214
|
+
- `referrer` - Complete previous route information (see structure above)
|
|
215
|
+
- **Zero programmer effort** - Works automatically once configured
|
|
216
|
+
- **Safe "Go Back" implementation** - Using `push()` with explicit URL instead of `history.back()`
|
|
217
|
+
- Safer than `history.back()` which breaks with `replace()` navigation
|
|
218
|
+
- Allows conditional logic (e.g., don't go back to unauthorized routes)
|
|
219
|
+
- Supports custom fallback destinations
|
|
220
|
+
- **Works correctly with both `push()` and `replace()` navigation**
|
|
221
|
+
- **Timing behavior** - On first navigation after referrer is updated, component $effects may run twice:
|
|
222
|
+
- First run: location changes but referrer not yet injected (sees undefined)
|
|
223
|
+
- Second run: navigationContext updates with referrer (sees correct value)
|
|
224
|
+
- This is expected Svelte 5 behavior and doesn't affect functionality
|
|
225
|
+
- Subsequent navigations are smooth with single $effect run
|
|
226
|
+
- UI always renders correctly on the second run
|
|
227
|
+
- **Example usage in NotFound component**:
|
|
228
|
+
```javascript
|
|
229
|
+
const navContext = $derived(navigationContext())
|
|
230
|
+
const referrer = $derived(navContext?.referrer)
|
|
231
|
+
const canGoBack = $derived(referrer?.location && referrer.location !== '/')
|
|
232
|
+
|
|
233
|
+
function goBack() {
|
|
234
|
+
const returnPath = referrer?.location || '/'
|
|
235
|
+
const returnQuery = referrer?.querystring
|
|
236
|
+
const returnUrl = returnQuery ? `${returnPath}?${returnQuery}` : returnPath
|
|
237
|
+
push(returnUrl)
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
- **Example usage in protected routes** (avoid going back to authorization failures):
|
|
241
|
+
```javascript
|
|
242
|
+
const navContext = $derived(navigationContext())
|
|
243
|
+
const referrer = $derived(navContext?.referrer)
|
|
244
|
+
const returnPath = $derived(referrer?.location || navContext?.returnTo || '/')
|
|
245
|
+
const returnQuery = $derived(referrer?.querystring || navContext?.returnQuery)
|
|
246
|
+
|
|
247
|
+
// Referrer tracks last SUCCESSFULLY loaded route, not attempted routes that failed auth
|
|
248
|
+
```
|
|
249
|
+
- Example implementations:
|
|
250
|
+
- `example/src/routes/NotFound.svelte` - 404 page with "Go Back"
|
|
251
|
+
- `example/src/routes/Unauthorized.svelte` - Authorization failure with safe "Go Back"
|
|
252
|
+
- `example/src/routes/User.svelte` - Regular page with referrer-based navigation
|
|
253
|
+
- `example/src/routes/ReferrerDemo.svelte` - Comprehensive demo with console logging
|
|
254
|
+
|
|
255
|
+
- **`setNavigationContext()` function** - Exported for advanced use cases
|
|
256
|
+
- Allows manual setting of navigation context
|
|
257
|
+
- Used internally by router for referrer auto-injection
|
|
258
|
+
- Example: `setNavigationContext({ customData: 123 })`
|
|
259
|
+
|
|
260
|
+
### Fixed
|
|
261
|
+
|
|
262
|
+
#### Named Routes Navigation
|
|
263
|
+
- **Fixed `push()` and `replace()` with single-argument named routes** - Resolved "Invalid parameter location" error
|
|
264
|
+
- Issue: Calling `push('routeName')` with a single named route argument threw error
|
|
265
|
+
- Root cause: Legacy signature detection treated non-slash strings as href paths instead of named routes
|
|
266
|
+
- Solution: Added check in legacy signature branch - strings without leading `/` or `#/` are treated as named routes
|
|
267
|
+
- Now works: `push('about')`, `push('userProfile')`, `replace('home')`
|
|
268
|
+
- Multi-param signature still required for routes with params: `push('userProfile', {id: 123})`
|
|
269
|
+
|
|
270
|
+
#### Hierarchical Routes & Breadcrumb System
|
|
271
|
+
- **Fixed infinite loop in route metadata updates** - Resolved state comparison issues causing Router to re-render continuously
|
|
272
|
+
- Issue: Router's `$effect()` was triggering on every update due to `routeContext` object reference changes
|
|
273
|
+
- Root cause: `updateRouteMetadata()` created new objects every call, even when values didn't change
|
|
274
|
+
- Solution 1: Added `lastAssignedContext` reference tracking to only update when context reference actually changes
|
|
275
|
+
- Solution 2: Implemented route key tracking (`${location}|${querystring}|${JSON.stringify(params)}`) to detect real route changes vs metadata updates
|
|
276
|
+
- Fixed `$state` proxy comparison warnings by using `$state.snapshot()` for value comparisons
|
|
277
|
+
|
|
278
|
+
- **Breadcrumb cache system for preserving manual updates** - Child route navigation no longer resets dynamic breadcrumbs to "Loading..."
|
|
279
|
+
- Issue: Navigating from `/documents/1` to `/documents/1/logs` reset "Document 1" breadcrumb back to "Loading..."
|
|
280
|
+
- Root cause: Router composed fresh breadcrumbs on every route change, losing manual `updateBreadcrumb()` calls
|
|
281
|
+
- Solution: Implemented breadcrumb cache (`updatedBreadcrumbsCache` Map) to preserve manual updates across child route navigation
|
|
282
|
+
- Cache intelligently clears only when navigating to different base routes (not child routes)
|
|
283
|
+
- `updateBreadcrumb(id, updates)` stores updates in cache, Router applies them during breadcrumb composition
|
|
284
|
+
|
|
285
|
+
- **Child component breadcrumb initialization pattern** - Direct child route reloads now show correct parent breadcrumbs
|
|
286
|
+
- Issue: Reloading `/documents/1/logs` directly showed "Loading..." for parent breadcrumb because parent component never mounted
|
|
287
|
+
- Pattern: Child components call `updateBreadcrumb()` in `onMount()` to update their parent's dynamic breadcrumbs
|
|
288
|
+
- Example: DocumentLogs component updates 'documentDetail' breadcrumb with actual document name on mount
|
|
289
|
+
- Works with breadcrumb cache to ensure updates persist across subsequent child navigation
|
|
290
|
+
- Applied to all child components in test apps: DocumentLogs, DocumentPermissions, DocumentHistory, AdminUserPermissions, AdminUserActivity
|
|
291
|
+
|
|
292
|
+
### Documentation
|
|
293
|
+
- **Added comprehensive documentation showcase** - New SvelteKit-based documentation site
|
|
294
|
+
- Built with @keenmate/svelte-docs for consistent styling and components
|
|
295
|
+
- Complete API reference with all functions, parameters, and return types
|
|
296
|
+
- Feature guides: Routing Modes, Route Configuration, Parameters, Guards, Named Routes, Programmatic Navigation, Querystring, Filters, Permissions, Multi-Zone Routing, Hierarchical Routes, Nested Routes
|
|
297
|
+
- Interactive code examples with syntax highlighting
|
|
298
|
+
- Live demo apps for both hash and history modes
|
|
299
|
+
- Deployed at https://svelte-spa-router.keenmate.dev
|
|
300
|
+
- Demo apps: https://history.svelte-spa-router.keenmate.dev and https://hash.svelte-spa-router.keenmate.dev
|
|
301
|
+
- **Added Multi-Zone Routing documentation** - Comprehensive guide for multi-zone layouts
|
|
302
|
+
- New showcase page at `/features/multi-zone`
|
|
303
|
+
- Visual diagram showing zone layout structure
|
|
304
|
+
- Complete examples: route configuration, layout setup, zone components
|
|
305
|
+
- Covers async loading, route parameters, guards, permissions, and metadata in zones
|
|
306
|
+
- Use cases: admin dashboards, email clients, music players, document editors
|
|
307
|
+
- Best practices and responsive layout patterns
|
|
308
|
+
- **Updated CLAUDE.md** - Added comprehensive section on tree/nested route structure
|
|
309
|
+
- **Added examples** - Tree structure examples in main example app
|
|
310
|
+
|
|
311
|
+
## [5.0.0-rc08] - 2025-10-26
|
|
312
|
+
|
|
313
|
+
### Breaking Changes
|
|
314
|
+
- **BREAKING: Renamed `params` to `routeParams`** for clarity
|
|
315
|
+
- Component prop: `let { params } = $props()` → `let { routeParams } = $props()`
|
|
316
|
+
- Function call: `params()` → `routeParams()`
|
|
317
|
+
- This makes it immediately clear these are route parameters from URL patterns
|
|
318
|
+
- Update all route components to use `routeParams` instead of `params`
|
|
319
|
+
- TypeScript: `params<T>()` → `routeParams<T>()`
|
|
320
|
+
|
|
321
|
+
## [5.0.0-rc07] - 2025-10-26
|
|
322
|
+
|
|
323
|
+
### Fixed
|
|
324
|
+
- **Critical: Tree-shaking issue in production builds** - Added `sideEffects` field to package.json to prevent Vite from incorrectly tree-shaking `.svelte` and `.svelte.js` files during production builds
|
|
325
|
+
- Fixes "link is not defined" and similar errors in production builds with npm package
|
|
326
|
+
- `.svelte.js` files contain Svelte 5 runes (`$state`, `$derived`, `$effect`) at module level which have side effects and must not be tree-shaken
|
|
327
|
+
- Issue only affected production builds, not development mode
|
|
328
|
+
|
|
329
|
+
## [5.0.0-rc06] - 2025-10-26
|
|
330
|
+
|
|
331
|
+
### Added
|
|
332
|
+
|
|
333
|
+
#### Multi-Parameter Navigation & Strict Parameter Replacement
|
|
334
|
+
- **Multi-parameter signature for `push()` and `replace()`** - Natural function call style
|
|
335
|
+
- `push(route, routeParams, queryString, navigationContext)`
|
|
336
|
+
- `replace(route, routeParams, queryString, navigationContext)`
|
|
337
|
+
- Route resolution: Starts with `/` = exact path, otherwise = named route lookup
|
|
338
|
+
- Examples: `push('userProfile', { userId: 123 }, { tab: 'settings' })`
|
|
339
|
+
- Backward compatible with all existing formats (string, array, object)
|
|
340
|
+
|
|
341
|
+
- **4-element array support** - Navigation context in arrays
|
|
342
|
+
- `push(['route', params, query, navigationContext])`
|
|
343
|
+
- `link={['route', params, query, navigationContext]}`
|
|
344
|
+
- Example: `<a use:link={['bookDetail', {bookId: 123}, {tab: 'reviews'}, {source: 'list'}]}>`
|
|
345
|
+
|
|
346
|
+
- **Strict parameter replacement with placeholder**
|
|
347
|
+
- `setParamReplacementPlaceholder(value)` - Configure placeholder for missing params (default: 'N-A')
|
|
348
|
+
- Missing params replaced with placeholder instead of being removed
|
|
349
|
+
- Predictable URLs: `/users/:userId/:section` with missing section → `/users/123/N-A`
|
|
350
|
+
- Triggers `onNotFound` callback for error tracking
|
|
351
|
+
|
|
352
|
+
#### Resource-Based Authorization
|
|
353
|
+
- **`authorizationCallback` parameter for `createProtectedRoute()`** - Combine role + resource checks
|
|
354
|
+
- Supports both role-based (permissions) and resource-based (authorizationCallback) authorization
|
|
355
|
+
- Conditions execute in order: permissions first (fast), then authorizationCallback (API call)
|
|
356
|
+
- Example: Check if user has 'editor' role, then check if they can access specific document
|
|
357
|
+
- Perfect for document access, resource ownership, dynamic permissions
|
|
358
|
+
- Callback receives full route detail: `{ route, location, params, query, routeContext, navigationContext }`
|
|
359
|
+
|
|
360
|
+
#### Global Error Handler System
|
|
361
|
+
- **Production-ready global error handler** for catching and recovering from unhandled errors
|
|
362
|
+
- `configureGlobalErrorHandler()` - Configure error handling behavior in `main.js`
|
|
363
|
+
- `GlobalErrorHandler` component - Wraps your app and catches all errors
|
|
364
|
+
- `ErrorDisplay` component - Beautiful default error UI with recovery options
|
|
365
|
+
- **Recovery Strategies**: `navigateSafe`, `restart`, `showError`, `custom`
|
|
366
|
+
- **Loop Prevention**: SessionStorage-based restart tracking prevents infinite reload loops
|
|
367
|
+
- **Custom Callbacks**: `onError` for logging/monitoring, `onRecover` for custom recovery logic
|
|
368
|
+
- **Helper Functions**: `restart()`, `navigate()`, `showError()`, `canRestart()`, `getRestartCount()`
|
|
369
|
+
- **Error Filtering**: Ignore known non-critical errors (ResizeObserver, etc.)
|
|
370
|
+
- **UI Options**: Toast notifications, full-page error component, or custom error component
|
|
371
|
+
- **TypeScript Support**: Full type definitions for all APIs
|
|
372
|
+
- **Example Integration**: Working demo with Sentry integration example
|
|
373
|
+
|
|
374
|
+
#### 404 Not Found Tracking
|
|
375
|
+
- **`onNotFound` callback on Router component** - Track 404s for analytics/monitoring
|
|
376
|
+
- Fires when catch-all route (`'*'`) matches (user sees 404 page)
|
|
377
|
+
- Fires when no route matches at all (no 404 page defined)
|
|
378
|
+
- Perfect for logging to Sentry, Google Analytics, or other monitoring services
|
|
379
|
+
- Event detail includes `{ location, querystring }`
|
|
380
|
+
- Example: `<Router {routes} onNotFound={(e) => Sentry.captureMessage('404', { extra: e.detail })} />`
|
|
381
|
+
|
|
382
|
+
#### Convenient Route Creation API
|
|
383
|
+
- **New `createRoute()` and `createRouteDefinition()` functions** for easier route configuration
|
|
384
|
+
- `createRoute()` - Returns already wrapped component (most convenient, no `wrap()` needed)
|
|
385
|
+
- `createRouteDefinition()` - Returns route definition for use with `wrap()` (advanced use)
|
|
386
|
+
- Consistent API pattern matching `createProtectedRoute()` / `createProtectedRouteDefinition()`
|
|
387
|
+
- Support for `title` and `breadcrumbs` metadata directly in route options
|
|
388
|
+
- Automatic detection of sync vs async components
|
|
389
|
+
|
|
390
|
+
- **Enhanced Permission System**
|
|
391
|
+
- `createProtectedRoute()` - Now returns already wrapped component (breaking change from definition-only)
|
|
392
|
+
- `createProtectedRouteDefinition()` - New function that returns definition (replaces old `createProtectedRoute()` behavior)
|
|
393
|
+
- Maintains backward compatibility for existing `wrap(createProtectedRoute(...))` usage
|
|
394
|
+
|
|
395
|
+
- **Route Metadata Support**
|
|
396
|
+
- `title` - Set page title for routes
|
|
397
|
+
- `breadcrumbs` - Define breadcrumb trail with `{ label, path? }` structure
|
|
398
|
+
- Metadata stored in `routeContext` object, accessible in route events and components
|
|
399
|
+
|
|
400
|
+
#### Dynamic Metadata & Loading Control
|
|
401
|
+
- **Reactive Metadata Helpers**: `helpers/route-metadata.svelte.js`
|
|
402
|
+
- `routeTitle()` - Get current route title reactively
|
|
403
|
+
- `routeBreadcrumbs()` - Get current breadcrumb trail reactively
|
|
404
|
+
- `routeContext()` - Get full route context reactively
|
|
405
|
+
- `updateRouteMetadata(routeContext)` - Update metadata after data loads (e.g., change title from "Document" to "Invoice.pdf")
|
|
406
|
+
- **`updateTitle(title)` - Update just the title** (simpler than updateRouteMetadata)
|
|
407
|
+
- **`updateBreadcrumb(id, updates)` - Partial breadcrumb updates** (update specific segments by ID)
|
|
408
|
+
- Automatically updated by Router on route changes
|
|
409
|
+
|
|
410
|
+
- **Partial Breadcrumb Updates** (NEW!)
|
|
411
|
+
- Add `id` property to breadcrumb items in route config: `{ id: 'docDetail', label: 'Loading...', path: '/doc/:id' }`
|
|
412
|
+
- Update specific breadcrumbs after data loads: `updateBreadcrumb('docDetail', { label: 'Invoice.pdf', path: '/doc/123' })`
|
|
413
|
+
- **No need to replace the entire breadcrumbs array** - only update what changes!
|
|
414
|
+
- Perfect for nested paths: `/documents/:id/logs/:logId` where each segment needs dynamic data
|
|
415
|
+
- Static segments (Home, Documents, etc.) stay unchanged
|
|
416
|
+
|
|
417
|
+
- **Flexible Loading Control** with `shouldDisplayLoadingOnRouteLoad` flag
|
|
418
|
+
- **Pattern 1 (Router-managed with loadingComponent)**: Set `shouldDisplayLoadingOnRouteLoad: true` - Router keeps loading component visible until component calls `hideLoading()`
|
|
419
|
+
- **Pattern 2 (Component-managed)**: Default behavior - component handles its own loading state
|
|
420
|
+
- **Pattern 3 (Global overlay)**: User-defined global loading UI in App.svelte that reacts to `routeIsLoading()`
|
|
421
|
+
- `showLoading()` - Manually show loading state (triggers global overlay if defined)
|
|
422
|
+
- `hideLoading()` - Component signals data is loaded (hides loading component/overlay)
|
|
423
|
+
- `routeIsLoading()` - Check if route is currently loading (reactive state)
|
|
424
|
+
- Perfect for routes that fetch data and need dynamic titles/breadcrumbs
|
|
425
|
+
- Supports multi-zone layouts (toolpanel + content) with different loading UIs per zone
|
|
426
|
+
|
|
427
|
+
**Use cases**:
|
|
428
|
+
- Document detail page: Show "Document" while loading, then "Invoice template.pdf" after data loads
|
|
429
|
+
- User profile: Show "User Profile" while loading, then "John Doe" after data loads
|
|
430
|
+
- Product page: Show "Product" while loading, then "iPhone 15 Pro" after data loads
|
|
431
|
+
- Nested paths: `/documents/:id/logs` where both document name and "Logs" need to be in breadcrumbs
|
|
432
|
+
|
|
433
|
+
#### Named Routes Enhancement
|
|
434
|
+
- **Array/Object Syntax for Navigation**: `push()` and `replace()` functions now support the same convenient array/object syntax as the `link` action
|
|
435
|
+
- Array format: `push(['userProfile', { userId: 123 }, { tab: 'settings' }])`
|
|
436
|
+
- Object format: `push({ route: 'userProfile', params: { userId: 123 }, query: { tab: 'settings' } })`
|
|
437
|
+
- String format (legacy): `push('/about')` - still fully supported for backward compatibility
|
|
438
|
+
- Eliminates the need to manually call `buildUrl()` for programmatic navigation
|
|
439
|
+
- See `example-history/src/routes/LinksDemo.svelte` for interactive demos
|
|
440
|
+
|
|
441
|
+
### Added - Querystring & Filter System
|
|
442
|
+
|
|
443
|
+
#### Querystring Helpers
|
|
444
|
+
- **Shared Reactive State**: `helpers/querystring.svelte.js`
|
|
445
|
+
- `configureQuerystring(options)` - Configure querystring parsing for entire app
|
|
446
|
+
- `query<T>()` - Get reactive parsed querystring with TypeScript generics
|
|
447
|
+
- Auto-detection of array formats (repeat vs comma-separated)
|
|
448
|
+
- Configure once, use everywhere pattern
|
|
449
|
+
|
|
450
|
+
- **Querystring Utilities**: `helpers/querystring-helpers.svelte.js`
|
|
451
|
+
- `parseQuerystring(qs, options)` - Parse querystring with array format support
|
|
452
|
+
- `stringifyQuerystring(obj, options)` - Convert object to querystring
|
|
453
|
+
- `getParsedQuerystring(options)` - Get parsed querystring (non-reactive)
|
|
454
|
+
- `updateQuerystring(updates, options)` - Update URL querystring (partial or full)
|
|
455
|
+
- `createQuerystringHelpers(parser, stringifier)` - Custom parser support
|
|
456
|
+
|
|
457
|
+
- **Array Format Support**:
|
|
458
|
+
- `'auto'` (default) - Auto-detects both repeat and comma formats
|
|
459
|
+
- `'repeat'` - `?tags=foo&tags=bar` (multiple parameters with same key)
|
|
460
|
+
- `'comma'` - `?tags=foo,bar,baz` (comma-separated values)
|
|
461
|
+
|
|
462
|
+
#### Filter System
|
|
463
|
+
- **Flexible Filters**: `helpers/filters.svelte.js`
|
|
464
|
+
- `configureFilters(options)` - Configure filter mode and parsing
|
|
465
|
+
- `filters<T>()` - Get reactive parsed filters with TypeScript generics
|
|
466
|
+
- `updateFilters<T>(updates, options)` - Update filters with type safety
|
|
467
|
+
- `getFiltersConfig()` - Get current filter configuration
|
|
468
|
+
|
|
469
|
+
- **Dual Mode Support**:
|
|
470
|
+
- **Flat Mode** (default): Each filter as separate query parameter
|
|
471
|
+
- Example: `?search=java&category=books&status=active`
|
|
472
|
+
- **Structured Mode**: Single parameter with custom syntax
|
|
473
|
+
- Example: `?$filter=search eq 'java' AND category eq 'books'`
|
|
474
|
+
- Supports custom parse/stringify functions for OData, Microsoft Graph API, etc.
|
|
475
|
+
|
|
476
|
+
#### TypeScript Support
|
|
477
|
+
- Full generic support for type-safe parameter access:
|
|
478
|
+
- `params<T>()` - Route parameters with intellisense
|
|
479
|
+
- `query<T>()` - Query parameters with intellisense
|
|
480
|
+
- `filters<T>()` - Filter parameters with intellisense
|
|
481
|
+
- `updateFilters<T>(updates, options)` - Type-safe filter updates
|
|
482
|
+
|
|
483
|
+
#### Value Handling
|
|
484
|
+
- **Filters**:
|
|
485
|
+
- `undefined` - Always removes the parameter
|
|
486
|
+
- `null` - Keeps parameter with empty value
|
|
487
|
+
- **Querystring**:
|
|
488
|
+
- `undefined` - Always removes the parameter
|
|
489
|
+
- `null` - Controlled by `dropNull` option (default: true removes it)
|
|
490
|
+
- Empty string - Controlled by `dropEmpty` option (default: false keeps it)
|
|
491
|
+
|
|
492
|
+
#### Example Applications
|
|
493
|
+
- `example-history/src/routes/QuerystringDemo.svelte` - Interactive querystring demo
|
|
494
|
+
- `example-history/src/routes/FiltersDemo.svelte` - Filter system with products demo
|
|
495
|
+
- `example-history/src/routes/RouteDataDemo.svelte` - Route data extraction examples
|
|
496
|
+
|
|
497
|
+
### Fixed
|
|
498
|
+
|
|
499
|
+
#### Router Core Fixes
|
|
500
|
+
- **Router Initialization**: Fixed location state initialization to be reactive to config changes
|
|
501
|
+
- Issue: Direct URL access (e.g., `http://localhost:5050/querystring-demo`) showed homepage
|
|
502
|
+
- Solution: Changed from `$state(getLocation())` to `$derived.by()` to react to config changes
|
|
503
|
+
- Now correctly reads `setHashRoutingEnabled()` before initializing location state
|
|
504
|
+
|
|
505
|
+
#### Loading State Fixes
|
|
506
|
+
- **Component Double Mounting**: Fixed race condition causing components to mount twice
|
|
507
|
+
- Issue: Router template had component in two separate `{:else if}` blocks - one hidden, one visible
|
|
508
|
+
- When `isWaitingForData` changed, Svelte unmounted from first block and remounted in second
|
|
509
|
+
- Resulted in `onMount()` running twice, causing duplicate data fetches and delays
|
|
510
|
+
- Solution: Refactored template to keep component in single block with `style:display` toggle
|
|
511
|
+
- Component now mounts once and visibility is controlled via CSS
|
|
512
|
+
|
|
513
|
+
- **Component Params Not Available on Mount**: Fixed params being empty when component loads
|
|
514
|
+
- Issue: Router set `isWaitingForData = true` and waited for `hideLoading()` BEFORE setting `componentParams`
|
|
515
|
+
- Component mounted without params, causing "params not ready" errors
|
|
516
|
+
- Solution: Moved `componentParams`, `componentProps`, `componentrouteContext` assignment BEFORE waiting logic
|
|
517
|
+
- Component now has access to params immediately on mount
|
|
518
|
+
|
|
519
|
+
- **Global vs Route Loading Conflict**: Fixed overlapping loading indicators
|
|
520
|
+
- Issue: Both global loader and route-specific loader showed simultaneously
|
|
521
|
+
- Solution: Added `shouldShowGlobalLoading()` helper that only returns true when route doesn't have custom loading
|
|
522
|
+
- Added `hasCustomLoadingComponent` flag set by `startRouteLoading(hasCustomComponent)`
|
|
523
|
+
- Global loader now only shows for routes without custom loading components
|
|
524
|
+
|
|
525
|
+
- **Manual Loading Not Showing**: Fixed `showLoading()` not displaying global loader
|
|
526
|
+
- Issue: `showLoading()` set `isRouteLoading = true` but didn't reset `hasCustomLoadingComponent` flag
|
|
527
|
+
- If current route had custom loading, `shouldShowGlobalLoading()` returned false
|
|
528
|
+
- Solution: Made `showLoading()` also set `hasCustomLoadingComponent = false`
|
|
529
|
+
- Manual loading triggers now correctly show global loader
|
|
530
|
+
|
|
531
|
+
#### Authorization & Navigation Context Fixes
|
|
532
|
+
- **"Go Back" After Unauthorized**: Fixed return navigation after authorization failures
|
|
533
|
+
- Issue: Unauthorized page's "Return to Home" button always went to home instead of previous location
|
|
534
|
+
- Root cause: Using `push('/unauthorized', { data })` was ambiguous - interpreted as routeParams instead of navigationContext
|
|
535
|
+
- Solution: Changed all unauthorized redirects to use explicit 4-parameter signature: `push(route, {}, {}, navContext)`
|
|
536
|
+
- Added `returnTo` and `returnQuery` to navigation context for proper "Go Back" functionality
|
|
537
|
+
- Unauthorized page now displays "Go Back" button when `returnTo` is available
|
|
538
|
+
|
|
539
|
+
- **Document Authorization Navigation**: Fixed document access redirects
|
|
540
|
+
- Updated `authorizationCallback` to pass `returnTo` and `returnQuery` via navigationContext
|
|
541
|
+
- Fixed "View Document" buttons to pass navigation context for proper back navigation
|
|
542
|
+
- All authorization demo flows now preserve return path for better UX
|
|
543
|
+
|
|
544
|
+
#### Protected Route Fixes
|
|
545
|
+
- **Async Component Params Empty**: Fixed params not being passed to protected routes
|
|
546
|
+
- Issue: `createProtectedRouteDefinition` was using `asyncComponent: component` which caused wrap() to double-wrap async imports
|
|
547
|
+
- Solution: Kept `asyncComponent: component` which properly passes async imports to wrap()
|
|
548
|
+
- Protected routes now receive params correctly when using `() => import('./Component.svelte')`
|
|
549
|
+
|
|
550
|
+
#### Example Application Fixes
|
|
551
|
+
- **Missing Routes**: Added missing example routes
|
|
552
|
+
- Added `/about` route (About component)
|
|
553
|
+
- Added `/user/:first/:last?` route (User component with optional last name)
|
|
554
|
+
- Links in LinksDemo now work correctly
|
|
555
|
+
|
|
556
|
+
- **Navigation Context Demo**: Fixed incorrect route paths and push signatures
|
|
557
|
+
- Changed `/context-demo` references to correct `/navigation-context-demo` route
|
|
558
|
+
- Fixed `backToList()` function to navigate to correct route
|
|
559
|
+
- Updated code examples to show proper 4-parameter signature
|
|
560
|
+
- Fixed "View" and "Edit" buttons to use correct navigation context syntax
|
|
561
|
+
|
|
562
|
+
- **HTML Entity Escaping**: Fixed Svelte parse errors
|
|
563
|
+
- Changed unescaped `{}` in code examples to HTML entities `{}`
|
|
564
|
+
- Prevents Svelte from treating them as reactive expressions
|
|
565
|
+
|
|
566
|
+
### Documentation
|
|
567
|
+
- Updated `README.md` with comprehensive querystring and filter system documentation
|
|
568
|
+
- Added TypeScript generic examples throughout
|
|
569
|
+
- Added Quick Reference section with all available imports
|
|
570
|
+
- Updated main features list to highlight TypeScript and URL helpers
|
|
571
|
+
|
|
572
|
+
## [1.0.0] - 2024
|
|
573
|
+
|
|
574
|
+
### Package Information
|
|
575
|
+
|
|
576
|
+
- **Package Name:** @keenmate/svelte-spa-router
|
|
577
|
+
- **Publisher:** KeenMate (https://keenmate.com)
|
|
578
|
+
- **Repository:** https://github.com/keenmate/svelte-spa-router
|
|
579
|
+
|
|
580
|
+
### Added - Svelte 5 Conversion
|
|
581
|
+
|
|
582
|
+
#### Core Router
|
|
583
|
+
- Complete rewrite using Svelte 5 runes (`$state`, `$props`, `$effect`)
|
|
584
|
+
- Reactive location tracking with rune-based state management
|
|
585
|
+
- Event handlers converted from `on:` directives to callback props
|
|
586
|
+
- Maintained full backward compatibility in route definition syntax
|
|
587
|
+
|
|
588
|
+
#### Dual-Mode Routing System
|
|
589
|
+
- **Hash Mode (Default)**: Traditional hash-based routing (`#/path`)
|
|
590
|
+
- No configuration required
|
|
591
|
+
- Works everywhere, including `file://` protocol
|
|
592
|
+
- Perfect for static hosting
|
|
593
|
+
|
|
594
|
+
- **History Mode (New)**: Clean URL routing (`/path`)
|
|
595
|
+
- Uses History API for clean URLs without hash
|
|
596
|
+
- Supports modifier keys (Ctrl+Click to open in new tab)
|
|
597
|
+
- Respects `target` attribute on links
|
|
598
|
+
- Requires server configuration to serve index.html for all routes
|
|
599
|
+
|
|
600
|
+
#### Configuration Functions
|
|
601
|
+
- `setHashRoutingEnabled(boolean)` - Toggle between hash and history mode
|
|
602
|
+
- `setBasePath(string)` - Set base path for history mode
|
|
603
|
+
- `getHashRoutingEnabled()` - Get current routing mode
|
|
604
|
+
- `getBasePath()` - Get current base path
|
|
605
|
+
|
|
606
|
+
#### Permission System (New)
|
|
607
|
+
- `helpers/permissions.svelte.js` - Comprehensive RBAC system
|
|
608
|
+
- `configurePermissions(config)` - Setup permission checking logic
|
|
609
|
+
- `createPermissionCondition(requirements)` - Create route guards
|
|
610
|
+
- `createProtectedRoute(options)` - Helper for protected routes
|
|
611
|
+
- `hasPermission(requirements)` - UI-level permission checking
|
|
612
|
+
- Support for `any` (OR) and `all` (AND) permission logic
|
|
613
|
+
- Flexible integration with existing route guards
|
|
614
|
+
|
|
615
|
+
#### URL Helpers
|
|
616
|
+
- `helpers/url-helpers.svelte.js` - Path manipulation utilities
|
|
617
|
+
- `joinPaths(...paths)` - Intelligent path joining with slash handling
|
|
618
|
+
|
|
619
|
+
#### Enhanced Active Link Detection
|
|
620
|
+
- Updated to work with both hash and history modes
|
|
621
|
+
- Automatic CSS class application on matching routes
|
|
622
|
+
- Pattern matching support
|
|
623
|
+
|
|
624
|
+
### Changed
|
|
625
|
+
|
|
626
|
+
#### Naming Changes (Non-Breaking in Usage, Breaking for Type Imports)
|
|
627
|
+
- **`userData` → `routeContext`**: Renamed for clarity
|
|
628
|
+
- Refers to static route-level configuration data
|
|
629
|
+
- `wrap({ routeContext: { ... } })`
|
|
630
|
+
- `routeContext()` helper function
|
|
631
|
+
- All TypeScript interfaces updated
|
|
632
|
+
|
|
633
|
+
- **`context` → `navigationContext`**: Renamed for clarity
|
|
634
|
+
- Refers to dynamic data passed during navigation (doesn't appear in URL)
|
|
635
|
+
- `push('/path', { orderId: 123 })` - second parameter is navigationContext
|
|
636
|
+
- `navigationContext()` accessor function
|
|
637
|
+
- `wrap({ navigationContext: { ... } })`
|
|
638
|
+
|
|
639
|
+
These changes clarify the distinction between:
|
|
640
|
+
- **routeContext**: Static metadata defined in route configuration
|
|
641
|
+
- **navigationContext**: Dynamic data passed at navigation time (WinForms-like experience)
|
|
642
|
+
|
|
643
|
+
#### API Changes (Breaking)
|
|
644
|
+
- **Stores → Functions**:
|
|
645
|
+
- `$location` → `location()`
|
|
646
|
+
- `$querystring` → `querystring()`
|
|
647
|
+
- `$params` → `params()`
|
|
648
|
+
- `$loc` → `loc()`
|
|
649
|
+
|
|
650
|
+
- **Events → Props**:
|
|
651
|
+
- `on:routeLoading` → `onrouteLoading`
|
|
652
|
+
- `on:routeLoaded` → `onrouteLoaded`
|
|
653
|
+
- `on:conditionsFailed` → `onconditionsFailed`
|
|
654
|
+
- `on:routeEvent` → `onrouteEvent`
|
|
655
|
+
|
|
656
|
+
- **Component Props**:
|
|
657
|
+
- `export let params` → `let { params } = $props()`
|
|
658
|
+
|
|
659
|
+
- **Reactive Subscriptions**:
|
|
660
|
+
- `.subscribe()` → `$effect(() => { ... })`
|
|
661
|
+
|
|
662
|
+
### Unchanged (Backward Compatible)
|
|
663
|
+
|
|
664
|
+
- ✅ Route definition syntax (same object/Map structure)
|
|
665
|
+
- ✅ `push()`, `pop()`, `replace()` navigation functions
|
|
666
|
+
- ✅ `link` action for anchor tags
|
|
667
|
+
- ✅ `active` action for link highlighting
|
|
668
|
+
- ✅ `wrap()` utility for async components and conditions
|
|
669
|
+
- ✅ Route guards/pre-conditions
|
|
670
|
+
- ✅ Dynamic imports and code-splitting
|
|
671
|
+
- ✅ Nested routers with prefix
|
|
672
|
+
- ✅ Scroll restoration
|
|
673
|
+
- ✅ Loading components
|
|
674
|
+
|
|
675
|
+
### Documentation
|
|
676
|
+
|
|
677
|
+
#### New Files
|
|
678
|
+
- `CONTEXT.md` - Comprehensive project overview
|
|
679
|
+
- `CHANGELOG.md` - This file
|
|
680
|
+
- `DEVELOPMENT.md` - Development workflow guide
|
|
681
|
+
- `MIGRATION.md` - Detailed v4 → v5 migration guide
|
|
682
|
+
- `HASHLESS_MERGE_PLAN.md` - Technical implementation notes
|
|
683
|
+
|
|
684
|
+
#### Updated Files
|
|
685
|
+
- `README.md` - Added routing modes and permission system documentation
|
|
686
|
+
- `package.json` - Updated exports for new modules
|
|
687
|
+
|
|
688
|
+
### Examples
|
|
689
|
+
|
|
690
|
+
- `example/` - Hash mode example (updated for Svelte 5)
|
|
691
|
+
- `example-history/` - History mode example (new)
|
|
692
|
+
|
|
693
|
+
### Infrastructure
|
|
694
|
+
|
|
695
|
+
- ESLint configuration for Svelte 5
|
|
696
|
+
- Makefile for cross-platform development commands
|
|
697
|
+
- Package exports for all modules
|
|
698
|
+
|
|
699
|
+
## Migration Guide
|
|
700
|
+
|
|
701
|
+
See [MIGRATION.md](./MIGRATION.md) for detailed instructions on upgrading from v4 to v5.
|
|
702
|
+
|
|
703
|
+
### Quick Migration Checklist
|
|
704
|
+
|
|
705
|
+
1. ✅ Update imports: `svelte-spa-router` → `@keenmate/svelte-spa-router`
|
|
706
|
+
2. ✅ Change stores to functions: `$location` → `location()`
|
|
707
|
+
3. ✅ Update event handlers: `on:routeLoaded` → `onrouteLoaded`
|
|
708
|
+
4. ✅ Convert component props: `export let params` → `let { params } = $props()`
|
|
709
|
+
5. ✅ Replace subscriptions with `$effect`
|
|
710
|
+
6. ✅ Test all routes and navigation
|
|
711
|
+
|
|
712
|
+
### Optional: Enable History Mode
|
|
713
|
+
|
|
714
|
+
```javascript
|
|
715
|
+
// main.js
|
|
716
|
+
import { setHashRoutingEnabled, setBasePath } from '@keenmate/svelte-spa-router/utils'
|
|
717
|
+
|
|
718
|
+
setHashRoutingEnabled(false)
|
|
719
|
+
setBasePath('/')
|
|
720
|
+
|
|
721
|
+
mount(App, { target: document.body })
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
### Optional: Add Permission System
|
|
725
|
+
|
|
726
|
+
```javascript
|
|
727
|
+
// main.js
|
|
728
|
+
import { configurePermissions } from '@keenmate/svelte-spa-router/helpers/permissions'
|
|
729
|
+
|
|
730
|
+
configurePermissions({
|
|
731
|
+
checkPermissions: (user, requirements) => {
|
|
732
|
+
// Your permission logic
|
|
733
|
+
},
|
|
734
|
+
getCurrentUser: () => getCurrentUser(),
|
|
735
|
+
onUnauthorized: (detail) => push('/unauthorized')
|
|
736
|
+
})
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
## Browser Compatibility
|
|
740
|
+
|
|
741
|
+
- **Svelte 5**: All modern browsers (Chrome, Firefox, Safari, Edge)
|
|
742
|
+
- **Hash Mode**: All browsers including IE10+
|
|
743
|
+
- **History Mode**: All browsers with History API support (IE10+)
|
|
744
|
+
- **Permissions**: All modern browsers
|
|
745
|
+
|
|
746
|
+
## Dependencies
|
|
747
|
+
|
|
748
|
+
### Runtime
|
|
749
|
+
- `regexparam@2.0.2` - Route pattern matching
|
|
750
|
+
|
|
751
|
+
### Peer Dependencies
|
|
752
|
+
- `svelte@^5.0.0` - Required
|
|
753
|
+
|
|
754
|
+
### Dev Dependencies
|
|
755
|
+
- `eslint@^9.0.0` - Code linting
|
|
756
|
+
|
|
757
|
+
## Credits
|
|
758
|
+
|
|
759
|
+
- **Publisher:** KeenMate (https://keenmate.com)
|
|
760
|
+
- **Original svelte-spa-router:** Alessandro Segala (@ItalyPaleAle)
|
|
761
|
+
- **Svelte 5 Conversion:** KeenMate team
|
|
762
|
+
- **History Mode:** Adapted from svelte-spa-router-hashless
|
|
763
|
+
- **Permission System:** KeenMate team
|
|
764
|
+
|
|
765
|
+
## License
|
|
766
|
+
|
|
767
|
+
MIT License - See [LICENSE.md](./LICENSE.md)
|