@real-router/sources 0.2.8 → 0.3.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/README.md CHANGED
@@ -38,20 +38,21 @@ const unsubscribe = source.subscribe(() => {
38
38
 
39
39
  ## Source Factories
40
40
 
41
- | Factory | Snapshot | Updates when |
42
- |---------|----------|--------------|
43
- | `createRouteSource(router)` | `{ route, previousRoute }` | Every navigation |
44
- | `createRouteNodeSource(router, node)` | `{ route, previousRoute }` | Only when node activates/deactivates |
45
- | `createActiveRouteSource(router, name, params?, opts?)` | `boolean` | Route active status changes |
46
- | `createTransitionSource(router)` | `{ isTransitioning, toRoute, fromRoute }` | Transition start/end/cancel/error |
41
+ | Factory | Snapshot | Updates when |
42
+ | ------------------------------------------------------- | ----------------------------------------- | --------------------------------------------------- |
43
+ | `createRouteSource(router)` | `{ route, previousRoute }` | Every navigation |
44
+ | `createRouteNodeSource(router, node)` | `{ route, previousRoute }` | Only when node activates/deactivates |
45
+ | `createActiveRouteSource(router, name, params?, opts?)` | `boolean` | Route active status changes |
46
+ | `createTransitionSource(router)` | `{ isTransitioning, toRoute, fromRoute }` | Transition start/end/cancel/error |
47
+ | `createErrorSource(router)` | `{ error, toRoute, fromRoute, version }` | Navigation error (guard rejection, route not found) |
47
48
 
48
49
  All factories return a `RouterSource<T>`:
49
50
 
50
51
  ```typescript
51
52
  interface RouterSource<T> {
52
- subscribe(listener: () => void): () => void; // useSyncExternalStore-compatible
53
- getSnapshot(): T; // current value, synchronous
54
- destroy(): void; // teardown, remove router subscription
53
+ subscribe(listener: () => void): () => void; // useSyncExternalStore-compatible
54
+ getSnapshot(): T; // current value, synchronous
55
+ destroy(): void; // teardown, remove router subscription
55
56
  }
56
57
  ```
57
58
 
@@ -59,12 +60,13 @@ interface RouterSource<T> {
59
60
 
60
61
  - `createRouteSource`, `createRouteNodeSource`, `createActiveRouteSource` — **lazy**: subscribe to the router on first listener, unsubscribe when all removed
61
62
  - `createTransitionSource` — **eager**: subscribes immediately (needs to track `TRANSITION_START`)
63
+ - `createErrorSource` — **eager**: subscribes immediately (needs to track `TRANSITION_ERROR`)
62
64
 
63
65
  ### `createActiveRouteSource` Options
64
66
 
65
67
  ```typescript
66
68
  const source = createActiveRouteSource(router, "users", undefined, {
67
- strict: false, // default: false — match descendants too
69
+ strict: false, // default: false — match descendants too
68
70
  ignoreQueryParams: true, // default: true
69
71
  });
70
72
  ```
@@ -118,17 +120,32 @@ source.subscribe(() => {
118
120
  });
119
121
  ```
120
122
 
123
+ ### Error Tracking
124
+
125
+ ```typescript
126
+ import { createErrorSource } from "@real-router/sources";
127
+
128
+ const source = createErrorSource(router);
129
+
130
+ source.subscribe(() => {
131
+ const { error, toRoute } = source.getSnapshot();
132
+ if (error) {
133
+ console.error(`Navigation to ${toRoute?.name} failed: ${error.code}`);
134
+ }
135
+ });
136
+ ```
137
+
121
138
  ## Documentation
122
139
 
123
140
  Full documentation: [Wiki — sources](https://github.com/greydragon888/real-router/wiki/sources-package)
124
141
 
125
142
  ## Related Packages
126
143
 
127
- | Package | Description |
128
- |---------|-------------|
129
- | [@real-router/core](https://www.npmjs.com/package/@real-router/core) | Core router (required dependency) |
144
+ | Package | Description |
145
+ | ---------------------------------------------------------------------- | ------------------------------------------- |
146
+ | [@real-router/core](https://www.npmjs.com/package/@real-router/core) | Core router (required dependency) |
130
147
  | [@real-router/react](https://www.npmjs.com/package/@real-router/react) | React integration (uses sources internally) |
131
- | [@real-router/rx](https://www.npmjs.com/package/@real-router/rx) | Observable API (`state$`, `events$`) |
148
+ | [@real-router/rx](https://www.npmjs.com/package/@real-router/rx) | Observable API (`state$`, `events$`) |
132
149
 
133
150
  ## Contributing
134
151
 
@@ -1,4 +1,4 @@
1
- import { State, Router, Params } from '@real-router/core';
1
+ import { State, RouterError, Router, Params } from '@real-router/core';
2
2
 
3
3
  interface RouteSnapshot {
4
4
  route: State | undefined;
@@ -22,6 +22,12 @@ interface RouterTransitionSnapshot {
22
22
  toRoute: State | null;
23
23
  fromRoute: State | null;
24
24
  }
25
+ interface RouterErrorSnapshot {
26
+ error: RouterError | null;
27
+ toRoute: State | null;
28
+ fromRoute: State | null;
29
+ version: number;
30
+ }
25
31
 
26
32
  /**
27
33
  * Creates a source for the full route state.
@@ -45,4 +51,6 @@ declare function createActiveRouteSource(router: Router, routeName: string, para
45
51
 
46
52
  declare function createTransitionSource(router: Router): RouterSource<RouterTransitionSnapshot>;
47
53
 
48
- export { type ActiveRouteSourceOptions, type RouteNodeSnapshot, type RouteSnapshot, type RouterSource, type RouterTransitionSnapshot, createActiveRouteSource, createRouteNodeSource, createRouteSource, createTransitionSource };
54
+ declare function createErrorSource(router: Router): RouterSource<RouterErrorSnapshot>;
55
+
56
+ export { type ActiveRouteSourceOptions, type RouteNodeSnapshot, type RouteSnapshot, type RouterErrorSnapshot, type RouterSource, type RouterTransitionSnapshot, createActiveRouteSource, createErrorSource, createRouteNodeSource, createRouteSource, createTransitionSource };
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- var t=require("@real-router/route-utils"),e=require("@real-router/core"),s=require("@real-router/core/api"),o=class{#t;#e=!1;#s=new Set;#o;#r;#n;constructor(t,e){this.#t=t,this.#o=e?.onFirstSubscribe,this.#r=e?.onLastUnsubscribe,this.#n=e?.onDestroy,this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#e?()=>{}:(0===this.#s.size&&this.#o&&this.#o(),this.#s.add(t),()=>{this.#s.delete(t),!this.#e&&0===this.#s.size&&this.#r&&this.#r()})}getSnapshot(){return this.#t}updateSnapshot(t){this.#e||(this.#t=t,this.#s.forEach(t=>{t()}))}destroy(){this.#e||(this.#e=!0,this.#n?.(),this.#s.clear())}};function r(t,e){return t===e?t:t?.path!==e?.path?e:t}function n(t,e,s,o){const n=o?.route??e.getState(),i=o?.previousRoute,u=""===s||void 0!==n&&(n.name===s||n.name.startsWith(`${s}.`))?n:void 0;if(u===t.route&&i===t.previousRoute)return t;const a=r(t.route,u),c=r(t.previousRoute,i);return a===t.route&&c===t.previousRoute?t:{route:a,previousRoute:c}}var i=new WeakMap,u={isTransitioning:!1,toRoute:null,fromRoute:null};exports.createActiveRouteSource=function(e,s,r,n){const i=n?.strict??!1,u=n?.ignoreQueryParams??!0,a=e.isActiveRoute(s,r,i,u),c=new o(a,{onDestroy:()=>{h()}}),h=e.subscribe(o=>{const n=t.areRoutesRelated(s,o.route.name),a=o.previousRoute&&t.areRoutesRelated(s,o.previousRoute.name);if(!n&&!a)return;const h=!!n&&e.isActiveRoute(s,r,i,u);Object.is(c.getSnapshot(),h)||c.updateSnapshot(h)});return c},exports.createRouteNodeSource=function(t,e){let s=null;const r=function(t,e){let s=i.get(t);s||(s=new Map,i.set(t,s));let o=s.get(e);return o||(o=t.shouldUpdateNode(e),s.set(e,o)),o}(t,e),u=()=>{const t=s;s=null,t?.()},a=new o(n({route:void 0,previousRoute:void 0},t,e),{onFirstSubscribe:()=>{const o=n(a.getSnapshot(),t,e);Object.is(o,a.getSnapshot())||a.updateSnapshot(o),s=t.subscribe(s=>{if(!r(s.route,s.previousRoute))return;const o=n(a.getSnapshot(),t,e,s);Object.is(a.getSnapshot(),o)||a.updateSnapshot(o)})},onLastUnsubscribe:u,onDestroy:u});return a},exports.createRouteSource=function(t){let e=null;const s=()=>{const t=e;e=null,t?.()},n=new o({route:t.getState(),previousRoute:void 0},{onFirstSubscribe:()=>{e=t.subscribe(t=>{const e=n.getSnapshot(),s=r(e.route,t.route),o=r(e.previousRoute,t.previousRoute);s===e.route&&o===e.previousRoute||n.updateSnapshot({route:s,previousRoute:o})})},onLastUnsubscribe:s,onDestroy:s});return n},exports.createTransitionSource=function(t){const n=new o(u,{onDestroy:()=>{c.forEach(t=>{t()})}}),i=s.getPluginApi(t),a=()=>{n.updateSnapshot(u)},c=[i.addEventListener(e.events.TRANSITION_START,(t,e)=>{const s=n.getSnapshot(),o=r(s.toRoute,t),i=r(s.fromRoute,e??null);s.isTransitioning&&o===s.toRoute&&i===s.fromRoute||n.updateSnapshot({isTransitioning:!0,toRoute:o,fromRoute:i})}),i.addEventListener(e.events.TRANSITION_SUCCESS,a),i.addEventListener(e.events.TRANSITION_ERROR,a),i.addEventListener(e.events.TRANSITION_CANCEL,a)];return n};//# sourceMappingURL=index.js.map
1
+ var t=require("@real-router/route-utils"),e=require("@real-router/core"),s=require("@real-router/core/api"),o=class{#t;#e=!1;#s=new Set;#o;#r;#n;constructor(t,e){this.#t=t,this.#o=e?.onFirstSubscribe,this.#r=e?.onLastUnsubscribe,this.#n=e?.onDestroy,this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#e?()=>{}:(0===this.#s.size&&this.#o&&this.#o(),this.#s.add(t),()=>{this.#s.delete(t),!this.#e&&0===this.#s.size&&this.#r&&this.#r()})}getSnapshot(){return this.#t}updateSnapshot(t){this.#e||(this.#t=t,this.#s.forEach(t=>{t()}))}destroy(){this.#e||(this.#e=!0,this.#n?.(),this.#s.clear())}};function r(t,e){return t===e?t:t?.path!==e?.path?e:t}function n(t,e,s,o){const n=o?.route??e.getState(),u=o?.previousRoute,i=""===s||void 0!==n&&(n.name===s||n.name.startsWith(`${s}.`))?n:void 0;if(i===t.route&&u===t.previousRoute)return t;const a=r(t.route,i),c=r(t.previousRoute,u);return a===t.route&&c===t.previousRoute?t:{route:a,previousRoute:c}}var u=new WeakMap,i={isTransitioning:!1,toRoute:null,fromRoute:null},a={error:null,toRoute:null,fromRoute:null,version:0};exports.createActiveRouteSource=function(e,s,r,n){const u=n?.strict??!1,i=n?.ignoreQueryParams??!0,a=e.isActiveRoute(s,r,u,i),c=new o(a,{onDestroy:()=>{h()}}),h=e.subscribe(o=>{const n=t.areRoutesRelated(s,o.route.name),a=o.previousRoute&&t.areRoutesRelated(s,o.previousRoute.name);if(!n&&!a)return;const h=!!n&&e.isActiveRoute(s,r,u,i);Object.is(c.getSnapshot(),h)||c.updateSnapshot(h)});return c},exports.createErrorSource=function(t){let r=0;const n=new o(a,{onDestroy:()=>{i.forEach(t=>{t()})}}),u=s.getPluginApi(t),i=[u.addEventListener(e.events.TRANSITION_ERROR,(t,e,s)=>{r++,n.updateSnapshot({error:s,toRoute:t??null,fromRoute:e??null,version:r})}),u.addEventListener(e.events.TRANSITION_SUCCESS,()=>{null!==n.getSnapshot().error&&n.updateSnapshot({error:null,toRoute:null,fromRoute:null,version:r})})];return n},exports.createRouteNodeSource=function(t,e){let s=null;const r=function(t,e){let s=u.get(t);s||(s=new Map,u.set(t,s));let o=s.get(e);return o||(o=t.shouldUpdateNode(e),s.set(e,o)),o}(t,e),i=()=>{const t=s;s=null,t?.()},a=new o(n({route:void 0,previousRoute:void 0},t,e),{onFirstSubscribe:()=>{const o=n(a.getSnapshot(),t,e);Object.is(o,a.getSnapshot())||a.updateSnapshot(o),s=t.subscribe(s=>{if(!r(s.route,s.previousRoute))return;const o=n(a.getSnapshot(),t,e,s);Object.is(a.getSnapshot(),o)||a.updateSnapshot(o)})},onLastUnsubscribe:i,onDestroy:i});return a},exports.createRouteSource=function(t){let e=null;const s=()=>{const t=e;e=null,t?.()},n=new o({route:t.getState(),previousRoute:void 0},{onFirstSubscribe:()=>{e=t.subscribe(t=>{const e=n.getSnapshot(),s=r(e.route,t.route),o=r(e.previousRoute,t.previousRoute);s===e.route&&o===e.previousRoute||n.updateSnapshot({route:s,previousRoute:o})})},onLastUnsubscribe:s,onDestroy:s});return n},exports.createTransitionSource=function(t){const n=new o(i,{onDestroy:()=>{c.forEach(t=>{t()})}}),u=s.getPluginApi(t),a=()=>{n.updateSnapshot(i)},c=[u.addEventListener(e.events.TRANSITION_START,(t,e)=>{const s=n.getSnapshot(),o=r(s.toRoute,t),u=r(s.fromRoute,e??null);s.isTransitioning&&o===s.toRoute&&u===s.fromRoute||n.updateSnapshot({isTransitioning:!0,toRoute:o,fromRoute:u})}),u.addEventListener(e.events.TRANSITION_SUCCESS,a),u.addEventListener(e.events.TRANSITION_ERROR,a),u.addEventListener(e.events.TRANSITION_CANCEL,a)];return n};//# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/BaseSource.ts","../../src/stabilizeState.ts","../../src/createRouteSource.ts","../../src/computeSnapshot.ts","../../src/shouldUpdateCache.ts","../../src/createRouteNodeSource.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts"],"names":["areRoutesRelated","api","getPluginApi","events"],"mappings":"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","file":"index.js","sourcesContent":["export interface BaseSourceOptions {\n onFirstSubscribe?: () => void;\n onLastUnsubscribe?: () => void;\n onDestroy?: () => void;\n}\n\nexport class BaseSource<T> {\n #currentSnapshot: T;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n readonly #onFirstSubscribe: (() => void) | undefined;\n readonly #onLastUnsubscribe: (() => void) | undefined;\n readonly #onDestroy: (() => void) | undefined;\n\n constructor(initialSnapshot: T, options?: BaseSourceOptions) {\n this.#currentSnapshot = initialSnapshot;\n this.#onFirstSubscribe = options?.onFirstSubscribe;\n this.#onLastUnsubscribe = options?.onLastUnsubscribe;\n this.#onDestroy = options?.onDestroy;\n\n this.subscribe = this.subscribe.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n this.destroy = this.destroy.bind(this);\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#destroyed) {\n return () => {};\n }\n\n if (this.#listeners.size === 0 && this.#onFirstSubscribe) {\n this.#onFirstSubscribe();\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (\n !this.#destroyed &&\n this.#listeners.size === 0 &&\n this.#onLastUnsubscribe\n ) {\n this.#onLastUnsubscribe();\n }\n };\n }\n\n getSnapshot(): T {\n return this.#currentSnapshot;\n }\n\n updateSnapshot(snapshot: T): void {\n /* v8 ignore next 2 -- @preserve: defensive guard unreachable via public API (destroy() removes router subscription first) */\n if (this.#destroyed) {\n return;\n }\n\n this.#currentSnapshot = snapshot;\n this.#listeners.forEach((listener) => {\n listener();\n });\n }\n\n destroy(): void {\n if (this.#destroyed) {\n return;\n }\n\n this.#destroyed = true;\n this.#onDestroy?.();\n this.#listeners.clear();\n }\n}\n","import type { State } from \"@real-router/core\";\n\n/**\n * State-aware stabilization for route snapshots.\n *\n * Compares `path` — the canonical representation of rendering-relevant\n * State fields (name + params). When path matches, returns `prev`\n * (preserving reference), enabling frameworks to skip re-renders.\n *\n * Ignores `meta` (internal: auto-increment id) and `transition`\n * (reference data: from, segments, reload) — they don't affect\n * what is rendered.\n *\n * Accepts `null` for compatibility with `RouterTransitionSnapshot`\n * (toRoute/fromRoute are `State | null`).\n *\n * @internal Not exported from package public API.\n */\nexport function stabilizeState<T extends State | null | undefined>(\n prev: T,\n next: T,\n): T {\n if (prev === next) {\n return prev;\n }\n if (prev?.path !== next?.path) {\n return next;\n }\n\n return prev;\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouteSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\n/**\n * Creates a source for the full route state.\n *\n * Uses a lazy-connection pattern: the router subscription is created when the\n * first listener subscribes and removed when the last listener unsubscribes.\n * This is compatible with React's useSyncExternalStore and Strict Mode.\n */\nexport function createRouteSource(router: Router): RouterSource<RouteSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const source = new BaseSource<RouteSnapshot>(\n {\n route: router.getState(),\n previousRoute: undefined,\n },\n {\n onFirstSubscribe: () => {\n routerUnsubscribe = router.subscribe((next) => {\n const prev = source.getSnapshot();\n const newRoute = stabilizeState(prev.route, next.route);\n const newPreviousRoute = stabilizeState(\n prev.previousRoute,\n next.previousRoute,\n );\n\n if (\n newRoute !== prev.route ||\n newPreviousRoute !== prev.previousRoute\n ) {\n source.updateSnapshot({\n route: newRoute,\n previousRoute: newPreviousRoute,\n });\n }\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\n}\n","import { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouteNodeSnapshot } from \"./types.js\";\nimport type { Router, SubscribeState } from \"@real-router/core\";\n\nexport function computeSnapshot(\n currentSnapshot: RouteNodeSnapshot,\n router: Router,\n nodeName: string,\n next?: SubscribeState,\n): RouteNodeSnapshot {\n const currentRoute = next?.route ?? router.getState();\n const previousRoute = next?.previousRoute;\n\n const isNodeActive =\n nodeName === \"\" ||\n (currentRoute !== undefined &&\n (currentRoute.name === nodeName ||\n currentRoute.name.startsWith(`${nodeName}.`)));\n\n const route = isNodeActive ? currentRoute : undefined;\n\n if (\n route === currentSnapshot.route &&\n previousRoute === currentSnapshot.previousRoute\n ) {\n return currentSnapshot;\n }\n\n const newRoute = stabilizeState(currentSnapshot.route, route);\n const newPreviousRoute = stabilizeState(\n currentSnapshot.previousRoute,\n previousRoute,\n );\n\n if (\n newRoute === currentSnapshot.route &&\n newPreviousRoute === currentSnapshot.previousRoute\n ) {\n return currentSnapshot;\n }\n\n return { route: newRoute, previousRoute: newPreviousRoute };\n}\n","import type { Router, State } from \"@real-router/core\";\n\nconst shouldUpdateCache = new WeakMap<\n Router,\n Map<string, (toState: State, fromState?: State) => boolean>\n>();\n\nexport function getCachedShouldUpdate(\n router: Router,\n nodeName: string,\n): (toState: State, fromState?: State) => boolean {\n let routerCache = shouldUpdateCache.get(router);\n\n if (!routerCache) {\n routerCache = new Map();\n shouldUpdateCache.set(router, routerCache);\n }\n\n let fn = routerCache.get(nodeName);\n\n if (!fn) {\n fn = router.shouldUpdateNode(nodeName);\n routerCache.set(nodeName, fn);\n }\n\n return fn;\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { computeSnapshot } from \"./computeSnapshot.js\";\nimport { getCachedShouldUpdate } from \"./shouldUpdateCache.js\";\n\nimport type { RouteNodeSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\n/**\n * Creates a source scoped to a specific route node.\n *\n * Uses a lazy-connection pattern: the router subscription is created when the\n * first listener subscribes and removed when the last listener unsubscribes.\n * This is compatible with React's useSyncExternalStore and Strict Mode.\n */\nexport function createRouteNodeSource(\n router: Router,\n nodeName: string,\n): RouterSource<RouteNodeSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n const shouldUpdate = getCachedShouldUpdate(router, nodeName);\n\n const initialSnapshot: RouteNodeSnapshot = {\n route: undefined,\n previousRoute: undefined,\n };\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const source = new BaseSource<RouteNodeSnapshot>(\n computeSnapshot(initialSnapshot, router, nodeName),\n {\n onFirstSubscribe: () => {\n // Reconcile snapshot with current router state before connecting.\n // Covers reconnection after Activity hide/show cycles where the\n // source was disconnected and missed navigation events.\n const reconciled = computeSnapshot(\n source.getSnapshot(),\n router,\n nodeName,\n );\n\n if (!Object.is(reconciled, source.getSnapshot())) {\n source.updateSnapshot(reconciled);\n }\n\n // Connect to router on first subscription\n routerUnsubscribe = router.subscribe((next) => {\n if (!shouldUpdate(next.route, next.previousRoute)) {\n return;\n }\n\n const newSnapshot = computeSnapshot(\n source.getSnapshot(),\n router,\n nodeName,\n next,\n );\n\n if (!Object.is(source.getSnapshot(), newSnapshot)) {\n source.updateSnapshot(newSnapshot);\n }\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\n}\n","import { areRoutesRelated } from \"@real-router/route-utils\";\n\nimport { BaseSource } from \"./BaseSource\";\n\nimport type { ActiveRouteSourceOptions, RouterSource } from \"./types.js\";\nimport type { Params, Router } from \"@real-router/core\";\n\nexport function createActiveRouteSource(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n): RouterSource<boolean> {\n const strict = options?.strict ?? false;\n const ignoreQueryParams = options?.ignoreQueryParams ?? true;\n\n const initialValue = router.isActiveRoute(\n routeName,\n params,\n strict,\n ignoreQueryParams,\n );\n\n const source = new BaseSource(initialValue, {\n onDestroy: () => {\n unsubscribe();\n },\n });\n\n // Eager connection: subscribe to router immediately\n const unsubscribe = router.subscribe((next) => {\n const isNewRelated = areRoutesRelated(routeName, next.route.name);\n const isPrevRelated =\n next.previousRoute &&\n areRoutesRelated(routeName, next.previousRoute.name);\n\n if (!isNewRelated && !isPrevRelated) {\n return;\n }\n\n // If new route is not related, we know the route is inactive —\n // avoid calling isActiveRoute for the optimization\n const newValue = isNewRelated\n ? router.isActiveRoute(routeName, params, strict, ignoreQueryParams)\n : false;\n\n if (!Object.is(source.getSnapshot(), newValue)) {\n source.updateSnapshot(newValue);\n }\n });\n\n return source;\n}\n","import { events } from \"@real-router/core\";\nimport { getPluginApi } from \"@real-router/core/api\";\n\nimport { BaseSource } from \"./BaseSource\";\nimport { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouterTransitionSnapshot, RouterSource } from \"./types.js\";\nimport type { Router, State } from \"@real-router/core\";\n\nconst IDLE_SNAPSHOT: RouterTransitionSnapshot = {\n isTransitioning: false,\n toRoute: null,\n fromRoute: null,\n};\n\nexport function createTransitionSource(\n router: Router,\n): RouterSource<RouterTransitionSnapshot> {\n const source = new BaseSource(IDLE_SNAPSHOT, {\n onDestroy: () => {\n unsubs.forEach((unsub) => {\n unsub();\n });\n },\n });\n\n const api = getPluginApi(router);\n\n const resetToIdle = (): void => {\n source.updateSnapshot(IDLE_SNAPSHOT);\n };\n\n // Eager connection: subscribe to router events immediately\n const unsubs = [\n api.addEventListener(\n events.TRANSITION_START,\n (toState: State, fromState?: State) => {\n const prev = source.getSnapshot();\n const newToRoute = stabilizeState(prev.toRoute, toState);\n const newFromRoute = stabilizeState(prev.fromRoute, fromState ?? null);\n\n if (\n !prev.isTransitioning ||\n newToRoute !== prev.toRoute ||\n newFromRoute !== prev.fromRoute\n ) {\n source.updateSnapshot({\n isTransitioning: true,\n toRoute: newToRoute,\n fromRoute: newFromRoute,\n });\n }\n },\n ),\n api.addEventListener(events.TRANSITION_SUCCESS, resetToIdle),\n api.addEventListener(events.TRANSITION_ERROR, resetToIdle),\n api.addEventListener(events.TRANSITION_CANCEL, resetToIdle),\n ];\n\n return source;\n}\n"]}
1
+ {"version":3,"sources":["../../src/BaseSource.ts","../../src/stabilizeState.ts","../../src/createRouteSource.ts","../../src/computeSnapshot.ts","../../src/shouldUpdateCache.ts","../../src/createRouteNodeSource.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts","../../src/createErrorSource.ts"],"names":["areRoutesRelated","api","getPluginApi","events"],"mappings":"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","file":"index.js","sourcesContent":["export interface BaseSourceOptions {\n onFirstSubscribe?: () => void;\n onLastUnsubscribe?: () => void;\n onDestroy?: () => void;\n}\n\nexport class BaseSource<T> {\n #currentSnapshot: T;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n readonly #onFirstSubscribe: (() => void) | undefined;\n readonly #onLastUnsubscribe: (() => void) | undefined;\n readonly #onDestroy: (() => void) | undefined;\n\n constructor(initialSnapshot: T, options?: BaseSourceOptions) {\n this.#currentSnapshot = initialSnapshot;\n this.#onFirstSubscribe = options?.onFirstSubscribe;\n this.#onLastUnsubscribe = options?.onLastUnsubscribe;\n this.#onDestroy = options?.onDestroy;\n\n this.subscribe = this.subscribe.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n this.destroy = this.destroy.bind(this);\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#destroyed) {\n return () => {};\n }\n\n if (this.#listeners.size === 0 && this.#onFirstSubscribe) {\n this.#onFirstSubscribe();\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (\n !this.#destroyed &&\n this.#listeners.size === 0 &&\n this.#onLastUnsubscribe\n ) {\n this.#onLastUnsubscribe();\n }\n };\n }\n\n getSnapshot(): T {\n return this.#currentSnapshot;\n }\n\n updateSnapshot(snapshot: T): void {\n /* v8 ignore next 2 -- @preserve: defensive guard unreachable via public API (destroy() removes router subscription first) */\n if (this.#destroyed) {\n return;\n }\n\n this.#currentSnapshot = snapshot;\n this.#listeners.forEach((listener) => {\n listener();\n });\n }\n\n destroy(): void {\n if (this.#destroyed) {\n return;\n }\n\n this.#destroyed = true;\n this.#onDestroy?.();\n this.#listeners.clear();\n }\n}\n","import type { State } from \"@real-router/core\";\n\n/**\n * State-aware stabilization for route snapshots.\n *\n * Compares `path` — the canonical representation of rendering-relevant\n * State fields (name + params). When path matches, returns `prev`\n * (preserving reference), enabling frameworks to skip re-renders.\n *\n * Ignores `meta` (internal: auto-increment id) and `transition`\n * (reference data: from, segments, reload) — they don't affect\n * what is rendered.\n *\n * Accepts `null` for compatibility with `RouterTransitionSnapshot`\n * (toRoute/fromRoute are `State | null`).\n *\n * @internal Not exported from package public API.\n */\nexport function stabilizeState<T extends State | null | undefined>(\n prev: T,\n next: T,\n): T {\n if (prev === next) {\n return prev;\n }\n if (prev?.path !== next?.path) {\n return next;\n }\n\n return prev;\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouteSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\n/**\n * Creates a source for the full route state.\n *\n * Uses a lazy-connection pattern: the router subscription is created when the\n * first listener subscribes and removed when the last listener unsubscribes.\n * This is compatible with React's useSyncExternalStore and Strict Mode.\n */\nexport function createRouteSource(router: Router): RouterSource<RouteSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const source = new BaseSource<RouteSnapshot>(\n {\n route: router.getState(),\n previousRoute: undefined,\n },\n {\n onFirstSubscribe: () => {\n routerUnsubscribe = router.subscribe((next) => {\n const prev = source.getSnapshot();\n const newRoute = stabilizeState(prev.route, next.route);\n const newPreviousRoute = stabilizeState(\n prev.previousRoute,\n next.previousRoute,\n );\n\n if (\n newRoute !== prev.route ||\n newPreviousRoute !== prev.previousRoute\n ) {\n source.updateSnapshot({\n route: newRoute,\n previousRoute: newPreviousRoute,\n });\n }\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\n}\n","import { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouteNodeSnapshot } from \"./types.js\";\nimport type { Router, SubscribeState } from \"@real-router/core\";\n\nexport function computeSnapshot(\n currentSnapshot: RouteNodeSnapshot,\n router: Router,\n nodeName: string,\n next?: SubscribeState,\n): RouteNodeSnapshot {\n const currentRoute = next?.route ?? router.getState();\n const previousRoute = next?.previousRoute;\n\n const isNodeActive =\n nodeName === \"\" ||\n (currentRoute !== undefined &&\n (currentRoute.name === nodeName ||\n currentRoute.name.startsWith(`${nodeName}.`)));\n\n const route = isNodeActive ? currentRoute : undefined;\n\n if (\n route === currentSnapshot.route &&\n previousRoute === currentSnapshot.previousRoute\n ) {\n return currentSnapshot;\n }\n\n const newRoute = stabilizeState(currentSnapshot.route, route);\n const newPreviousRoute = stabilizeState(\n currentSnapshot.previousRoute,\n previousRoute,\n );\n\n if (\n newRoute === currentSnapshot.route &&\n newPreviousRoute === currentSnapshot.previousRoute\n ) {\n return currentSnapshot;\n }\n\n return { route: newRoute, previousRoute: newPreviousRoute };\n}\n","import type { Router, State } from \"@real-router/core\";\n\nconst shouldUpdateCache = new WeakMap<\n Router,\n Map<string, (toState: State, fromState?: State) => boolean>\n>();\n\nexport function getCachedShouldUpdate(\n router: Router,\n nodeName: string,\n): (toState: State, fromState?: State) => boolean {\n let routerCache = shouldUpdateCache.get(router);\n\n if (!routerCache) {\n routerCache = new Map();\n shouldUpdateCache.set(router, routerCache);\n }\n\n let fn = routerCache.get(nodeName);\n\n if (!fn) {\n fn = router.shouldUpdateNode(nodeName);\n routerCache.set(nodeName, fn);\n }\n\n return fn;\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { computeSnapshot } from \"./computeSnapshot.js\";\nimport { getCachedShouldUpdate } from \"./shouldUpdateCache.js\";\n\nimport type { RouteNodeSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\n/**\n * Creates a source scoped to a specific route node.\n *\n * Uses a lazy-connection pattern: the router subscription is created when the\n * first listener subscribes and removed when the last listener unsubscribes.\n * This is compatible with React's useSyncExternalStore and Strict Mode.\n */\nexport function createRouteNodeSource(\n router: Router,\n nodeName: string,\n): RouterSource<RouteNodeSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n const shouldUpdate = getCachedShouldUpdate(router, nodeName);\n\n const initialSnapshot: RouteNodeSnapshot = {\n route: undefined,\n previousRoute: undefined,\n };\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const source = new BaseSource<RouteNodeSnapshot>(\n computeSnapshot(initialSnapshot, router, nodeName),\n {\n onFirstSubscribe: () => {\n // Reconcile snapshot with current router state before connecting.\n // Covers reconnection after Activity hide/show cycles where the\n // source was disconnected and missed navigation events.\n const reconciled = computeSnapshot(\n source.getSnapshot(),\n router,\n nodeName,\n );\n\n if (!Object.is(reconciled, source.getSnapshot())) {\n source.updateSnapshot(reconciled);\n }\n\n // Connect to router on first subscription\n routerUnsubscribe = router.subscribe((next) => {\n if (!shouldUpdate(next.route, next.previousRoute)) {\n return;\n }\n\n const newSnapshot = computeSnapshot(\n source.getSnapshot(),\n router,\n nodeName,\n next,\n );\n\n if (!Object.is(source.getSnapshot(), newSnapshot)) {\n source.updateSnapshot(newSnapshot);\n }\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\n}\n","import { areRoutesRelated } from \"@real-router/route-utils\";\n\nimport { BaseSource } from \"./BaseSource\";\n\nimport type { ActiveRouteSourceOptions, RouterSource } from \"./types.js\";\nimport type { Params, Router } from \"@real-router/core\";\n\nexport function createActiveRouteSource(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n): RouterSource<boolean> {\n const strict = options?.strict ?? false;\n const ignoreQueryParams = options?.ignoreQueryParams ?? true;\n\n const initialValue = router.isActiveRoute(\n routeName,\n params,\n strict,\n ignoreQueryParams,\n );\n\n const source = new BaseSource(initialValue, {\n onDestroy: () => {\n unsubscribe();\n },\n });\n\n // Eager connection: subscribe to router immediately\n const unsubscribe = router.subscribe((next) => {\n const isNewRelated = areRoutesRelated(routeName, next.route.name);\n const isPrevRelated =\n next.previousRoute &&\n areRoutesRelated(routeName, next.previousRoute.name);\n\n if (!isNewRelated && !isPrevRelated) {\n return;\n }\n\n // If new route is not related, we know the route is inactive —\n // avoid calling isActiveRoute for the optimization\n const newValue = isNewRelated\n ? router.isActiveRoute(routeName, params, strict, ignoreQueryParams)\n : false;\n\n if (!Object.is(source.getSnapshot(), newValue)) {\n source.updateSnapshot(newValue);\n }\n });\n\n return source;\n}\n","import { events } from \"@real-router/core\";\nimport { getPluginApi } from \"@real-router/core/api\";\n\nimport { BaseSource } from \"./BaseSource\";\nimport { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouterTransitionSnapshot, RouterSource } from \"./types.js\";\nimport type { Router, State } from \"@real-router/core\";\n\nconst IDLE_SNAPSHOT: RouterTransitionSnapshot = {\n isTransitioning: false,\n toRoute: null,\n fromRoute: null,\n};\n\nexport function createTransitionSource(\n router: Router,\n): RouterSource<RouterTransitionSnapshot> {\n const source = new BaseSource(IDLE_SNAPSHOT, {\n onDestroy: () => {\n unsubs.forEach((unsub) => {\n unsub();\n });\n },\n });\n\n const api = getPluginApi(router);\n\n const resetToIdle = (): void => {\n source.updateSnapshot(IDLE_SNAPSHOT);\n };\n\n // Eager connection: subscribe to router events immediately\n const unsubs = [\n api.addEventListener(\n events.TRANSITION_START,\n (toState: State, fromState?: State) => {\n const prev = source.getSnapshot();\n const newToRoute = stabilizeState(prev.toRoute, toState);\n const newFromRoute = stabilizeState(prev.fromRoute, fromState ?? null);\n\n if (\n !prev.isTransitioning ||\n newToRoute !== prev.toRoute ||\n newFromRoute !== prev.fromRoute\n ) {\n source.updateSnapshot({\n isTransitioning: true,\n toRoute: newToRoute,\n fromRoute: newFromRoute,\n });\n }\n },\n ),\n api.addEventListener(events.TRANSITION_SUCCESS, resetToIdle),\n api.addEventListener(events.TRANSITION_ERROR, resetToIdle),\n api.addEventListener(events.TRANSITION_CANCEL, resetToIdle),\n ];\n\n return source;\n}\n","import { events } from \"@real-router/core\";\nimport { getPluginApi } from \"@real-router/core/api\";\n\nimport { BaseSource } from \"./BaseSource\";\n\nimport type { RouterErrorSnapshot, RouterSource } from \"./types.js\";\nimport type { Router, State, RouterError } from \"@real-router/core\";\n\nconst INITIAL_SNAPSHOT: RouterErrorSnapshot = {\n error: null,\n toRoute: null,\n fromRoute: null,\n version: 0,\n};\n\nexport function createErrorSource(\n router: Router,\n): RouterSource<RouterErrorSnapshot> {\n let errorVersion = 0;\n\n const source = new BaseSource(INITIAL_SNAPSHOT, {\n onDestroy: () => {\n unsubs.forEach((unsub) => {\n unsub();\n });\n },\n });\n\n const api = getPluginApi(router);\n\n // Eager connection: subscribe to router events immediately\n const unsubs = [\n api.addEventListener(\n events.TRANSITION_ERROR,\n (\n toState: State | undefined,\n fromState: State | undefined,\n err: RouterError,\n ) => {\n errorVersion++;\n source.updateSnapshot({\n error: err,\n toRoute: toState ?? null,\n /* v8 ignore next -- @preserve: fromState undefined only during start() error; unreachable via navigate() */\n fromRoute: fromState ?? null,\n version: errorVersion,\n });\n },\n ),\n api.addEventListener(events.TRANSITION_SUCCESS, () => {\n // Skip if no error — avoids unnecessary re-renders.\n // BaseSource.updateSnapshot() always notifies listeners (new object = new ref),\n // and useSyncExternalStore compares via Object.is().\n if (source.getSnapshot().error !== null) {\n source.updateSnapshot({\n error: null,\n toRoute: null,\n fromRoute: null,\n version: errorVersion,\n });\n }\n }),\n ];\n\n return source;\n}\n"]}
@@ -1 +1 @@
1
- {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/BaseSource.ts":{"bytes":1857,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/stabilizeState.ts":{"bytes":856,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createRouteSource.ts":{"bytes":1599,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/computeSnapshot.ts":{"bytes":1184,"imports":[{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/shouldUpdateCache.ts":{"bytes":610,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createRouteNodeSource.ts":{"bytes":2239,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/computeSnapshot.ts","kind":"import-statement","original":"./computeSnapshot.js"},{"path":"src/shouldUpdateCache.ts","kind":"import-statement","original":"./shouldUpdateCache.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createActiveRouteSource.ts":{"bytes":1461,"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createTransitionSource.ts":{"bytes":1731,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":407,"imports":[{"path":"src/createRouteSource.ts","kind":"import-statement","original":"./createRouteSource"},{"path":"src/createRouteNodeSource.ts","kind":"import-statement","original":"./createRouteNodeSource"},{"path":"src/createActiveRouteSource.ts","kind":"import-statement","original":"./createActiveRouteSource"},{"path":"src/createTransitionSource.ts","kind":"import-statement","original":"./createTransitionSource"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":17153},"dist/cjs/index.js":{"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true}],"exports":["createActiveRouteSource","createRouteNodeSource","createRouteSource","createTransitionSource"],"entryPoint":"src/index.ts","inputs":{"src/BaseSource.ts":{"bytesInOutput":1375},"src/stabilizeState.ts":{"bytesInOutput":155},"src/createRouteSource.ts":{"bytesInOutput":966},"src/index.ts":{"bytesInOutput":0},"src/computeSnapshot.ts":{"bytesInOutput":860},"src/shouldUpdateCache.ts":{"bytesInOutput":425},"src/createRouteNodeSource.ts":{"bytesInOutput":1251},"src/createActiveRouteSource.ts":{"bytesInOutput":977},"src/createTransitionSource.ts":{"bytesInOutput":1279}},"bytes":7758}}}
1
+ {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/BaseSource.ts":{"bytes":1857,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/stabilizeState.ts":{"bytes":856,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createRouteSource.ts":{"bytes":1599,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/computeSnapshot.ts":{"bytes":1184,"imports":[{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/shouldUpdateCache.ts":{"bytes":610,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createRouteNodeSource.ts":{"bytes":2239,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/computeSnapshot.ts","kind":"import-statement","original":"./computeSnapshot.js"},{"path":"src/shouldUpdateCache.ts","kind":"import-statement","original":"./shouldUpdateCache.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createActiveRouteSource.ts":{"bytes":1461,"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createTransitionSource.ts":{"bytes":1731,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createErrorSource.ts":{"bytes":1806,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":488,"imports":[{"path":"src/createRouteSource.ts","kind":"import-statement","original":"./createRouteSource"},{"path":"src/createRouteNodeSource.ts","kind":"import-statement","original":"./createRouteNodeSource"},{"path":"src/createActiveRouteSource.ts","kind":"import-statement","original":"./createActiveRouteSource"},{"path":"src/createTransitionSource.ts","kind":"import-statement","original":"./createTransitionSource"},{"path":"src/createErrorSource.ts","kind":"import-statement","original":"./createErrorSource"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":19881},"dist/cjs/index.js":{"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true}],"exports":["createActiveRouteSource","createErrorSource","createRouteNodeSource","createRouteSource","createTransitionSource"],"entryPoint":"src/index.ts","inputs":{"src/BaseSource.ts":{"bytesInOutput":1375},"src/stabilizeState.ts":{"bytesInOutput":155},"src/createRouteSource.ts":{"bytesInOutput":966},"src/index.ts":{"bytesInOutput":0},"src/computeSnapshot.ts":{"bytesInOutput":860},"src/shouldUpdateCache.ts":{"bytesInOutput":425},"src/createRouteNodeSource.ts":{"bytesInOutput":1251},"src/createActiveRouteSource.ts":{"bytesInOutput":977},"src/createTransitionSource.ts":{"bytesInOutput":1279},"src/createErrorSource.ts":{"bytesInOutput":1210}},"bytes":9018}}}
@@ -1,4 +1,4 @@
1
- import { State, Router, Params } from '@real-router/core';
1
+ import { State, RouterError, Router, Params } from '@real-router/core';
2
2
 
3
3
  interface RouteSnapshot {
4
4
  route: State | undefined;
@@ -22,6 +22,12 @@ interface RouterTransitionSnapshot {
22
22
  toRoute: State | null;
23
23
  fromRoute: State | null;
24
24
  }
25
+ interface RouterErrorSnapshot {
26
+ error: RouterError | null;
27
+ toRoute: State | null;
28
+ fromRoute: State | null;
29
+ version: number;
30
+ }
25
31
 
26
32
  /**
27
33
  * Creates a source for the full route state.
@@ -45,4 +51,6 @@ declare function createActiveRouteSource(router: Router, routeName: string, para
45
51
 
46
52
  declare function createTransitionSource(router: Router): RouterSource<RouterTransitionSnapshot>;
47
53
 
48
- export { type ActiveRouteSourceOptions, type RouteNodeSnapshot, type RouteSnapshot, type RouterSource, type RouterTransitionSnapshot, createActiveRouteSource, createRouteNodeSource, createRouteSource, createTransitionSource };
54
+ declare function createErrorSource(router: Router): RouterSource<RouterErrorSnapshot>;
55
+
56
+ export { type ActiveRouteSourceOptions, type RouteNodeSnapshot, type RouteSnapshot, type RouterErrorSnapshot, type RouterSource, type RouterTransitionSnapshot, createActiveRouteSource, createErrorSource, createRouteNodeSource, createRouteSource, createTransitionSource };
@@ -1 +1 @@
1
- import{areRoutesRelated as t}from"@real-router/route-utils";import{events as e}from"@real-router/core";import{getPluginApi as s}from"@real-router/core/api";var o=class{#t;#e=!1;#s=new Set;#o;#r;#n;constructor(t,e){this.#t=t,this.#o=e?.onFirstSubscribe,this.#r=e?.onLastUnsubscribe,this.#n=e?.onDestroy,this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#e?()=>{}:(0===this.#s.size&&this.#o&&this.#o(),this.#s.add(t),()=>{this.#s.delete(t),!this.#e&&0===this.#s.size&&this.#r&&this.#r()})}getSnapshot(){return this.#t}updateSnapshot(t){this.#e||(this.#t=t,this.#s.forEach(t=>{t()}))}destroy(){this.#e||(this.#e=!0,this.#n?.(),this.#s.clear())}};function r(t,e){return t===e?t:t?.path!==e?.path?e:t}function n(t){let e=null;const s=()=>{const t=e;e=null,t?.()},n=new o({route:t.getState(),previousRoute:void 0},{onFirstSubscribe:()=>{e=t.subscribe(t=>{const e=n.getSnapshot(),s=r(e.route,t.route),o=r(e.previousRoute,t.previousRoute);s===e.route&&o===e.previousRoute||n.updateSnapshot({route:s,previousRoute:o})})},onLastUnsubscribe:s,onDestroy:s});return n}function i(t,e,s,o){const n=o?.route??e.getState(),i=o?.previousRoute,u=""===s||void 0!==n&&(n.name===s||n.name.startsWith(`${s}.`))?n:void 0;if(u===t.route&&i===t.previousRoute)return t;const a=r(t.route,u),c=r(t.previousRoute,i);return a===t.route&&c===t.previousRoute?t:{route:a,previousRoute:c}}var u=new WeakMap;function a(t,e){let s=null;const r=function(t,e){let s=u.get(t);s||(s=new Map,u.set(t,s));let o=s.get(e);return o||(o=t.shouldUpdateNode(e),s.set(e,o)),o}(t,e),n=()=>{const t=s;s=null,t?.()},a=new o(i({route:void 0,previousRoute:void 0},t,e),{onFirstSubscribe:()=>{const o=i(a.getSnapshot(),t,e);Object.is(o,a.getSnapshot())||a.updateSnapshot(o),s=t.subscribe(s=>{if(!r(s.route,s.previousRoute))return;const o=i(a.getSnapshot(),t,e,s);Object.is(a.getSnapshot(),o)||a.updateSnapshot(o)})},onLastUnsubscribe:n,onDestroy:n});return a}function c(e,s,r,n){const i=n?.strict??!1,u=n?.ignoreQueryParams??!0,a=e.isActiveRoute(s,r,i,u),c=new o(a,{onDestroy:()=>{h()}}),h=e.subscribe(o=>{const n=t(s,o.route.name),a=o.previousRoute&&t(s,o.previousRoute.name);if(!n&&!a)return;const h=!!n&&e.isActiveRoute(s,r,i,u);Object.is(c.getSnapshot(),h)||c.updateSnapshot(h)});return c}var h={isTransitioning:!1,toRoute:null,fromRoute:null};function p(t){const n=new o(h,{onDestroy:()=>{a.forEach(t=>{t()})}}),i=s(t),u=()=>{n.updateSnapshot(h)},a=[i.addEventListener(e.TRANSITION_START,(t,e)=>{const s=n.getSnapshot(),o=r(s.toRoute,t),i=r(s.fromRoute,e??null);s.isTransitioning&&o===s.toRoute&&i===s.fromRoute||n.updateSnapshot({isTransitioning:!0,toRoute:o,fromRoute:i})}),i.addEventListener(e.TRANSITION_SUCCESS,u),i.addEventListener(e.TRANSITION_ERROR,u),i.addEventListener(e.TRANSITION_CANCEL,u)];return n}export{c as createActiveRouteSource,a as createRouteNodeSource,n as createRouteSource,p as createTransitionSource};//# sourceMappingURL=index.mjs.map
1
+ import{areRoutesRelated as t}from"@real-router/route-utils";import{events as e}from"@real-router/core";import{getPluginApi as s}from"@real-router/core/api";var o=class{#t;#e=!1;#s=new Set;#o;#r;#n;constructor(t,e){this.#t=t,this.#o=e?.onFirstSubscribe,this.#r=e?.onLastUnsubscribe,this.#n=e?.onDestroy,this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#e?()=>{}:(0===this.#s.size&&this.#o&&this.#o(),this.#s.add(t),()=>{this.#s.delete(t),!this.#e&&0===this.#s.size&&this.#r&&this.#r()})}getSnapshot(){return this.#t}updateSnapshot(t){this.#e||(this.#t=t,this.#s.forEach(t=>{t()}))}destroy(){this.#e||(this.#e=!0,this.#n?.(),this.#s.clear())}};function r(t,e){return t===e?t:t?.path!==e?.path?e:t}function n(t){let e=null;const s=()=>{const t=e;e=null,t?.()},n=new o({route:t.getState(),previousRoute:void 0},{onFirstSubscribe:()=>{e=t.subscribe(t=>{const e=n.getSnapshot(),s=r(e.route,t.route),o=r(e.previousRoute,t.previousRoute);s===e.route&&o===e.previousRoute||n.updateSnapshot({route:s,previousRoute:o})})},onLastUnsubscribe:s,onDestroy:s});return n}function i(t,e,s,o){const n=o?.route??e.getState(),i=o?.previousRoute,u=""===s||void 0!==n&&(n.name===s||n.name.startsWith(`${s}.`))?n:void 0;if(u===t.route&&i===t.previousRoute)return t;const a=r(t.route,u),c=r(t.previousRoute,i);return a===t.route&&c===t.previousRoute?t:{route:a,previousRoute:c}}var u=new WeakMap;function a(t,e){let s=null;const r=function(t,e){let s=u.get(t);s||(s=new Map,u.set(t,s));let o=s.get(e);return o||(o=t.shouldUpdateNode(e),s.set(e,o)),o}(t,e),n=()=>{const t=s;s=null,t?.()},a=new o(i({route:void 0,previousRoute:void 0},t,e),{onFirstSubscribe:()=>{const o=i(a.getSnapshot(),t,e);Object.is(o,a.getSnapshot())||a.updateSnapshot(o),s=t.subscribe(s=>{if(!r(s.route,s.previousRoute))return;const o=i(a.getSnapshot(),t,e,s);Object.is(a.getSnapshot(),o)||a.updateSnapshot(o)})},onLastUnsubscribe:n,onDestroy:n});return a}function c(e,s,r,n){const i=n?.strict??!1,u=n?.ignoreQueryParams??!0,a=e.isActiveRoute(s,r,i,u),c=new o(a,{onDestroy:()=>{h()}}),h=e.subscribe(o=>{const n=t(s,o.route.name),a=o.previousRoute&&t(s,o.previousRoute.name);if(!n&&!a)return;const h=!!n&&e.isActiveRoute(s,r,i,u);Object.is(c.getSnapshot(),h)||c.updateSnapshot(h)});return c}var h={isTransitioning:!1,toRoute:null,fromRoute:null};function p(t){const n=new o(h,{onDestroy:()=>{a.forEach(t=>{t()})}}),i=s(t),u=()=>{n.updateSnapshot(h)},a=[i.addEventListener(e.TRANSITION_START,(t,e)=>{const s=n.getSnapshot(),o=r(s.toRoute,t),i=r(s.fromRoute,e??null);s.isTransitioning&&o===s.toRoute&&i===s.fromRoute||n.updateSnapshot({isTransitioning:!0,toRoute:o,fromRoute:i})}),i.addEventListener(e.TRANSITION_SUCCESS,u),i.addEventListener(e.TRANSITION_ERROR,u),i.addEventListener(e.TRANSITION_CANCEL,u)];return n}var l={error:null,toRoute:null,fromRoute:null,version:0};function d(t){let r=0;const n=new o(l,{onDestroy:()=>{u.forEach(t=>{t()})}}),i=s(t),u=[i.addEventListener(e.TRANSITION_ERROR,(t,e,s)=>{r++,n.updateSnapshot({error:s,toRoute:t??null,fromRoute:e??null,version:r})}),i.addEventListener(e.TRANSITION_SUCCESS,()=>{null!==n.getSnapshot().error&&n.updateSnapshot({error:null,toRoute:null,fromRoute:null,version:r})})];return n}export{c as createActiveRouteSource,d as createErrorSource,a as createRouteNodeSource,n as createRouteSource,p as createTransitionSource};//# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/BaseSource.ts","../../src/stabilizeState.ts","../../src/createRouteSource.ts","../../src/computeSnapshot.ts","../../src/shouldUpdateCache.ts","../../src/createRouteNodeSource.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts"],"names":[],"mappings":";AAMO,IAAM,aAAN,MAAoB;AAAA,EACzB,gBAAA;AAAA,EACA,UAAA,GAAa,KAAA;AAAA,EAEJ,UAAA,uBAAiB,GAAA,EAAgB;AAAA,EACjC,iBAAA;AAAA,EACA,kBAAA;AAAA,EACA,UAAA;AAAA,EAET,WAAA,CAAY,iBAAoB,OAAA,EAA6B;AAC3D,IAAA,IAAA,CAAK,gBAAA,GAAmB,eAAA;AACxB,IAAA,IAAA,CAAK,oBAAoB,OAAA,EAAS,gBAAA;AAClC,IAAA,IAAA,CAAK,qBAAqB,OAAA,EAAS,iBAAA;AACnC,IAAA,IAAA,CAAK,aAAa,OAAA,EAAS,SAAA;AAE3B,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA;AACzC,IAAA,IAAA,CAAK,WAAA,GAAc,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA;AAC7C,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA;AAAA,EACvC;AAAA,EAEA,UAAU,QAAA,EAAkC;AAC1C,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA,OAAO,MAAM;AAAA,MAAC,CAAA;AAAA,IAChB;AAEA,IAAA,IAAI,IAAA,CAAK,UAAA,CAAW,IAAA,KAAS,CAAA,IAAK,KAAK,iBAAA,EAAmB;AACxD,MAAA,IAAA,CAAK,iBAAA,EAAkB;AAAA,IACzB;AAEA,IAAA,IAAA,CAAK,UAAA,CAAW,IAAI,QAAQ,CAAA;AAE5B,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,UAAA,CAAW,OAAO,QAAQ,CAAA;AAE/B,MAAA,IACE,CAAC,KAAK,UAAA,IACN,IAAA,CAAK,WAAW,IAAA,KAAS,CAAA,IACzB,KAAK,kBAAA,EACL;AACA,QAAA,IAAA,CAAK,kBAAA,EAAmB;AAAA,MAC1B;AAAA,IACF,CAAA;AAAA,EACF;AAAA,EAEA,WAAA,GAAiB;AACf,IAAA,OAAO,IAAA,CAAK,gBAAA;AAAA,EACd;AAAA,EAEA,eAAe,QAAA,EAAmB;AAEhC,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,gBAAA,GAAmB,QAAA;AACxB,IAAA,IAAA,CAAK,UAAA,CAAW,OAAA,CAAQ,CAAC,QAAA,KAAa;AACpC,MAAA,QAAA,EAAS;AAAA,IACX,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,IAAA,IAAA,CAAK,UAAA,IAAa;AAClB,IAAA,IAAA,CAAK,WAAW,KAAA,EAAM;AAAA,EACxB;AACF,CAAA;;;ACzDO,SAAS,cAAA,CACd,MACA,IAAA,EACG;AACH,EAAA,IAAI,SAAS,IAAA,EAAM;AACjB,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,IAAI,IAAA,EAAM,IAAA,KAAS,IAAA,EAAM,IAAA,EAAM;AAC7B,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAA;AACT;;;ACjBO,SAAS,kBAAkB,MAAA,EAA6C;AAC7E,EAAA,IAAI,iBAAA,GAAyC,IAAA;AAE7C,EAAA,MAAM,aAAa,MAAY;AAC7B,IAAA,MAAM,KAAA,GAAQ,iBAAA;AAEd,IAAA,iBAAA,GAAoB,IAAA;AACpB,IAAA,KAAA,IAAQ;AAAA,EACV,CAAA;AAEA,EAAA,MAAM,SAAS,IAAI,UAAA;AAAA,IACjB;AAAA,MACE,KAAA,EAAO,OAAO,QAAA,EAAS;AAAA,MACvB,aAAA,EAAe;AAAA,KACjB;AAAA,IACA;AAAA,MACE,kBAAkB,MAAM;AACtB,QAAA,iBAAA,GAAoB,MAAA,CAAO,SAAA,CAAU,CAAC,IAAA,KAAS;AAC7C,UAAA,MAAM,IAAA,GAAO,OAAO,WAAA,EAAY;AAChC,UAAA,MAAM,QAAA,GAAW,cAAA,CAAe,IAAA,CAAK,KAAA,EAAO,KAAK,KAAK,CAAA;AACtD,UAAA,MAAM,gBAAA,GAAmB,cAAA;AAAA,YACvB,IAAA,CAAK,aAAA;AAAA,YACL,IAAA,CAAK;AAAA,WACP;AAEA,UAAA,IACE,QAAA,KAAa,IAAA,CAAK,KAAA,IAClB,gBAAA,KAAqB,KAAK,aAAA,EAC1B;AACA,YAAA,MAAA,CAAO,cAAA,CAAe;AAAA,cACpB,KAAA,EAAO,QAAA;AAAA,cACP,aAAA,EAAe;AAAA,aAChB,CAAA;AAAA,UACH;AAAA,QACF,CAAC,CAAA;AAAA,MACH,CAAA;AAAA,MACA,iBAAA,EAAmB,UAAA;AAAA,MACnB,SAAA,EAAW;AAAA;AACb,GACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AClDO,SAAS,eAAA,CACd,eAAA,EACA,MAAA,EACA,QAAA,EACA,IAAA,EACmB;AACnB,EAAA,MAAM,YAAA,GAAe,IAAA,EAAM,KAAA,IAAS,MAAA,CAAO,QAAA,EAAS;AACpD,EAAA,MAAM,gBAAgB,IAAA,EAAM,aAAA;AAE5B,EAAA,MAAM,YAAA,GACJ,QAAA,KAAa,EAAA,IACZ,YAAA,KAAiB,MAAA,KACf,YAAA,CAAa,IAAA,KAAS,QAAA,IACrB,YAAA,CAAa,IAAA,CAAK,UAAA,CAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,CAAG,CAAA,CAAA;AAEjD,EAAA,MAAM,KAAA,GAAQ,eAAe,YAAA,GAAe,MAAA;AAE5C,EAAA,IACE,KAAA,KAAU,eAAA,CAAgB,KAAA,IAC1B,aAAA,KAAkB,gBAAgB,aAAA,EAClC;AACA,IAAA,OAAO,eAAA;AAAA,EACT;AAEA,EAAA,MAAM,QAAA,GAAW,cAAA,CAAe,eAAA,CAAgB,KAAA,EAAO,KAAK,CAAA;AAC5D,EAAA,MAAM,gBAAA,GAAmB,cAAA;AAAA,IACvB,eAAA,CAAgB,aAAA;AAAA,IAChB;AAAA,GACF;AAEA,EAAA,IACE,QAAA,KAAa,eAAA,CAAgB,KAAA,IAC7B,gBAAA,KAAqB,gBAAgB,aAAA,EACrC;AACA,IAAA,OAAO,eAAA;AAAA,EACT;AAEA,EAAA,OAAO,EAAE,KAAA,EAAO,QAAA,EAAU,aAAA,EAAe,gBAAA,EAAiB;AAC5D;;;ACzCA,IAAM,iBAAA,uBAAwB,OAAA,EAG5B;AAEK,SAAS,qBAAA,CACd,QACA,QAAA,EACgD;AAChD,EAAA,IAAI,WAAA,GAAc,iBAAA,CAAkB,GAAA,CAAI,MAAM,CAAA;AAE9C,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,WAAA,uBAAkB,GAAA,EAAI;AACtB,IAAA,iBAAA,CAAkB,GAAA,CAAI,QAAQ,WAAW,CAAA;AAAA,EAC3C;AAEA,EAAA,IAAI,EAAA,GAAK,WAAA,CAAY,GAAA,CAAI,QAAQ,CAAA;AAEjC,EAAA,IAAI,CAAC,EAAA,EAAI;AACP,IAAA,EAAA,GAAK,MAAA,CAAO,iBAAiB,QAAQ,CAAA;AACrC,IAAA,WAAA,CAAY,GAAA,CAAI,UAAU,EAAE,CAAA;AAAA,EAC9B;AAEA,EAAA,OAAO,EAAA;AACT;;;ACZO,SAAS,qBAAA,CACd,QACA,QAAA,EACiC;AACjC,EAAA,IAAI,iBAAA,GAAyC,IAAA;AAE7C,EAAA,MAAM,YAAA,GAAe,qBAAA,CAAsB,MAAA,EAAQ,QAAQ,CAAA;AAE3D,EAAA,MAAM,eAAA,GAAqC;AAAA,IACzC,KAAA,EAAO,MAAA;AAAA,IACP,aAAA,EAAe;AAAA,GACjB;AAEA,EAAA,MAAM,aAAa,MAAY;AAC7B,IAAA,MAAM,KAAA,GAAQ,iBAAA;AAEd,IAAA,iBAAA,GAAoB,IAAA;AACpB,IAAA,KAAA,IAAQ;AAAA,EACV,CAAA;AAEA,EAAA,MAAM,SAAS,IAAI,UAAA;AAAA,IACjB,eAAA,CAAgB,eAAA,EAAiB,MAAA,EAAQ,QAAQ,CAAA;AAAA,IACjD;AAAA,MACE,kBAAkB,MAAM;AAItB,QAAA,MAAM,UAAA,GAAa,eAAA;AAAA,UACjB,OAAO,WAAA,EAAY;AAAA,UACnB,MAAA;AAAA,UACA;AAAA,SACF;AAEA,QAAA,IAAI,CAAC,MAAA,CAAO,EAAA,CAAG,YAAY,MAAA,CAAO,WAAA,EAAa,CAAA,EAAG;AAChD,UAAA,MAAA,CAAO,eAAe,UAAU,CAAA;AAAA,QAClC;AAGA,QAAA,iBAAA,GAAoB,MAAA,CAAO,SAAA,CAAU,CAAC,IAAA,KAAS;AAC7C,UAAA,IAAI,CAAC,YAAA,CAAa,IAAA,CAAK,KAAA,EAAO,IAAA,CAAK,aAAa,CAAA,EAAG;AACjD,YAAA;AAAA,UACF;AAEA,UAAA,MAAM,WAAA,GAAc,eAAA;AAAA,YAClB,OAAO,WAAA,EAAY;AAAA,YACnB,MAAA;AAAA,YACA,QAAA;AAAA,YACA;AAAA,WACF;AAEA,UAAA,IAAI,CAAC,MAAA,CAAO,EAAA,CAAG,OAAO,WAAA,EAAY,EAAG,WAAW,CAAA,EAAG;AACjD,YAAA,MAAA,CAAO,eAAe,WAAW,CAAA;AAAA,UACnC;AAAA,QACF,CAAC,CAAA;AAAA,MACH,CAAA;AAAA,MACA,iBAAA,EAAmB,UAAA;AAAA,MACnB,SAAA,EAAW;AAAA;AACb,GACF;AAEA,EAAA,OAAO,MAAA;AACT;ACpEO,SAAS,uBAAA,CACd,MAAA,EACA,SAAA,EACA,MAAA,EACA,OAAA,EACuB;AACvB,EAAA,MAAM,MAAA,GAAS,SAAS,MAAA,IAAU,KAAA;AAClC,EAAA,MAAM,iBAAA,GAAoB,SAAS,iBAAA,IAAqB,IAAA;AAExD,EAAA,MAAM,eAAe,MAAA,CAAO,aAAA;AAAA,IAC1B,SAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,MAAA,GAAS,IAAI,UAAA,CAAW,YAAA,EAAc;AAAA,IAC1C,WAAW,MAAM;AACf,MAAA,WAAA,EAAY;AAAA,IACd;AAAA,GACD,CAAA;AAGD,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,SAAA,CAAU,CAAC,IAAA,KAAS;AAC7C,IAAA,MAAM,YAAA,GAAe,gBAAA,CAAiB,SAAA,EAAW,IAAA,CAAK,MAAM,IAAI,CAAA;AAChE,IAAA,MAAM,gBACJ,IAAA,CAAK,aAAA,IACL,iBAAiB,SAAA,EAAW,IAAA,CAAK,cAAc,IAAI,CAAA;AAErD,IAAA,IAAI,CAAC,YAAA,IAAgB,CAAC,aAAA,EAAe;AACnC,MAAA;AAAA,IACF;AAIA,IAAA,MAAM,QAAA,GAAW,eACb,MAAA,CAAO,aAAA,CAAc,WAAW,MAAA,EAAQ,MAAA,EAAQ,iBAAiB,CAAA,GACjE,KAAA;AAEJ,IAAA,IAAI,CAAC,MAAA,CAAO,EAAA,CAAG,OAAO,WAAA,EAAY,EAAG,QAAQ,CAAA,EAAG;AAC9C,MAAA,MAAA,CAAO,eAAe,QAAQ,CAAA;AAAA,IAChC;AAAA,EACF,CAAC,CAAA;AAED,EAAA,OAAO,MAAA;AACT;AC3CA,IAAM,aAAA,GAA0C;AAAA,EAC9C,eAAA,EAAiB,KAAA;AAAA,EACjB,OAAA,EAAS,IAAA;AAAA,EACT,SAAA,EAAW;AACb,CAAA;AAEO,SAAS,uBACd,MAAA,EACwC;AACxC,EAAA,MAAM,MAAA,GAAS,IAAI,UAAA,CAAW,aAAA,EAAe;AAAA,IAC3C,WAAW,MAAM;AACf,MAAA,MAAA,CAAO,OAAA,CAAQ,CAAC,KAAA,KAAU;AACxB,QAAA,KAAA,EAAM;AAAA,MACR,CAAC,CAAA;AAAA,IACH;AAAA,GACD,CAAA;AAED,EAAA,MAAM,GAAA,GAAM,aAAa,MAAM,CAAA;AAE/B,EAAA,MAAM,cAAc,MAAY;AAC9B,IAAA,MAAA,CAAO,eAAe,aAAa,CAAA;AAAA,EACrC,CAAA;AAGA,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,GAAA,CAAI,gBAAA;AAAA,MACF,MAAA,CAAO,gBAAA;AAAA,MACP,CAAC,SAAgB,SAAA,KAAsB;AACrC,QAAA,MAAM,IAAA,GAAO,OAAO,WAAA,EAAY;AAChC,QAAA,MAAM,UAAA,GAAa,cAAA,CAAe,IAAA,CAAK,OAAA,EAAS,OAAO,CAAA;AACvD,QAAA,MAAM,YAAA,GAAe,cAAA,CAAe,IAAA,CAAK,SAAA,EAAW,aAAa,IAAI,CAAA;AAErE,QAAA,IACE,CAAC,KAAK,eAAA,IACN,UAAA,KAAe,KAAK,OAAA,IACpB,YAAA,KAAiB,KAAK,SAAA,EACtB;AACA,UAAA,MAAA,CAAO,cAAA,CAAe;AAAA,YACpB,eAAA,EAAiB,IAAA;AAAA,YACjB,OAAA,EAAS,UAAA;AAAA,YACT,SAAA,EAAW;AAAA,WACZ,CAAA;AAAA,QACH;AAAA,MACF;AAAA,KACF;AAAA,IACA,GAAA,CAAI,gBAAA,CAAiB,MAAA,CAAO,kBAAA,EAAoB,WAAW,CAAA;AAAA,IAC3D,GAAA,CAAI,gBAAA,CAAiB,MAAA,CAAO,gBAAA,EAAkB,WAAW,CAAA;AAAA,IACzD,GAAA,CAAI,gBAAA,CAAiB,MAAA,CAAO,iBAAA,EAAmB,WAAW;AAAA,GAC5D;AAEA,EAAA,OAAO,MAAA;AACT","file":"index.mjs","sourcesContent":["export interface BaseSourceOptions {\n onFirstSubscribe?: () => void;\n onLastUnsubscribe?: () => void;\n onDestroy?: () => void;\n}\n\nexport class BaseSource<T> {\n #currentSnapshot: T;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n readonly #onFirstSubscribe: (() => void) | undefined;\n readonly #onLastUnsubscribe: (() => void) | undefined;\n readonly #onDestroy: (() => void) | undefined;\n\n constructor(initialSnapshot: T, options?: BaseSourceOptions) {\n this.#currentSnapshot = initialSnapshot;\n this.#onFirstSubscribe = options?.onFirstSubscribe;\n this.#onLastUnsubscribe = options?.onLastUnsubscribe;\n this.#onDestroy = options?.onDestroy;\n\n this.subscribe = this.subscribe.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n this.destroy = this.destroy.bind(this);\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#destroyed) {\n return () => {};\n }\n\n if (this.#listeners.size === 0 && this.#onFirstSubscribe) {\n this.#onFirstSubscribe();\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (\n !this.#destroyed &&\n this.#listeners.size === 0 &&\n this.#onLastUnsubscribe\n ) {\n this.#onLastUnsubscribe();\n }\n };\n }\n\n getSnapshot(): T {\n return this.#currentSnapshot;\n }\n\n updateSnapshot(snapshot: T): void {\n /* v8 ignore next 2 -- @preserve: defensive guard unreachable via public API (destroy() removes router subscription first) */\n if (this.#destroyed) {\n return;\n }\n\n this.#currentSnapshot = snapshot;\n this.#listeners.forEach((listener) => {\n listener();\n });\n }\n\n destroy(): void {\n if (this.#destroyed) {\n return;\n }\n\n this.#destroyed = true;\n this.#onDestroy?.();\n this.#listeners.clear();\n }\n}\n","import type { State } from \"@real-router/core\";\n\n/**\n * State-aware stabilization for route snapshots.\n *\n * Compares `path` — the canonical representation of rendering-relevant\n * State fields (name + params). When path matches, returns `prev`\n * (preserving reference), enabling frameworks to skip re-renders.\n *\n * Ignores `meta` (internal: auto-increment id) and `transition`\n * (reference data: from, segments, reload) — they don't affect\n * what is rendered.\n *\n * Accepts `null` for compatibility with `RouterTransitionSnapshot`\n * (toRoute/fromRoute are `State | null`).\n *\n * @internal Not exported from package public API.\n */\nexport function stabilizeState<T extends State | null | undefined>(\n prev: T,\n next: T,\n): T {\n if (prev === next) {\n return prev;\n }\n if (prev?.path !== next?.path) {\n return next;\n }\n\n return prev;\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouteSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\n/**\n * Creates a source for the full route state.\n *\n * Uses a lazy-connection pattern: the router subscription is created when the\n * first listener subscribes and removed when the last listener unsubscribes.\n * This is compatible with React's useSyncExternalStore and Strict Mode.\n */\nexport function createRouteSource(router: Router): RouterSource<RouteSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const source = new BaseSource<RouteSnapshot>(\n {\n route: router.getState(),\n previousRoute: undefined,\n },\n {\n onFirstSubscribe: () => {\n routerUnsubscribe = router.subscribe((next) => {\n const prev = source.getSnapshot();\n const newRoute = stabilizeState(prev.route, next.route);\n const newPreviousRoute = stabilizeState(\n prev.previousRoute,\n next.previousRoute,\n );\n\n if (\n newRoute !== prev.route ||\n newPreviousRoute !== prev.previousRoute\n ) {\n source.updateSnapshot({\n route: newRoute,\n previousRoute: newPreviousRoute,\n });\n }\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\n}\n","import { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouteNodeSnapshot } from \"./types.js\";\nimport type { Router, SubscribeState } from \"@real-router/core\";\n\nexport function computeSnapshot(\n currentSnapshot: RouteNodeSnapshot,\n router: Router,\n nodeName: string,\n next?: SubscribeState,\n): RouteNodeSnapshot {\n const currentRoute = next?.route ?? router.getState();\n const previousRoute = next?.previousRoute;\n\n const isNodeActive =\n nodeName === \"\" ||\n (currentRoute !== undefined &&\n (currentRoute.name === nodeName ||\n currentRoute.name.startsWith(`${nodeName}.`)));\n\n const route = isNodeActive ? currentRoute : undefined;\n\n if (\n route === currentSnapshot.route &&\n previousRoute === currentSnapshot.previousRoute\n ) {\n return currentSnapshot;\n }\n\n const newRoute = stabilizeState(currentSnapshot.route, route);\n const newPreviousRoute = stabilizeState(\n currentSnapshot.previousRoute,\n previousRoute,\n );\n\n if (\n newRoute === currentSnapshot.route &&\n newPreviousRoute === currentSnapshot.previousRoute\n ) {\n return currentSnapshot;\n }\n\n return { route: newRoute, previousRoute: newPreviousRoute };\n}\n","import type { Router, State } from \"@real-router/core\";\n\nconst shouldUpdateCache = new WeakMap<\n Router,\n Map<string, (toState: State, fromState?: State) => boolean>\n>();\n\nexport function getCachedShouldUpdate(\n router: Router,\n nodeName: string,\n): (toState: State, fromState?: State) => boolean {\n let routerCache = shouldUpdateCache.get(router);\n\n if (!routerCache) {\n routerCache = new Map();\n shouldUpdateCache.set(router, routerCache);\n }\n\n let fn = routerCache.get(nodeName);\n\n if (!fn) {\n fn = router.shouldUpdateNode(nodeName);\n routerCache.set(nodeName, fn);\n }\n\n return fn;\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { computeSnapshot } from \"./computeSnapshot.js\";\nimport { getCachedShouldUpdate } from \"./shouldUpdateCache.js\";\n\nimport type { RouteNodeSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\n/**\n * Creates a source scoped to a specific route node.\n *\n * Uses a lazy-connection pattern: the router subscription is created when the\n * first listener subscribes and removed when the last listener unsubscribes.\n * This is compatible with React's useSyncExternalStore and Strict Mode.\n */\nexport function createRouteNodeSource(\n router: Router,\n nodeName: string,\n): RouterSource<RouteNodeSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n const shouldUpdate = getCachedShouldUpdate(router, nodeName);\n\n const initialSnapshot: RouteNodeSnapshot = {\n route: undefined,\n previousRoute: undefined,\n };\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const source = new BaseSource<RouteNodeSnapshot>(\n computeSnapshot(initialSnapshot, router, nodeName),\n {\n onFirstSubscribe: () => {\n // Reconcile snapshot with current router state before connecting.\n // Covers reconnection after Activity hide/show cycles where the\n // source was disconnected and missed navigation events.\n const reconciled = computeSnapshot(\n source.getSnapshot(),\n router,\n nodeName,\n );\n\n if (!Object.is(reconciled, source.getSnapshot())) {\n source.updateSnapshot(reconciled);\n }\n\n // Connect to router on first subscription\n routerUnsubscribe = router.subscribe((next) => {\n if (!shouldUpdate(next.route, next.previousRoute)) {\n return;\n }\n\n const newSnapshot = computeSnapshot(\n source.getSnapshot(),\n router,\n nodeName,\n next,\n );\n\n if (!Object.is(source.getSnapshot(), newSnapshot)) {\n source.updateSnapshot(newSnapshot);\n }\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\n}\n","import { areRoutesRelated } from \"@real-router/route-utils\";\n\nimport { BaseSource } from \"./BaseSource\";\n\nimport type { ActiveRouteSourceOptions, RouterSource } from \"./types.js\";\nimport type { Params, Router } from \"@real-router/core\";\n\nexport function createActiveRouteSource(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n): RouterSource<boolean> {\n const strict = options?.strict ?? false;\n const ignoreQueryParams = options?.ignoreQueryParams ?? true;\n\n const initialValue = router.isActiveRoute(\n routeName,\n params,\n strict,\n ignoreQueryParams,\n );\n\n const source = new BaseSource(initialValue, {\n onDestroy: () => {\n unsubscribe();\n },\n });\n\n // Eager connection: subscribe to router immediately\n const unsubscribe = router.subscribe((next) => {\n const isNewRelated = areRoutesRelated(routeName, next.route.name);\n const isPrevRelated =\n next.previousRoute &&\n areRoutesRelated(routeName, next.previousRoute.name);\n\n if (!isNewRelated && !isPrevRelated) {\n return;\n }\n\n // If new route is not related, we know the route is inactive —\n // avoid calling isActiveRoute for the optimization\n const newValue = isNewRelated\n ? router.isActiveRoute(routeName, params, strict, ignoreQueryParams)\n : false;\n\n if (!Object.is(source.getSnapshot(), newValue)) {\n source.updateSnapshot(newValue);\n }\n });\n\n return source;\n}\n","import { events } from \"@real-router/core\";\nimport { getPluginApi } from \"@real-router/core/api\";\n\nimport { BaseSource } from \"./BaseSource\";\nimport { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouterTransitionSnapshot, RouterSource } from \"./types.js\";\nimport type { Router, State } from \"@real-router/core\";\n\nconst IDLE_SNAPSHOT: RouterTransitionSnapshot = {\n isTransitioning: false,\n toRoute: null,\n fromRoute: null,\n};\n\nexport function createTransitionSource(\n router: Router,\n): RouterSource<RouterTransitionSnapshot> {\n const source = new BaseSource(IDLE_SNAPSHOT, {\n onDestroy: () => {\n unsubs.forEach((unsub) => {\n unsub();\n });\n },\n });\n\n const api = getPluginApi(router);\n\n const resetToIdle = (): void => {\n source.updateSnapshot(IDLE_SNAPSHOT);\n };\n\n // Eager connection: subscribe to router events immediately\n const unsubs = [\n api.addEventListener(\n events.TRANSITION_START,\n (toState: State, fromState?: State) => {\n const prev = source.getSnapshot();\n const newToRoute = stabilizeState(prev.toRoute, toState);\n const newFromRoute = stabilizeState(prev.fromRoute, fromState ?? null);\n\n if (\n !prev.isTransitioning ||\n newToRoute !== prev.toRoute ||\n newFromRoute !== prev.fromRoute\n ) {\n source.updateSnapshot({\n isTransitioning: true,\n toRoute: newToRoute,\n fromRoute: newFromRoute,\n });\n }\n },\n ),\n api.addEventListener(events.TRANSITION_SUCCESS, resetToIdle),\n api.addEventListener(events.TRANSITION_ERROR, resetToIdle),\n api.addEventListener(events.TRANSITION_CANCEL, resetToIdle),\n ];\n\n return source;\n}\n"]}
1
+ {"version":3,"sources":["../../src/BaseSource.ts","../../src/stabilizeState.ts","../../src/createRouteSource.ts","../../src/computeSnapshot.ts","../../src/shouldUpdateCache.ts","../../src/createRouteNodeSource.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts","../../src/createErrorSource.ts"],"names":["getPluginApi","events"],"mappings":"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","file":"index.mjs","sourcesContent":["export interface BaseSourceOptions {\n onFirstSubscribe?: () => void;\n onLastUnsubscribe?: () => void;\n onDestroy?: () => void;\n}\n\nexport class BaseSource<T> {\n #currentSnapshot: T;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n readonly #onFirstSubscribe: (() => void) | undefined;\n readonly #onLastUnsubscribe: (() => void) | undefined;\n readonly #onDestroy: (() => void) | undefined;\n\n constructor(initialSnapshot: T, options?: BaseSourceOptions) {\n this.#currentSnapshot = initialSnapshot;\n this.#onFirstSubscribe = options?.onFirstSubscribe;\n this.#onLastUnsubscribe = options?.onLastUnsubscribe;\n this.#onDestroy = options?.onDestroy;\n\n this.subscribe = this.subscribe.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n this.destroy = this.destroy.bind(this);\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#destroyed) {\n return () => {};\n }\n\n if (this.#listeners.size === 0 && this.#onFirstSubscribe) {\n this.#onFirstSubscribe();\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (\n !this.#destroyed &&\n this.#listeners.size === 0 &&\n this.#onLastUnsubscribe\n ) {\n this.#onLastUnsubscribe();\n }\n };\n }\n\n getSnapshot(): T {\n return this.#currentSnapshot;\n }\n\n updateSnapshot(snapshot: T): void {\n /* v8 ignore next 2 -- @preserve: defensive guard unreachable via public API (destroy() removes router subscription first) */\n if (this.#destroyed) {\n return;\n }\n\n this.#currentSnapshot = snapshot;\n this.#listeners.forEach((listener) => {\n listener();\n });\n }\n\n destroy(): void {\n if (this.#destroyed) {\n return;\n }\n\n this.#destroyed = true;\n this.#onDestroy?.();\n this.#listeners.clear();\n }\n}\n","import type { State } from \"@real-router/core\";\n\n/**\n * State-aware stabilization for route snapshots.\n *\n * Compares `path` — the canonical representation of rendering-relevant\n * State fields (name + params). When path matches, returns `prev`\n * (preserving reference), enabling frameworks to skip re-renders.\n *\n * Ignores `meta` (internal: auto-increment id) and `transition`\n * (reference data: from, segments, reload) — they don't affect\n * what is rendered.\n *\n * Accepts `null` for compatibility with `RouterTransitionSnapshot`\n * (toRoute/fromRoute are `State | null`).\n *\n * @internal Not exported from package public API.\n */\nexport function stabilizeState<T extends State | null | undefined>(\n prev: T,\n next: T,\n): T {\n if (prev === next) {\n return prev;\n }\n if (prev?.path !== next?.path) {\n return next;\n }\n\n return prev;\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouteSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\n/**\n * Creates a source for the full route state.\n *\n * Uses a lazy-connection pattern: the router subscription is created when the\n * first listener subscribes and removed when the last listener unsubscribes.\n * This is compatible with React's useSyncExternalStore and Strict Mode.\n */\nexport function createRouteSource(router: Router): RouterSource<RouteSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const source = new BaseSource<RouteSnapshot>(\n {\n route: router.getState(),\n previousRoute: undefined,\n },\n {\n onFirstSubscribe: () => {\n routerUnsubscribe = router.subscribe((next) => {\n const prev = source.getSnapshot();\n const newRoute = stabilizeState(prev.route, next.route);\n const newPreviousRoute = stabilizeState(\n prev.previousRoute,\n next.previousRoute,\n );\n\n if (\n newRoute !== prev.route ||\n newPreviousRoute !== prev.previousRoute\n ) {\n source.updateSnapshot({\n route: newRoute,\n previousRoute: newPreviousRoute,\n });\n }\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\n}\n","import { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouteNodeSnapshot } from \"./types.js\";\nimport type { Router, SubscribeState } from \"@real-router/core\";\n\nexport function computeSnapshot(\n currentSnapshot: RouteNodeSnapshot,\n router: Router,\n nodeName: string,\n next?: SubscribeState,\n): RouteNodeSnapshot {\n const currentRoute = next?.route ?? router.getState();\n const previousRoute = next?.previousRoute;\n\n const isNodeActive =\n nodeName === \"\" ||\n (currentRoute !== undefined &&\n (currentRoute.name === nodeName ||\n currentRoute.name.startsWith(`${nodeName}.`)));\n\n const route = isNodeActive ? currentRoute : undefined;\n\n if (\n route === currentSnapshot.route &&\n previousRoute === currentSnapshot.previousRoute\n ) {\n return currentSnapshot;\n }\n\n const newRoute = stabilizeState(currentSnapshot.route, route);\n const newPreviousRoute = stabilizeState(\n currentSnapshot.previousRoute,\n previousRoute,\n );\n\n if (\n newRoute === currentSnapshot.route &&\n newPreviousRoute === currentSnapshot.previousRoute\n ) {\n return currentSnapshot;\n }\n\n return { route: newRoute, previousRoute: newPreviousRoute };\n}\n","import type { Router, State } from \"@real-router/core\";\n\nconst shouldUpdateCache = new WeakMap<\n Router,\n Map<string, (toState: State, fromState?: State) => boolean>\n>();\n\nexport function getCachedShouldUpdate(\n router: Router,\n nodeName: string,\n): (toState: State, fromState?: State) => boolean {\n let routerCache = shouldUpdateCache.get(router);\n\n if (!routerCache) {\n routerCache = new Map();\n shouldUpdateCache.set(router, routerCache);\n }\n\n let fn = routerCache.get(nodeName);\n\n if (!fn) {\n fn = router.shouldUpdateNode(nodeName);\n routerCache.set(nodeName, fn);\n }\n\n return fn;\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { computeSnapshot } from \"./computeSnapshot.js\";\nimport { getCachedShouldUpdate } from \"./shouldUpdateCache.js\";\n\nimport type { RouteNodeSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\n/**\n * Creates a source scoped to a specific route node.\n *\n * Uses a lazy-connection pattern: the router subscription is created when the\n * first listener subscribes and removed when the last listener unsubscribes.\n * This is compatible with React's useSyncExternalStore and Strict Mode.\n */\nexport function createRouteNodeSource(\n router: Router,\n nodeName: string,\n): RouterSource<RouteNodeSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n const shouldUpdate = getCachedShouldUpdate(router, nodeName);\n\n const initialSnapshot: RouteNodeSnapshot = {\n route: undefined,\n previousRoute: undefined,\n };\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const source = new BaseSource<RouteNodeSnapshot>(\n computeSnapshot(initialSnapshot, router, nodeName),\n {\n onFirstSubscribe: () => {\n // Reconcile snapshot with current router state before connecting.\n // Covers reconnection after Activity hide/show cycles where the\n // source was disconnected and missed navigation events.\n const reconciled = computeSnapshot(\n source.getSnapshot(),\n router,\n nodeName,\n );\n\n if (!Object.is(reconciled, source.getSnapshot())) {\n source.updateSnapshot(reconciled);\n }\n\n // Connect to router on first subscription\n routerUnsubscribe = router.subscribe((next) => {\n if (!shouldUpdate(next.route, next.previousRoute)) {\n return;\n }\n\n const newSnapshot = computeSnapshot(\n source.getSnapshot(),\n router,\n nodeName,\n next,\n );\n\n if (!Object.is(source.getSnapshot(), newSnapshot)) {\n source.updateSnapshot(newSnapshot);\n }\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\n}\n","import { areRoutesRelated } from \"@real-router/route-utils\";\n\nimport { BaseSource } from \"./BaseSource\";\n\nimport type { ActiveRouteSourceOptions, RouterSource } from \"./types.js\";\nimport type { Params, Router } from \"@real-router/core\";\n\nexport function createActiveRouteSource(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n): RouterSource<boolean> {\n const strict = options?.strict ?? false;\n const ignoreQueryParams = options?.ignoreQueryParams ?? true;\n\n const initialValue = router.isActiveRoute(\n routeName,\n params,\n strict,\n ignoreQueryParams,\n );\n\n const source = new BaseSource(initialValue, {\n onDestroy: () => {\n unsubscribe();\n },\n });\n\n // Eager connection: subscribe to router immediately\n const unsubscribe = router.subscribe((next) => {\n const isNewRelated = areRoutesRelated(routeName, next.route.name);\n const isPrevRelated =\n next.previousRoute &&\n areRoutesRelated(routeName, next.previousRoute.name);\n\n if (!isNewRelated && !isPrevRelated) {\n return;\n }\n\n // If new route is not related, we know the route is inactive —\n // avoid calling isActiveRoute for the optimization\n const newValue = isNewRelated\n ? router.isActiveRoute(routeName, params, strict, ignoreQueryParams)\n : false;\n\n if (!Object.is(source.getSnapshot(), newValue)) {\n source.updateSnapshot(newValue);\n }\n });\n\n return source;\n}\n","import { events } from \"@real-router/core\";\nimport { getPluginApi } from \"@real-router/core/api\";\n\nimport { BaseSource } from \"./BaseSource\";\nimport { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouterTransitionSnapshot, RouterSource } from \"./types.js\";\nimport type { Router, State } from \"@real-router/core\";\n\nconst IDLE_SNAPSHOT: RouterTransitionSnapshot = {\n isTransitioning: false,\n toRoute: null,\n fromRoute: null,\n};\n\nexport function createTransitionSource(\n router: Router,\n): RouterSource<RouterTransitionSnapshot> {\n const source = new BaseSource(IDLE_SNAPSHOT, {\n onDestroy: () => {\n unsubs.forEach((unsub) => {\n unsub();\n });\n },\n });\n\n const api = getPluginApi(router);\n\n const resetToIdle = (): void => {\n source.updateSnapshot(IDLE_SNAPSHOT);\n };\n\n // Eager connection: subscribe to router events immediately\n const unsubs = [\n api.addEventListener(\n events.TRANSITION_START,\n (toState: State, fromState?: State) => {\n const prev = source.getSnapshot();\n const newToRoute = stabilizeState(prev.toRoute, toState);\n const newFromRoute = stabilizeState(prev.fromRoute, fromState ?? null);\n\n if (\n !prev.isTransitioning ||\n newToRoute !== prev.toRoute ||\n newFromRoute !== prev.fromRoute\n ) {\n source.updateSnapshot({\n isTransitioning: true,\n toRoute: newToRoute,\n fromRoute: newFromRoute,\n });\n }\n },\n ),\n api.addEventListener(events.TRANSITION_SUCCESS, resetToIdle),\n api.addEventListener(events.TRANSITION_ERROR, resetToIdle),\n api.addEventListener(events.TRANSITION_CANCEL, resetToIdle),\n ];\n\n return source;\n}\n","import { events } from \"@real-router/core\";\nimport { getPluginApi } from \"@real-router/core/api\";\n\nimport { BaseSource } from \"./BaseSource\";\n\nimport type { RouterErrorSnapshot, RouterSource } from \"./types.js\";\nimport type { Router, State, RouterError } from \"@real-router/core\";\n\nconst INITIAL_SNAPSHOT: RouterErrorSnapshot = {\n error: null,\n toRoute: null,\n fromRoute: null,\n version: 0,\n};\n\nexport function createErrorSource(\n router: Router,\n): RouterSource<RouterErrorSnapshot> {\n let errorVersion = 0;\n\n const source = new BaseSource(INITIAL_SNAPSHOT, {\n onDestroy: () => {\n unsubs.forEach((unsub) => {\n unsub();\n });\n },\n });\n\n const api = getPluginApi(router);\n\n // Eager connection: subscribe to router events immediately\n const unsubs = [\n api.addEventListener(\n events.TRANSITION_ERROR,\n (\n toState: State | undefined,\n fromState: State | undefined,\n err: RouterError,\n ) => {\n errorVersion++;\n source.updateSnapshot({\n error: err,\n toRoute: toState ?? null,\n /* v8 ignore next -- @preserve: fromState undefined only during start() error; unreachable via navigate() */\n fromRoute: fromState ?? null,\n version: errorVersion,\n });\n },\n ),\n api.addEventListener(events.TRANSITION_SUCCESS, () => {\n // Skip if no error — avoids unnecessary re-renders.\n // BaseSource.updateSnapshot() always notifies listeners (new object = new ref),\n // and useSyncExternalStore compares via Object.is().\n if (source.getSnapshot().error !== null) {\n source.updateSnapshot({\n error: null,\n toRoute: null,\n fromRoute: null,\n version: errorVersion,\n });\n }\n }),\n ];\n\n return source;\n}\n"]}
@@ -1 +1 @@
1
- {"inputs":{"src/BaseSource.ts":{"bytes":1857,"imports":[],"format":"esm"},"src/stabilizeState.ts":{"bytes":856,"imports":[],"format":"esm"},"src/createRouteSource.ts":{"bytes":1599,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"}],"format":"esm"},"src/computeSnapshot.ts":{"bytes":1184,"imports":[{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"}],"format":"esm"},"src/shouldUpdateCache.ts":{"bytes":610,"imports":[],"format":"esm"},"src/createRouteNodeSource.ts":{"bytes":2239,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/computeSnapshot.ts","kind":"import-statement","original":"./computeSnapshot.js"},{"path":"src/shouldUpdateCache.ts","kind":"import-statement","original":"./shouldUpdateCache.js"}],"format":"esm"},"src/createActiveRouteSource.ts":{"bytes":1461,"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"}],"format":"esm"},"src/createTransitionSource.ts":{"bytes":1731,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"}],"format":"esm"},"src/index.ts":{"bytes":407,"imports":[{"path":"src/createRouteSource.ts","kind":"import-statement","original":"./createRouteSource"},{"path":"src/createRouteNodeSource.ts","kind":"import-statement","original":"./createRouteNodeSource"},{"path":"src/createActiveRouteSource.ts","kind":"import-statement","original":"./createActiveRouteSource"},{"path":"src/createTransitionSource.ts","kind":"import-statement","original":"./createTransitionSource"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":17153},"dist/esm/index.mjs":{"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true}],"exports":["createActiveRouteSource","createRouteNodeSource","createRouteSource","createTransitionSource"],"entryPoint":"src/index.ts","inputs":{"src/BaseSource.ts":{"bytesInOutput":1375},"src/stabilizeState.ts":{"bytesInOutput":155},"src/createRouteSource.ts":{"bytesInOutput":966},"src/index.ts":{"bytesInOutput":0},"src/computeSnapshot.ts":{"bytesInOutput":860},"src/shouldUpdateCache.ts":{"bytesInOutput":425},"src/createRouteNodeSource.ts":{"bytesInOutput":1251},"src/createActiveRouteSource.ts":{"bytesInOutput":977},"src/createTransitionSource.ts":{"bytesInOutput":1279}},"bytes":7758}}}
1
+ {"inputs":{"src/BaseSource.ts":{"bytes":1857,"imports":[],"format":"esm"},"src/stabilizeState.ts":{"bytes":856,"imports":[],"format":"esm"},"src/createRouteSource.ts":{"bytes":1599,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"}],"format":"esm"},"src/computeSnapshot.ts":{"bytes":1184,"imports":[{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"}],"format":"esm"},"src/shouldUpdateCache.ts":{"bytes":610,"imports":[],"format":"esm"},"src/createRouteNodeSource.ts":{"bytes":2239,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/computeSnapshot.ts","kind":"import-statement","original":"./computeSnapshot.js"},{"path":"src/shouldUpdateCache.ts","kind":"import-statement","original":"./shouldUpdateCache.js"}],"format":"esm"},"src/createActiveRouteSource.ts":{"bytes":1461,"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"}],"format":"esm"},"src/createTransitionSource.ts":{"bytes":1731,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"}],"format":"esm"},"src/createErrorSource.ts":{"bytes":1806,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"}],"format":"esm"},"src/index.ts":{"bytes":488,"imports":[{"path":"src/createRouteSource.ts","kind":"import-statement","original":"./createRouteSource"},{"path":"src/createRouteNodeSource.ts","kind":"import-statement","original":"./createRouteNodeSource"},{"path":"src/createActiveRouteSource.ts","kind":"import-statement","original":"./createActiveRouteSource"},{"path":"src/createTransitionSource.ts","kind":"import-statement","original":"./createTransitionSource"},{"path":"src/createErrorSource.ts","kind":"import-statement","original":"./createErrorSource"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":19881},"dist/esm/index.mjs":{"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true}],"exports":["createActiveRouteSource","createErrorSource","createRouteNodeSource","createRouteSource","createTransitionSource"],"entryPoint":"src/index.ts","inputs":{"src/BaseSource.ts":{"bytesInOutput":1375},"src/stabilizeState.ts":{"bytesInOutput":155},"src/createRouteSource.ts":{"bytesInOutput":966},"src/index.ts":{"bytesInOutput":0},"src/computeSnapshot.ts":{"bytesInOutput":860},"src/shouldUpdateCache.ts":{"bytesInOutput":425},"src/createRouteNodeSource.ts":{"bytesInOutput":1251},"src/createActiveRouteSource.ts":{"bytesInOutput":977},"src/createTransitionSource.ts":{"bytesInOutput":1279},"src/createErrorSource.ts":{"bytesInOutput":1210}},"bytes":9018}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/sources",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "type": "commonjs",
5
5
  "description": "Framework-agnostic subscription layer for Real-Router state",
6
6
  "main": "./dist/cjs/index.js",
@@ -0,0 +1,66 @@
1
+ import { events } from "@real-router/core";
2
+ import { getPluginApi } from "@real-router/core/api";
3
+
4
+ import { BaseSource } from "./BaseSource";
5
+
6
+ import type { RouterErrorSnapshot, RouterSource } from "./types.js";
7
+ import type { Router, State, RouterError } from "@real-router/core";
8
+
9
+ const INITIAL_SNAPSHOT: RouterErrorSnapshot = {
10
+ error: null,
11
+ toRoute: null,
12
+ fromRoute: null,
13
+ version: 0,
14
+ };
15
+
16
+ export function createErrorSource(
17
+ router: Router,
18
+ ): RouterSource<RouterErrorSnapshot> {
19
+ let errorVersion = 0;
20
+
21
+ const source = new BaseSource(INITIAL_SNAPSHOT, {
22
+ onDestroy: () => {
23
+ unsubs.forEach((unsub) => {
24
+ unsub();
25
+ });
26
+ },
27
+ });
28
+
29
+ const api = getPluginApi(router);
30
+
31
+ // Eager connection: subscribe to router events immediately
32
+ const unsubs = [
33
+ api.addEventListener(
34
+ events.TRANSITION_ERROR,
35
+ (
36
+ toState: State | undefined,
37
+ fromState: State | undefined,
38
+ err: RouterError,
39
+ ) => {
40
+ errorVersion++;
41
+ source.updateSnapshot({
42
+ error: err,
43
+ toRoute: toState ?? null,
44
+ /* v8 ignore next -- @preserve: fromState undefined only during start() error; unreachable via navigate() */
45
+ fromRoute: fromState ?? null,
46
+ version: errorVersion,
47
+ });
48
+ },
49
+ ),
50
+ api.addEventListener(events.TRANSITION_SUCCESS, () => {
51
+ // Skip if no error — avoids unnecessary re-renders.
52
+ // BaseSource.updateSnapshot() always notifies listeners (new object = new ref),
53
+ // and useSyncExternalStore compares via Object.is().
54
+ if (source.getSnapshot().error !== null) {
55
+ source.updateSnapshot({
56
+ error: null,
57
+ toRoute: null,
58
+ fromRoute: null,
59
+ version: errorVersion,
60
+ });
61
+ }
62
+ }),
63
+ ];
64
+
65
+ return source;
66
+ }
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export type {
4
4
  RouteNodeSnapshot,
5
5
  ActiveRouteSourceOptions,
6
6
  RouterTransitionSnapshot,
7
+ RouterErrorSnapshot,
7
8
  } from "./types.js";
8
9
 
9
10
  export { createRouteSource } from "./createRouteSource";
@@ -13,3 +14,5 @@ export { createRouteNodeSource } from "./createRouteNodeSource";
13
14
  export { createActiveRouteSource } from "./createActiveRouteSource";
14
15
 
15
16
  export { createTransitionSource } from "./createTransitionSource";
17
+
18
+ export { createErrorSource } from "./createErrorSource";
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { State } from "@real-router/core";
1
+ import type { RouterError, State } from "@real-router/core";
2
2
 
3
3
  export interface RouteSnapshot {
4
4
  route: State | undefined;
@@ -26,3 +26,10 @@ export interface RouterTransitionSnapshot {
26
26
  toRoute: State | null;
27
27
  fromRoute: State | null;
28
28
  }
29
+
30
+ export interface RouterErrorSnapshot {
31
+ error: RouterError | null;
32
+ toRoute: State | null;
33
+ fromRoute: State | null;
34
+ version: number;
35
+ }