@keenmate/svelte-spa-router 5.2.0-rc01 → 5.2.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 +107 -4
- package/README.md +89 -2069
- package/package.json +7 -2
- package/src/lib/Router.svelte +167 -30
- package/src/lib/helpers/GlobalErrorHandler.svelte +0 -124
- package/src/lib/helpers/error-handler.d.ts +0 -6
- package/src/lib/helpers/error-handler.svelte.js +0 -1
- package/src/lib/helpers/hierarchy.d.ts +7 -1
- package/src/lib/helpers/permissions.d.ts +92 -9
- package/src/lib/helpers/permissions.svelte.js +110 -13
- package/src/lib/helpers/route-metadata.svelte.js +43 -1
- package/src/lib/helpers/url-helpers.svelte.js +23 -0
- package/src/lib/index.js +13 -10
- package/src/lib/routes.d.ts +5 -0
- package/src/lib/routes.svelte.js +4 -9
- package/src/lib/utils.d.ts +29 -0
- package/src/lib/utils.svelte.js +124 -7
- package/src/lib/wrap.d.ts +28 -2
- package/src/lib/wrap.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,9 +5,112 @@ 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
|
-
## [
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
## [5.2.0] - 2026-06-02 [PUBLISHED]
|
|
17
|
+
|
|
18
|
+
### Changed (docs)
|
|
19
|
+
- **README split into a slim entry point plus `docs/*.md`** — the README had grown to ~2,300 lines mixing install instructions with deep-dive content for every feature. Trimmed it to the basics (features, What's new, install, quick start, docs index, license) and moved the rest into 14 domain-specific files under `docs/`: `imports.md`, `routing-modes.md`, `routes.md`, `navigation.md`, `route-params.md`, `querystring-filters.md`, `loading-metadata.md`, `route-guards.md`, `permissions.md`, `navigation-guards.md`, `hierarchical-routes.md`, `error-handling.md`, `logging.md`, `advanced.md`. All original content is preserved; cross-references use relative links. The new README is 157 lines.
|
|
20
|
+
|
|
21
|
+
## [5.2.0-rc02] - 2026-05-01 [PUBLISHED]
|
|
9
22
|
|
|
10
23
|
### Fixed
|
|
24
|
+
- **`ReferenceError: __PACKAGE_NAME__ is not defined` masking real route errors** — The global API setup in `src/lib/index.js` referenced bundler-injected placeholders (`__PACKAGE_NAME__`, `__VERSION__`, `__AUTHOR__`, `__LICENSE__`, `__REPOSITORY__`, `__HOMEPAGE__`) with `typeof X !== 'undefined'` fallbacks. Consumer Vite optimizers (esbuild prebundle) were not preserving the guard, leaving bare references that threw `ReferenceError` at module-init. The error surfaced asynchronously during route activation and masked real errors thrown by route components.
|
|
25
|
+
- Replaced the placeholder pattern with a direct `import pkg from '../../package.json'` — works universally because npm always ships `package.json` and all modern bundlers handle JSON imports natively.
|
|
26
|
+
- No consumer-side bundler config required.
|
|
27
|
+
|
|
28
|
+
- **Svelte 5 `state_referenced_locally` warnings on every consumer dev build** — `Router.svelte` parsed the `routes` prop into `routesList` at script top level, which captures only the initial value. Svelte 5 emitted three `state_referenced_locally` warnings (lines 172, 173, 177) in any consumer's dev console, and silently ignored prop swaps if a consumer ever replaced `routes`.
|
|
29
|
+
- Wrapped the parse logic in `$derived.by(() => { ... })` so the closure captures `routes` reactively. Warnings are gone and the routes prop is now properly reactive — replacing it rebuilds `routesList` and takes effect on the next navigation.
|
|
30
|
+
- `findParentRoute()` and `findMatchingRoute()` already read `routesList` per-call, so no other changes were needed.
|
|
31
|
+
|
|
32
|
+
- **`navigationContext()` was never `null` after any `push()` call, breaking the "no context" check in consumer code** — `push()` and `replace()` internally inject `_routeName` (for referrer tracking) and optionally `__scrollBehavior` (for scroll control) into the stored navigation context. Previously these internal keys leaked through the public `navigationContext()` accessor, so `navigationContext()` returned `{ _routeName: '/path' }` (truthy) instead of `null` even when the consumer hadn't passed any context. Code patterns like `{#if !navigationContext()}` or `if (ctx === null)` silently broke. The `NavigationContextDemo` example was a victim: clicking "Back to List" called `push('/navigation-context-demo')`, expecting `ctx` to become null and the list view to re-render — but `ctx` was `{ _routeName: ... }`, so none of the `{#if}` branches matched and the middle area went blank.
|
|
33
|
+
- `navigationContext()` now filters out the router's internal keys (`_routeName`, `__scrollBehavior`) and returns `null` if nothing user-visible remains.
|
|
34
|
+
- Added an internal `getRawNavigationContext()` accessor that returns the full state including internal keys. `Router.svelte` uses this for its own internal reads (referrer-route lookup, scroll-behavior override). Not exposed in the public type declarations.
|
|
35
|
+
- **Migration:** if you were depending on seeing `_routeName` or `__scrollBehavior` in `navigationContext()` output (you almost certainly weren't), use `getRawNavigationContext()` from utils — but these are internal flags and the contract is that they may change without notice.
|
|
36
|
+
- vitest coverage in `navigation.test.js`: two new cases assert `navigationContext()` returns `null` after a no-context `push()`, and that user keys pass through while `_routeName` is filtered.
|
|
37
|
+
|
|
38
|
+
- **`push('/path', {}, queryObject)` and `replace(...)` silently dropped the querystring** — The multi-parameter signature `push(route, params, query, context)` only serialized the `query` object when the first argument was a *named route*. When the first argument was a path string (starting with `/`), the path branch stored `query` on `opts` but the downstream code only read `opts.href`, so the query object was never applied to the URL. The named-route branch worked because it routes through `buildUrl()`, which does the serialization. Existing tests only covered the named-route case and the path case with empty `{}`, so the regression was invisible.
|
|
39
|
+
- Extracted `serializeQuery(query)` into `helpers/url-helpers.svelte.js` and reuse it in both `buildUrl()` (named branch, behavior unchanged) and `push()` / `replace()` (path branch).
|
|
40
|
+
- When the path already contains a `?`, the serialized query is appended with `&`; otherwise with `?`.
|
|
41
|
+
- Added 6 vitest cases covering: path + query object, URL encoding of keys/values, skipping `null` / `undefined`, merging with a path-embedded query, empty-query no-op, and the equivalent `replace()` case.
|
|
42
|
+
|
|
43
|
+
- **`onNotFound` was never emitted when a `'*'` catch-all route was configured** — `runRoutingPipeline()` only dispatched `notFound` on the true no-match path (`!ctx.match`). With a `'*'` route present, `regexparam` turned it into a wildcard that matched every unmatched URL, so `ctx.match` was always truthy and the event was suppressed. Apps that configured a `'*': NotFound` route to render a 404 page lost the ability to *observe* 404s programmatically (for logging, analytics, error tracking).
|
|
44
|
+
- In `src/lib/Router.svelte`, after the no-match early-exit, dispatch `notFound` whenever `ctx.match.routeItem.path === '*'`. The catch-all component still renders — the event is purely additive.
|
|
45
|
+
- The check uses the same exact-`'*'` test as the existing `isCatchAll` flag at line 727, so subtree wildcards like `'/admin/*'` (whose `routeItem.path` is `/admin/*`, not `*`) and the synthetic unauthorized-route match (whose `routeItem.path` is the unauthorized route) are unaffected.
|
|
46
|
+
- Added an e2e test in `e2e/router-events.spec.ts` asserting that both the catch-all component renders *and* `onNotFound` fires with the correct `{ location, querystring }` payload. The pre-existing nested-router-without-catch-all test still covers the true no-match path.
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
- **`revalidateCurrentRoute()` + `onRevalidationFailure` — re-check the active route on user state changes** — `hasPermission()` reactivity (above) covers UI element visibility (menus, buttons), but it doesn't cover the case where the user is *sitting on a protected page* when their permissions are revoked. The router checks route conditions only during navigation, so a user already on `/admin` who loses admin permission would stay on `/admin` until they navigated away. This is exactly the websocket-permission-update scenario most real apps need to handle.
|
|
50
|
+
- New `revalidateCurrentRoute()` export from the main module. Re-runs guards and conditions against the currently mounted route without re-mounting the component. On success, nothing visible happens — the component keeps its state (no flicker, no scroll reset, no in-flight form data lost). On failure, the same unauthorized handling that runs for fresh navigation fires here too.
|
|
51
|
+
- New `configurePermissions({ onRevalidationFailure })` callback. When configured, fires *instead of* the standard unauthorized handling for revalidation failures — letting apps show a confirmation dialog, soft-warn the user, or log an audit trail before deciding what to do. If the callback doesn't navigate, the user stays on the current page. The `onConditionsFailed` Router event still fires for consistency with navigation behavior. Pass `null` to clear and fall back to standard handling.
|
|
52
|
+
- Calls to `revalidateCurrentRoute()` within a ~50ms window are coalesced into a single re-validation pass — safe to call on every websocket message.
|
|
53
|
+
- Multiple Router instances (nested routers, zones) each register independently and re-validate their own routes.
|
|
54
|
+
- Implementation: `runRoutingPipeline` accepts a `revalidationOnly` option that (a) skips the `beforeLeave` guards (user isn't navigating away), (b) skips the `routeLoading` event dispatch (nothing's loading), (c) on success returns early before the load/commit phases (preserves the mounted component), (d) on failure routes through the new callback when configured. The existing `loadingId` race-condition mechanism handles overlapping navigation/revalidation cleanly.
|
|
55
|
+
- vitest coverage in `navigation.test.js` (5 new cases: debounce, coalescing, multi-listener, unregister, throwing-listener tolerance) and `permissions.test.js` (4 new cases for the handler getter/setter contract).
|
|
56
|
+
- e2e: new `RevalidateTest` + `RevalidateProtected` fixtures and `e2e/revalidate.spec.ts` (3 tests: callback fires on protected-route downgrade, no callback when still authorized, no-op on unprotected routes). App.svelte wires up an `onRevalidationFailure` recorder for the spec to assert on.
|
|
57
|
+
- **Bug fix found while wiring this up:** the pipeline's internal `isPermissionFailure` value is the permissions object (truthy) rather than the literal `true`. Coerced to a boolean before exposing it in the callback's `detail`.
|
|
58
|
+
|
|
59
|
+
- **`setCurrentUser()` / `getCurrentUser()` — reactivity-by-default for `hasPermission()`** — Previously, `hasPermission()` only re-evaluated in `{#if}` blocks if the consumer's configured `getCurrentUser` happened to read reactive state. The README's canonical example used `getCurrentUser: () => get(currentUser)` (Svelte 4 store, non-reactive read), so following the docs literally meant `{#if hasPermission(...)}` only updated on navigation. A websocket pushing a permission change wouldn't update the UI until the user clicked a link.
|
|
60
|
+
- Added module-level `$state` (`currentUserState`) inside `permissions.svelte.js`. The default `currentUserGetter` now reads from this rune, so any `hasPermission()` call inside a reactive context (`{#if}`, `$derived`, `$effect`) automatically tracks user changes.
|
|
61
|
+
- New `setCurrentUser(user)` export — consumers call this on login/logout/websocket update; every reactive `hasPermission()` call site re-evaluates immediately. No subscription wiring needed on the consumer side.
|
|
62
|
+
- New `getCurrentUser()` export — symmetric reader for cases like `setCurrentUser({ ...getCurrentUser(), permissions: newPerms })`.
|
|
63
|
+
- `configurePermissions({ getCurrentUser })` still works for consumers who already maintain their own reactive user store (and remains the right choice when they do). Pass `getCurrentUser: null` to explicitly reset back to the default state-backed getter.
|
|
64
|
+
- **Migration:** existing apps keep working unchanged. New apps can skip `getCurrentUser` and use `setCurrentUser` instead — typically less code and guaranteed-reactive without thinking about it. The README, `permissions.d.ts`, and `ai/permissions.txt` now recommend this path and call out the non-reactive footgun explicitly.
|
|
65
|
+
- vitest coverage (`src/tests/permissions.test.js`): 5 new cases — `setCurrentUser`/`getCurrentUser` round-trip, `hasPermission` reflecting writes, explicit `getCurrentUser` overriding the default, `getCurrentUser: null` resetting back, and logged-out (`null`) handling.
|
|
66
|
+
|
|
67
|
+
- **Diagnostic warning when `shouldDisplayLoadingOnRouteLoad` routes never call `hideLoading()`** — Routes configured with `wrap({ shouldDisplayLoadingOnRouteLoad: true, loadingComponent: ... })` mount the real component immediately but keep it hidden under the loading component, waiting for the component itself to call `hideLoading()` from `@keenmate/svelte-spa-router/helpers/route-metadata` once data is ready. Previously, if the consumer forgot to call `hideLoading()` (or threw before reaching it, or hit a code path that didn't call it), the loading screen stayed up forever — no console message, no timeout, no recovery short of navigating away. The author hit this themselves while writing the e2e fixture and spent ten minutes debugging a "broken" wrap option.
|
|
68
|
+
- `startRouteLoading()` in `route-metadata.svelte.js` now schedules a `setTimeout(console.warn, 10_000)` that fires only if `isRouteLoading` is still true at the threshold. Bypasses the configurable logger (matches the project convention that misconfiguration warnings always print). The message names the option, names the required call, and links to the README.
|
|
69
|
+
- `hideLoading()` and a subsequent `startRouteLoading()` both clear the timer so the warning never fires when things are working normally.
|
|
70
|
+
- vitest coverage in `route-metadata.test.js`: three new cases (warning fires after threshold, warning doesn't fire when `hideLoading()` is called in time, warning timer resets on subsequent `startRouteLoading()`).
|
|
71
|
+
|
|
72
|
+
- **Pending `waitForRouteReady()` promises are resolved when a new navigation starts** — Previously, if a user navigated away from a `shouldDisplayLoadingOnRouteLoad` route before its `hideLoading()` fired, the next `startRouteLoading()` clobbered `routeReadyResolvers = []` without resolving the pending entry. The old pipeline's `await waitForRouteReady()` was orphaned and leaked one unresolvable promise per abandoned navigation. After resolving, it would also have raced with the new pipeline's state writes if not for the new race check.
|
|
73
|
+
- `startRouteLoading()` now resolves any pending resolvers before clearing the array.
|
|
74
|
+
- `Router.svelte` adds a `loadingId !== loadingId` race-condition check immediately after `await waitForRouteReady()`, matching the pattern already used after `pipelineLoadComponent` and `pipelineLoadZoneComponents`. Stale pipeline runs bail cleanly instead of racing.
|
|
75
|
+
- vitest coverage: new case asserting that a second `startRouteLoading()` resolves the first navigation's pending waiter.
|
|
76
|
+
|
|
77
|
+
- **Documented the `shouldDisplayLoadingOnRouteLoad` contract loudly** — `wrap.d.ts`, `wrap.js`, `routes.d.ts`, `helpers/hierarchy.d.ts`, and the README's Pattern 1 section now spell out the "you MUST call `hideLoading()`" requirement and the blank-page failure mode. Old docstring was 14 words; new copy names the failure mode and the dev-mode safety net.
|
|
78
|
+
|
|
79
|
+
- **`relativeLocation` field on every Router event payload** — Nested Routers configured with `prefix="/foo/bar"` define their routes in prefix-relative terms (e.g. `'/known'`, not `'/foo/bar/known'`) and match against the prefix-stripped path internally. But every event payload (`onRouteLoading`, `onRouteLoaded`, `onConditionsFailed`, `onNotFound`) reported the **full app URL** in `location` — so the router used two different notions of "location" depending on whether you looked at route definitions or event payloads. Consumers had to know the difference and strip the prefix themselves to reason about what the nested Router actually saw.
|
|
80
|
+
- Added a new `relativeLocation` field to every dispatched event payload that already carried `location`. For a root Router (no `prefix`), `relativeLocation === location`. For a nested Router with `prefix`, it's the path with the prefix stripped (`/` if stripping leaves it empty). For paths outside the prefix (the rare case where the parent app navigates to something the nested Router doesn't own), `relativeLocation` equals `location`.
|
|
81
|
+
- `location` is preserved unchanged — useful for logging, analytics, and re-navigating with `push()` (which always takes app-wide paths). `relativeLocation` is useful for reasoning about *this* Router's routing decisions.
|
|
82
|
+
- All 7 `dispatchNextTick` sites in `Router.svelte` updated. The field is computed once per pipeline run in `createPipelineContext` and threaded through `ctx`.
|
|
83
|
+
- README event-payload table updated to list the field and explain when it differs from `location`.
|
|
84
|
+
- e2e coverage: extended `e2e/router-events.spec.ts` to assert (1) `relativeLocation === '/missing'` for a nested Router with `prefix="/test/embed"` navigating to `/test/embed/missing`, and (2) `relativeLocation === location` for the root Router with no prefix.
|
|
85
|
+
|
|
86
|
+
### Changed (docs)
|
|
87
|
+
- **Documented `conditions` failure behavior and the `conditions` vs `permissions` distinction** — Previously the README showed how to *write* a `wrap({ conditions: [...] })` guard and listed `onConditionsFailed` as a Router event, but never explained what the user actually sees when a condition returns `false`: the route component is unmounted, the slot becomes empty, and no built-in fallback UI is rendered. This surprised even the maintainers when writing the e2e suite — assertions assumed conditions failures would mount the same Unauthorized component that the permission system mounts.
|
|
88
|
+
- `README.md`: added a "What happens when a condition returns `false`" callout to the *Route guards (pre-conditions)* section; expanded the *Event handling* example with a payload-shape table (`onRouteLoading` / `onRouteLoaded` / `onConditionsFailed` / `onNotFound`); added a new *Conditions vs Permissions* subsection with a side-by-side comparison covering setup, where the logic lives, what happens on failure (UI + event), and when to choose each.
|
|
89
|
+
- `ai/guards-conditions.txt`: added a `WHAT HAPPENS ON FAILURE` section mirroring the README so the AI-facing summary captures the same fact.
|
|
90
|
+
- **No code change.** The conditions/permissions split is the intended design — conditions are the low-level primitive, permissions are the opinionated wrapper that builds on top. Per the project's "library shouldn't paint default UI" principle (see the toast removal in this same release), conditions stay unopinionated. The behavior just needed to be documented.
|
|
91
|
+
|
|
92
|
+
### Removed (breaking — rc02 is unreleased)
|
|
93
|
+
- **Built-in error toast removed from `GlobalErrorHandler`** — The library used to render its own `<div class="error-toast">` on caught errors, gated by the `showToast` config flag (`true` by default). The render condition was `toastVisible && errorState.currentError && !config.showErrorComponent`, which made it dead code under the default `navigateSafe` strategy: that strategy calls `clearError()` synchronously after `push()` in the same handler tick, so by the time Svelte's reactive system flushed the `toastVisible = true` update, `errorState.currentError` was already `null` and the toast never rendered. `restart` with `autoRestart: false` and `showError` set `config.showErrorComponent = true`, which the same guard hides — so the toast was effectively only observable for `restart` with `autoRestart: true` during the delay window, and for `custom` strategies that left state untouched.
|
|
94
|
+
- Rather than patch the broken interaction, the toast is removed entirely. Notification UI is the consumer's responsibility — every app already has a preferred toast/snackbar library, and the router's job is to surface the event, not paint pixels.
|
|
95
|
+
- `onError(error, errorInfo, context)` is the supported integration point and was already wired up. Consumers can call their own toast library, Sentry, LogRocket, analytics, etc. from inside that callback.
|
|
96
|
+
- Removed: `showToast` config field (was: `boolean`, default `true`), `toastVisible` / `toastTimeoutId` state, `showToast()` / `dismissToast()` helpers, the toast `{#if}` block in the template, and all `.error-toast` / `.toast-*` CSS in `GlobalErrorHandler.svelte`.
|
|
97
|
+
- Updated: the `GlobalErrorHandlerConfig` TypeScript declaration (`error-handler.d.ts`), the example app (`main.js`, `ErrorHandlingDemo.svelte`, `test/ErrorTest.svelte`), and documentation (`README.md`, `ai/error-handling.txt`, `CLAUDE.md`, `e2e/README.md`, `e2e/error-handling.spec.ts`) to drop `showToast` and point at `onError` as the toast hook.
|
|
98
|
+
- **Migration:** if you were passing `showToast: true`, remove it (TypeScript will flag it). To preserve toast behavior, call your toast library inside `onError` — e.g. `onError: (error) => toast.error(error.message)`.
|
|
99
|
+
|
|
100
|
+
### Added
|
|
101
|
+
- **Playwright e2e test suite** — Browser-level tests for the router, run against the example app in history mode on port 5050. Dedicated, minimal fixture pages live under `example/src/routes/test/` and are kept separate from the demo routes (which are user-oriented and evolve over time) so assertions stay stable.
|
|
102
|
+
- **16 specs / 98 tests** covering all 20 feature areas from `ai/INDEX.txt`: basic setup & events, navigation (push/replace/pop/goBack + array/object signatures), named routes, route params (required / optional / wildcard), permissions (RBAC + authorization), guards & conditions, hierarchical inheritance, tree structure (`createHierarchy`), link actions (`use:link`, `use:active`), error handling (GlobalErrorHandler), referrer tracking, breadcrumbs / route metadata, debug logging & `window.components` API, querystring helpers, filters, multi-zone, loading states, 404 / NotFound, `wrap()`, and `routeContext`.
|
|
103
|
+
- Playwright auto-starts the example dev server and reuses an already-running one (so `make dev` in another terminal is fine).
|
|
104
|
+
- Each fixture surfaces router state via `data-testid` nodes; action buttons use `data-testid="btn-<action>"`. Reload always resets fixture state.
|
|
105
|
+
- New npm scripts: `test:e2e`, `test:e2e:install`, `test:e2e:ui`, `test:e2e:headed`. New Makefile targets: `test-e2e`, `test-e2e-install`, `test-e2e-ui`.
|
|
106
|
+
- Documentation: `e2e/README.md` covers run instructions, the fixture/spec convention, a coverage matrix, and a 6-step recipe for adding new fixtures. A discoverable in-browser index of all fixtures lives at `/test`.
|
|
107
|
+
|
|
108
|
+
## [5.2.0-rc01] - 2026-02-18 [PUBLISHED]
|
|
109
|
+
|
|
110
|
+
### Fixed
|
|
111
|
+
- **Routes no longer wrapped in a layout-breaking `<div style="display: block">`** (Issue #1) — `Router.svelte` previously wrapped every routed component in a wrapper div, even for routes that don't use the loading feature. This broke CSS flexbox/grid layouts and child selectors for the majority of routes that have no need for the wrapper.
|
|
112
|
+
- Split rendering into two paths in `src/lib/Router.svelte`: routes without loading render the component directly (no wrapper div at all); routes with loading keep the wrapper (needed to hide the component during loading) but switch from `display: block` to `display: contents`, making the wrapper invisible to layout.
|
|
113
|
+
|
|
11
114
|
- **`routeContext()` function missing / mangled name** (Issue #3) — The exported function was named `routerouteContext()` instead of `routeContext()` due to a find-replace accident during the `userData` → `routeContext` rename. The README also referenced the old name `routeUserData()`.
|
|
12
115
|
- Renamed `routerouteContext()` → `routeContext()` in `route-metadata.svelte.js` (function + all internal variable references)
|
|
13
116
|
- Updated `route-metadata.d.ts` type declaration to match
|
|
@@ -104,7 +207,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
104
207
|
- Best practices and common patterns
|
|
105
208
|
- Debugging with ROUTER:METADATA logging category
|
|
106
209
|
|
|
107
|
-
## [5.1.1] - 2025-11-30
|
|
210
|
+
## [5.1.1] - 2025-11-30 [PUBLISHED]
|
|
108
211
|
|
|
109
212
|
### Fixed
|
|
110
213
|
- **Breadcrumbs preserved on querystring changes** - Fixed breadcrumbs resetting to "Loading..." when only the querystring changes (e.g., tab navigation)
|
|
@@ -134,7 +237,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
134
237
|
- Shows common pattern with page definitions array
|
|
135
238
|
- Explains the "Route X not found in registry" error and how to fix it
|
|
136
239
|
|
|
137
|
-
## [5.1.0] - 2025-11-20
|
|
240
|
+
## [5.1.0] - 2025-11-20 [PUBLISHED]
|
|
138
241
|
|
|
139
242
|
### Added
|
|
140
243
|
- **Global Window API:** Added runtime debugging and introspection via `window.components['svelte-spa-router']`
|
|
@@ -151,7 +254,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
151
254
|
- Enables debugging production issues without code changes or rebuilding
|
|
152
255
|
- Example: `window.components['svelte-spa-router'].logging.setCategoryLevel('ROUTER:NAVIGATION', 'debug')`
|
|
153
256
|
|
|
154
|
-
## [5.0.0] - 2025-01-17
|
|
257
|
+
## [5.0.0] - 2025-01-17 [PUBLISHED]
|
|
155
258
|
|
|
156
259
|
### Changed
|
|
157
260
|
- **Code Quality:** Major ESLint cleanup - reduced linting issues from 161 to 11 (93% reduction)
|