@real-router/svelte 0.1.2 → 0.2.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/README.md CHANGED
@@ -196,6 +196,30 @@ Snippet names must be valid JavaScript identifiers and match the first segment o
196
196
 
197
197
  > **Note:** `keepAlive` is not supported. Svelte has no equivalent of React's `<Activity>` API or Vue's `<KeepAlive>`. Components are destroyed when navigating away.
198
198
 
199
+ ### `<RouterErrorBoundary>`
200
+
201
+ Declarative error handling for navigation errors. Shows a fallback **alongside** children (not instead of) when a guard rejects or a route is not found.
202
+
203
+ ```svelte
204
+ <script lang="ts">
205
+ import { RouterErrorBoundary } from "@real-router/svelte";
206
+ </script>
207
+
208
+ <RouterErrorBoundary
209
+ onError={(error) => analytics.track("nav_error", { code: error.code })}
210
+ >
211
+ {#snippet fallback(error, resetError)}
212
+ <div class="toast">
213
+ {error.code} <button onclick={resetError}>Dismiss</button>
214
+ </div>
215
+ {/snippet}
216
+
217
+ <Link routeName="protected">Go to Protected</Link>
218
+ </RouterErrorBoundary>
219
+ ```
220
+
221
+ Auto-resets on next successful navigation. Works with both `<Link>` and imperative `router.navigate()`.
222
+
199
223
  ## Actions
200
224
 
201
225
  ### `createLinkAction`
@@ -334,7 +358,7 @@ When enabled, a visually hidden `aria-live` region announces each navigation. Fo
334
358
 
335
359
  Full documentation: [Wiki](https://github.com/greydragon888/real-router/wiki)
336
360
 
337
- - [RouterProvider](https://github.com/greydragon888/real-router/wiki/RouterProvider) · [RouteView](https://github.com/greydragon888/real-router/wiki/RouteView) · [Link](https://github.com/greydragon888/real-router/wiki/Link)
361
+ - [RouterProvider](https://github.com/greydragon888/real-router/wiki/RouterProvider) · [RouteView](https://github.com/greydragon888/real-router/wiki/RouteView) · [RouterErrorBoundary](https://github.com/greydragon888/real-router/wiki/RouterErrorBoundary) · [Link](https://github.com/greydragon888/real-router/wiki/Link)
338
362
  - [useRouter](https://github.com/greydragon888/real-router/wiki/useRouter) · [useRoute](https://github.com/greydragon888/real-router/wiki/useRoute) · [useRouteNode](https://github.com/greydragon888/real-router/wiki/useRouteNode) · [useNavigator](https://github.com/greydragon888/real-router/wiki/useNavigator) · [useRouteUtils](https://github.com/greydragon888/real-router/wiki/useRouteUtils) · [useRouterTransition](https://github.com/greydragon888/real-router/wiki/useRouterTransition)
339
363
 
340
364
  ## Examples
@@ -0,0 +1,47 @@
1
+ <script lang="ts">
2
+ import { untrack } from "svelte";
3
+
4
+ import { useRouterError } from "../composables/useRouterError.svelte";
5
+
6
+ import type { RouterError, State } from "@real-router/core";
7
+ import type { Snippet } from "svelte";
8
+
9
+ interface Props {
10
+ children: Snippet;
11
+ fallback: Snippet<[RouterError, () => void]>;
12
+ onError?: (
13
+ error: RouterError,
14
+ toRoute: State | null,
15
+ fromRoute: State | null,
16
+ ) => void;
17
+ }
18
+
19
+ let { children, fallback, onError }: Props = $props();
20
+
21
+ const snapshot = useRouterError();
22
+ let dismissedVersion = $state(-1);
23
+
24
+ const visibleError = $derived(
25
+ snapshot.current.version > dismissedVersion
26
+ ? snapshot.current.error
27
+ : null,
28
+ );
29
+
30
+ function resetError(): void {
31
+ dismissedVersion = snapshot.current.version;
32
+ }
33
+
34
+ $effect(() => {
35
+ if (snapshot.current.error) {
36
+ const { error, toRoute, fromRoute } = snapshot.current;
37
+ untrack(() => {
38
+ onError?.(error, toRoute, fromRoute);
39
+ });
40
+ }
41
+ });
42
+ </script>
43
+
44
+ {@render children?.()}
45
+ {#if visibleError}
46
+ {@render fallback(visibleError, resetError)}
47
+ {/if}
@@ -0,0 +1,10 @@
1
+ import type { RouterError, State } from "@real-router/core";
2
+ import type { Snippet } from "svelte";
3
+ interface Props {
4
+ children: Snippet;
5
+ fallback: Snippet<[RouterError, () => void]>;
6
+ onError?: (error: RouterError, toRoute: State | null, fromRoute: State | null) => void;
7
+ }
8
+ declare const RouterErrorBoundary: import("svelte").Component<Props, {}, "">;
9
+ type RouterErrorBoundary = ReturnType<typeof RouterErrorBoundary>;
10
+ export default RouterErrorBoundary;
@@ -0,0 +1,4 @@
1
+ import type { RouterErrorSnapshot } from "@real-router/sources";
2
+ export declare function useRouterError(): {
3
+ readonly current: RouterErrorSnapshot;
4
+ };
@@ -0,0 +1,13 @@
1
+ import { createErrorSource } from "@real-router/sources";
2
+ import { createReactiveSource } from "../createReactiveSource.svelte";
3
+ import { useRouter } from "./useRouter.svelte";
4
+ const cache = new WeakMap();
5
+ export function useRouterError() {
6
+ const router = useRouter();
7
+ let source = cache.get(router);
8
+ if (!source) {
9
+ source = createErrorSource(router);
10
+ cache.set(router, source);
11
+ }
12
+ return createReactiveSource(source);
13
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { default as RouteView } from "./components/RouteView.svelte";
2
2
  export { default as Link } from "./components/Link.svelte";
3
3
  export { default as Lazy } from "./components/Lazy.svelte";
4
+ export { default as RouterErrorBoundary } from "./components/RouterErrorBoundary.svelte";
4
5
  export { createReactiveSource } from "./createReactiveSource.svelte";
5
6
  export { useRouter } from "./composables/useRouter.svelte";
6
7
  export { useNavigator } from "./composables/useNavigator.svelte";
@@ -14,4 +15,4 @@ export { default as RouterProvider } from "./RouterProvider.svelte";
14
15
  export { ROUTER_KEY, NAVIGATOR_KEY, ROUTE_KEY } from "./context";
15
16
  export type { LinkProps, RouteContext } from "./types";
16
17
  export type { Navigator } from "@real-router/core";
17
- export type { RouterTransitionSnapshot } from "@real-router/sources";
18
+ export type { RouterTransitionSnapshot, RouterErrorSnapshot, } from "@real-router/sources";
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  export { default as RouteView } from "./components/RouteView.svelte";
3
3
  export { default as Link } from "./components/Link.svelte";
4
4
  export { default as Lazy } from "./components/Lazy.svelte";
5
+ export { default as RouterErrorBoundary } from "./components/RouterErrorBoundary.svelte";
5
6
  // Reactive Primitives
6
7
  export { createReactiveSource } from "./createReactiveSource.svelte";
7
8
  // Composables
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/svelte",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "Svelte 5 integration for Real-Router",
6
6
  "svelte": "./dist/index.js",
@@ -44,10 +44,10 @@
44
44
  "license": "MIT",
45
45
  "sideEffects": false,
46
46
  "dependencies": {
47
- "@real-router/core": "^0.40.0",
48
- "@real-router/route-utils": "^0.1.6",
49
- "@real-router/sources": "^0.2.7",
50
- "dom-utils": "^0.2.2"
47
+ "@real-router/core": "^0.41.0",
48
+ "@real-router/route-utils": "^0.1.7",
49
+ "@real-router/sources": "^0.3.1",
50
+ "dom-utils": "^0.2.3"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@sveltejs/package": "2.5.7",
@@ -59,7 +59,7 @@
59
59
  "svelte": "5.54.0",
60
60
  "svelte-check": "4.4.5",
61
61
  "svelte-eslint-parser": "1.6.0",
62
- "@real-router/browser-plugin": "^0.10.5"
62
+ "@real-router/browser-plugin": "^0.11.0"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "svelte": ">=5.7.0"
@@ -69,6 +69,7 @@
69
69
  "test": "vitest",
70
70
  "test:stress": "vitest run --config vitest.config.stress.mts",
71
71
  "type-check": "svelte-check --tsconfig ./tsconfig.json",
72
- "lint": "eslint --cache src/ tests/ --fix --max-warnings 0"
72
+ "lint": "eslint --cache src/ tests/ --fix --max-warnings 0",
73
+ "build:dist-only": "svelte-package -i src -o dist"
73
74
  }
74
75
  }
@@ -0,0 +1,47 @@
1
+ <script lang="ts">
2
+ import { untrack } from "svelte";
3
+
4
+ import { useRouterError } from "../composables/useRouterError.svelte";
5
+
6
+ import type { RouterError, State } from "@real-router/core";
7
+ import type { Snippet } from "svelte";
8
+
9
+ interface Props {
10
+ children: Snippet;
11
+ fallback: Snippet<[RouterError, () => void]>;
12
+ onError?: (
13
+ error: RouterError,
14
+ toRoute: State | null,
15
+ fromRoute: State | null,
16
+ ) => void;
17
+ }
18
+
19
+ let { children, fallback, onError }: Props = $props();
20
+
21
+ const snapshot = useRouterError();
22
+ let dismissedVersion = $state(-1);
23
+
24
+ const visibleError = $derived(
25
+ snapshot.current.version > dismissedVersion
26
+ ? snapshot.current.error
27
+ : null,
28
+ );
29
+
30
+ function resetError(): void {
31
+ dismissedVersion = snapshot.current.version;
32
+ }
33
+
34
+ $effect(() => {
35
+ if (snapshot.current.error) {
36
+ const { error, toRoute, fromRoute } = snapshot.current;
37
+ untrack(() => {
38
+ onError?.(error, toRoute, fromRoute);
39
+ });
40
+ }
41
+ });
42
+ </script>
43
+
44
+ {@render children?.()}
45
+ {#if visibleError}
46
+ {@render fallback(visibleError, resetError)}
47
+ {/if}
@@ -0,0 +1,21 @@
1
+ import { createErrorSource } from "@real-router/sources";
2
+
3
+ import { createReactiveSource } from "../createReactiveSource.svelte";
4
+ import { useRouter } from "./useRouter.svelte";
5
+
6
+ import type { Router } from "@real-router/core";
7
+ import type { RouterErrorSnapshot, RouterSource } from "@real-router/sources";
8
+
9
+ const cache = new WeakMap<Router, RouterSource<RouterErrorSnapshot>>();
10
+
11
+ export function useRouterError(): { readonly current: RouterErrorSnapshot } {
12
+ const router = useRouter();
13
+
14
+ let source = cache.get(router);
15
+ if (!source) {
16
+ source = createErrorSource(router);
17
+ cache.set(router, source);
18
+ }
19
+
20
+ return createReactiveSource(source);
21
+ }
package/src/index.ts CHANGED
@@ -5,6 +5,8 @@ export { default as Link } from "./components/Link.svelte";
5
5
 
6
6
  export { default as Lazy } from "./components/Lazy.svelte";
7
7
 
8
+ export { default as RouterErrorBoundary } from "./components/RouterErrorBoundary.svelte";
9
+
8
10
  // Reactive Primitives
9
11
  export { createReactiveSource } from "./createReactiveSource.svelte";
10
12
 
@@ -36,4 +38,7 @@ export type { LinkProps, RouteContext } from "./types";
36
38
 
37
39
  export type { Navigator } from "@real-router/core";
38
40
 
39
- export type { RouterTransitionSnapshot } from "@real-router/sources";
41
+ export type {
42
+ RouterTransitionSnapshot,
43
+ RouterErrorSnapshot,
44
+ } from "@real-router/sources";