@keenmate/svelte-spa-router 5.0.0 → 5.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -1
- package/package.json +1 -1
- package/src/lib/Router.svelte +0 -5
- package/src/lib/helpers/route-metadata.svelte.js +44 -8
- package/src/lib/index.js +70 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,9 +5,75 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [5.1.1] - 2025-11-30
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Breadcrumbs preserved on querystring changes** - Fixed breadcrumbs resetting to "Loading..." when only the querystring changes (e.g., tab navigation)
|
|
12
|
+
- Issue: Changing tabs via `replace('/items/1', {}, { tab: 'settings' })` would reset dynamically updated breadcrumbs back to their initial "Loading..." state
|
|
13
|
+
- Root cause: `updateRouteMetadata()` was resetting the entire route context on any navigation, including querystring-only changes
|
|
14
|
+
- Solution: Detect when only querystring changed (same location + params) and preserve current breadcrumbs instead of resetting
|
|
15
|
+
- Also applies cached breadcrumb updates when the route context is updated
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- **TabsDemo example** - New example demonstrating correct pattern for tabs with querystring state
|
|
19
|
+
- Shows how to use `replace()` instead of `push()` for tab changes
|
|
20
|
+
- Demonstrates dynamic breadcrumbs that persist across tab switches
|
|
21
|
+
- Located at `example/src/routes/TabsDemo.svelte`
|
|
22
|
+
|
|
23
|
+
### Documentation
|
|
24
|
+
- **AI Documentation Index** - Added comprehensive `ai/INDEX.txt` file for quick keyword lookup
|
|
25
|
+
- Organized by topic sections: Getting Started, Imports, Navigation, Named Routes, etc.
|
|
26
|
+
- Includes file descriptions, reading order recommendations, and cross-references
|
|
27
|
+
- Quick problem solving guide mapping common errors to solutions
|
|
28
|
+
- Examples organized by use case (Simple SPA, Admin Dashboard, E-commerce, etc.)
|
|
29
|
+
- **Breadcrumbs with Tabs** - Added "TABS WITH QUERY STRING" section to `ai/breadcrumbs.txt`
|
|
30
|
+
- Documents the pattern for tabs that use querystring for state
|
|
31
|
+
- Explains why `replace()` is necessary to preserve breadcrumbs
|
|
32
|
+
- Includes complete code example
|
|
33
|
+
- **Named Routes in Basic Setup** - Added "ENABLING NAMED ROUTES" section to `ai/basic-setup.txt`
|
|
34
|
+
- Documents that `registerRoutes()` must be called for named route navigation to work
|
|
35
|
+
- Shows common pattern with page definitions array
|
|
36
|
+
- Explains the "Route X not found in registry" error and how to fix it
|
|
37
|
+
|
|
8
38
|
## [Unreleased]
|
|
9
39
|
|
|
10
|
-
|
|
40
|
+
### Documentation
|
|
41
|
+
- **AI Assistant Documentation** - Added 15 concise text files in `./ai` folder optimized for AI assistants
|
|
42
|
+
- Plain text format (no markdown) with bullet-style structure for efficient AI parsing
|
|
43
|
+
- Files organized by feature: basic-setup, navigation, named-routes, route-params, permissions, guards-conditions, hierarchical-routes, tree-structure, link-actions, error-handling, referrer-tracking, debug-logging, import-patterns, utilities, breadcrumbs
|
|
44
|
+
- Includes correct/incorrect usage patterns (✅/❌) for common mistakes
|
|
45
|
+
- Code examples designed for copy-paste usage
|
|
46
|
+
- Complements CLAUDE.md by providing quick-reference documentation
|
|
47
|
+
- Aimed at helping AI coding assistants (like Claude, Cursor, Copilot) quickly understand router functionality
|
|
48
|
+
- **Breadcrumbs Documentation** - Added comprehensive `ai/breadcrumbs.txt` covering breadcrumb navigation system
|
|
49
|
+
- Basic breadcrumb definition and structure
|
|
50
|
+
- Accessing breadcrumbs in components via `routeBreadcrumbs()` helper
|
|
51
|
+
- Breadcrumb component examples with navigation and styling
|
|
52
|
+
- Dynamic breadcrumb updates using `updateBreadcrumb(id, updates)` after data loads
|
|
53
|
+
- Integration with route parameters for dynamic segments
|
|
54
|
+
- Hierarchical breadcrumb inheritance with automatic concatenation
|
|
55
|
+
- Tree structure support with `createHierarchy()`
|
|
56
|
+
- Best practices and common patterns
|
|
57
|
+
- Debugging with ROUTER:METADATA logging category
|
|
58
|
+
|
|
59
|
+
## [5.1.0] - 2025-11-20 ✅ Published
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
- **Global Window API:** Added runtime debugging and introspection via `window.components['svelte-spa-router']`
|
|
63
|
+
- `version()` - Get library version at runtime
|
|
64
|
+
- `config` - Access package metadata (name, version, author, license, repository, homepage)
|
|
65
|
+
- `logging.enableLogging()` - Enable all debug logging from browser console
|
|
66
|
+
- `logging.disableLogging()` - Disable all logging from browser console
|
|
67
|
+
- `logging.setLogLevel(level)` - Set global log level from browser console
|
|
68
|
+
- `logging.setCategoryLevel(category, level)` - Control specific logging categories from browser console
|
|
69
|
+
- `logging.getCategories()` - List all available logging categories
|
|
70
|
+
- TypeScript support with full autocompletion for global API
|
|
71
|
+
- SSR-safe implementation (only initializes in browser)
|
|
72
|
+
- Namespace-safe pattern using `window.components` (shared across all component libraries)
|
|
73
|
+
- Enables debugging production issues without code changes or rebuilding
|
|
74
|
+
- Example: `window.components['svelte-spa-router'].logging.setCategoryLevel('ROUTER:NAVIGATION', 'debug')`
|
|
75
|
+
|
|
76
|
+
## [5.0.0] - 2025-01-17 ✅ Published
|
|
11
77
|
|
|
12
78
|
### Changed
|
|
13
79
|
- **Code Quality:** Major ESLint cleanup - reduced linting issues from 161 to 11 (93% reduction)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keenmate/svelte-spa-router",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.1",
|
|
4
4
|
"description": "Router for SPAs using Svelte 5 with runes, dual-mode routing, permissions, and error handling",
|
|
5
5
|
"main": "./src/lib/index.js",
|
|
6
6
|
"svelte": "./src/lib/Router.svelte",
|
package/src/lib/Router.svelte
CHANGED
|
@@ -595,11 +595,6 @@ function commitToReactiveState(ctx) {
|
|
|
595
595
|
}
|
|
596
596
|
}
|
|
597
597
|
|
|
598
|
-
// Debug log AFTER navigationContext is set
|
|
599
|
-
const finalReferrer = ctx.updatedNavigationContext?.referrer?.location || 'none'
|
|
600
|
-
const finalSeq = typeof window !== 'undefined' && window.history.state?.__navigationSequence || 0
|
|
601
|
-
console.log(`🔍 NAV: route="${ctx.location}" referrer="${finalReferrer}" seq=${finalSeq}`)
|
|
602
|
-
|
|
603
598
|
// Update current route tracking (unless catch-all)
|
|
604
599
|
if (!ctx.isCatchAll) {
|
|
605
600
|
currentRoute = ctx.location
|
|
@@ -42,8 +42,11 @@ let updatedBreadcrumbsCache = new Map()
|
|
|
42
42
|
* @param {Object} params - route params
|
|
43
43
|
*/
|
|
44
44
|
export function updateRouteMetadata(routeContext = {}, location = '', querystring = '', params = {}) {
|
|
45
|
-
// Create a
|
|
46
|
-
const
|
|
45
|
+
// Create a key for the route path (without querystring) to detect actual route changes
|
|
46
|
+
const locationKey = `${location}|${JSON.stringify(params)}`
|
|
47
|
+
|
|
48
|
+
// Create full key including querystring for logging
|
|
49
|
+
const fullRouteKey = `${location}|${querystring}|${JSON.stringify(params)}`
|
|
47
50
|
|
|
48
51
|
// Extract base path (e.g., /documents/1 → /documents, /documents/1/logs → /documents/1)
|
|
49
52
|
// This is a simple heuristic: get path up to the last segment
|
|
@@ -56,12 +59,45 @@ export function updateRouteMetadata(routeContext = {}, location = '', querystrin
|
|
|
56
59
|
clearBreadcrumbCache()
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
currentRouteKey
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
// Check if only querystring changed (same location and params)
|
|
63
|
+
const onlyQuerystringChanged = currentRouteKey && currentRouteKey.startsWith(locationKey.split('|')[0] + '|')
|
|
64
|
+
&& currentRouteKey.includes('|' + JSON.stringify(params))
|
|
65
|
+
&& currentRouteKey !== fullRouteKey
|
|
66
|
+
|
|
67
|
+
// Only update context if the actual route (location + params) changed, not just querystring
|
|
68
|
+
if (currentRouteKey !== fullRouteKey) {
|
|
69
|
+
if (onlyQuerystringChanged) {
|
|
70
|
+
// Querystring-only change: preserve breadcrumbs, just update the key
|
|
71
|
+
metadataLogger.debug('[updateRouteMetadata] Querystring changed, preserving breadcrumbs')
|
|
72
|
+
currentRouteKey = fullRouteKey
|
|
73
|
+
} else {
|
|
74
|
+
// Actual route change: update context but apply cached breadcrumb updates
|
|
75
|
+
let finalContext = { ...routeContext }
|
|
76
|
+
|
|
77
|
+
// Apply any cached breadcrumb updates to the new context
|
|
78
|
+
if (routeContext.breadcrumbs && updatedBreadcrumbsCache.size > 0) {
|
|
79
|
+
const breadcrumbs = [...routeContext.breadcrumbs]
|
|
80
|
+
let appliedUpdates = false
|
|
81
|
+
|
|
82
|
+
for (const [id, updates] of updatedBreadcrumbsCache) {
|
|
83
|
+
const index = breadcrumbs.findIndex(crumb => crumb.id === id)
|
|
84
|
+
if (index !== -1) {
|
|
85
|
+
breadcrumbs[index] = { ...breadcrumbs[index], ...updates }
|
|
86
|
+
appliedUpdates = true
|
|
87
|
+
metadataLogger.debug('[updateRouteMetadata] Applied cached update for:', id)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (appliedUpdates) {
|
|
92
|
+
finalContext = { ...finalContext, breadcrumbs }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
currentRouterouteContext = finalContext
|
|
97
|
+
currentRouteKey = fullRouteKey
|
|
98
|
+
currentBasePath = basePath
|
|
99
|
+
metadataLogger.debug('[updateRouteMetadata] Route changed to:', fullRouteKey)
|
|
100
|
+
}
|
|
65
101
|
} else {
|
|
66
102
|
metadataLogger.debug('[updateRouteMetadata] Same route, ignoring update')
|
|
67
103
|
}
|
package/src/lib/index.js
CHANGED
|
@@ -24,3 +24,73 @@ export * from './helpers/filters.svelte.js';
|
|
|
24
24
|
|
|
25
25
|
// Navigation guard system
|
|
26
26
|
export * from './helpers/navigation-guard.svelte.js';
|
|
27
|
+
|
|
28
|
+
// Import logging utilities for global API
|
|
29
|
+
import {
|
|
30
|
+
enableLogging,
|
|
31
|
+
disableLogging,
|
|
32
|
+
setLogLevel,
|
|
33
|
+
setCategoryLevel
|
|
34
|
+
} from './logger';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {Object} GlobalRouterAPI
|
|
38
|
+
* @property {() => string} version - Get library version
|
|
39
|
+
* @property {Object} config - Package metadata
|
|
40
|
+
* @property {string} config.name - Package name
|
|
41
|
+
* @property {string} config.version - Package version
|
|
42
|
+
* @property {string} config.author - Package author
|
|
43
|
+
* @property {string} config.license - Package license
|
|
44
|
+
* @property {string} config.repository - Repository URL
|
|
45
|
+
* @property {string} config.homepage - Homepage URL
|
|
46
|
+
* @property {Object} logging - Logging controls
|
|
47
|
+
* @property {() => void} logging.enableLogging - Enable all debug logging
|
|
48
|
+
* @property {() => void} logging.disableLogging - Disable all logging
|
|
49
|
+
* @property {(level: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent') => void} logging.setLogLevel - Set global log level
|
|
50
|
+
* @property {(category: string, level?: string) => void} logging.setCategoryLevel - Set category-specific log level
|
|
51
|
+
* @property {() => string[]} logging.getCategories - List all logging categories
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
// List of all logging categories
|
|
55
|
+
const LOGGING_CATEGORIES = [
|
|
56
|
+
'ROUTER',
|
|
57
|
+
'ROUTER:NAVIGATION',
|
|
58
|
+
'ROUTER:SCROLL',
|
|
59
|
+
'ROUTER:GUARDS',
|
|
60
|
+
'ROUTER:CONDITIONS',
|
|
61
|
+
'ROUTER:HIERARCHY',
|
|
62
|
+
'ROUTER:PERMISSIONS',
|
|
63
|
+
'ROUTER:ROUTES',
|
|
64
|
+
'ROUTER:ZONES',
|
|
65
|
+
'ROUTER:METADATA',
|
|
66
|
+
'ROUTER:ERROR_HANDLER',
|
|
67
|
+
'ROUTER:FILTERS'
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
// Initialize global API (SSR-safe)
|
|
71
|
+
if (typeof window !== 'undefined') {
|
|
72
|
+
// Create components namespace if it doesn't exist
|
|
73
|
+
window.components = window.components || {};
|
|
74
|
+
|
|
75
|
+
// Initialize svelte-spa-router API
|
|
76
|
+
// Note: __VERSION__, __PACKAGE_NAME__, etc. are injected by build tools (Vite, Rollup)
|
|
77
|
+
// via the `define` option. Fallback values are provided for source distribution.
|
|
78
|
+
window.components['svelte-spa-router'] = {
|
|
79
|
+
version: () => typeof __VERSION__ !== 'undefined' ? __VERSION__ : '5.1.0',
|
|
80
|
+
config: {
|
|
81
|
+
name: typeof __PACKAGE_NAME__ !== 'undefined' ? __PACKAGE_NAME__ : '@keenmate/svelte-spa-router',
|
|
82
|
+
version: typeof __VERSION__ !== 'undefined' ? __VERSION__ : '5.1.0',
|
|
83
|
+
author: typeof __AUTHOR__ !== 'undefined' ? __AUTHOR__ : 'KeenMate (https://keenmate.com)',
|
|
84
|
+
license: typeof __LICENSE__ !== 'undefined' ? __LICENSE__ : 'MIT',
|
|
85
|
+
repository: typeof __REPOSITORY__ !== 'undefined' ? __REPOSITORY__ : 'https://github.com/keenmate/svelte-spa-router',
|
|
86
|
+
homepage: typeof __HOMEPAGE__ !== 'undefined' ? __HOMEPAGE__ : 'https://github.com/keenmate/svelte-spa-router#readme'
|
|
87
|
+
},
|
|
88
|
+
logging: {
|
|
89
|
+
enableLogging,
|
|
90
|
+
disableLogging,
|
|
91
|
+
setLogLevel,
|
|
92
|
+
setCategoryLevel,
|
|
93
|
+
getCategories: () => [...LOGGING_CATEGORIES]
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|