@real-router/sources 0.5.1 → 0.6.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,13 +38,19 @@ 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 |
47
- | `createErrorSource(router)` | `{ error, toRoute, fromRoute, version }` | Navigation error (guard rejection, route not found) |
41
+ | Factory | Snapshot | Cache |
42
+ | ------------------------------------------------------- | -------------------------------------------------- | --------------------------------------------- |
43
+ | `createRouteSource(router)` | `{ route, previousRoute }` | not cached |
44
+ | `createRouteNodeSource(router, node)` | `{ route, previousRoute }` | per-router + per-nodeName |
45
+ | `createActiveRouteSource(router, name, params?, opts?)` | `boolean` | per-router + canonical-args |
46
+ | `createTransitionSource(router)` | `{ isTransitioning, toRoute, fromRoute }` | not cached (advanced) |
47
+ | `getTransitionSource(router)` | same as above | **per-router** recommended for integrations |
48
+ | `createErrorSource(router)` | `{ error, toRoute, fromRoute, version }` | not cached (advanced) |
49
+ | `getErrorSource(router)` | same as above | **per-router** — recommended for integrations |
50
+ | `createDismissableError(router)` | `{ error, toRoute, fromRoute, version, resetError }` | **per-router** — dismissal-aware error source for RouterErrorBoundary-style UIs |
51
+ | `createActiveNameSelector(router)` | `{ subscribe(name, listener), isActive(name), destroy }` | **per-router** — O(1) active-name checker for Link fast-path |
52
+
53
+ Plus utilities: `DEFAULT_ACTIVE_OPTIONS`, `normalizeActiveOptions(opts?)`, `canonicalJson(value)`.
48
54
 
49
55
  All factories return a `RouterSource<T>`:
50
56
 
@@ -52,15 +58,21 @@ All factories return a `RouterSource<T>`:
52
58
  interface RouterSource<T> {
53
59
  subscribe(listener: () => void): () => void; // useSyncExternalStore-compatible
54
60
  getSnapshot(): T; // current value, synchronous
55
- destroy(): void; // teardown, remove router subscription
61
+ destroy(): void; // no-op for cached wrappers; real teardown for create*
56
62
  }
57
63
  ```
58
64
 
65
+ ### Cached vs non-cached factories
66
+
67
+ Cached factories (`createRouteNodeSource`, `createActiveRouteSource`, `getTransitionSource`, `getErrorSource`) share a single source across all consumers of the same router. Multiple `subscribe`/`unsubscribe` pairs on the same instance share one router subscription. `destroy()` on the returned wrapper is a **no-op** — the underlying source lives as long as the router (the `WeakMap` entry releases on router GC).
68
+
69
+ Non-cached factories (`createRouteSource`, `createTransitionSource`, `createErrorSource`) return a fresh instance every call with real teardown on `destroy()` — use when you need an isolated source.
70
+
59
71
  ### Lazy vs Eager Subscription
60
72
 
61
73
  - `createRouteSource`, `createRouteNodeSource`, `createActiveRouteSource` — **lazy**: subscribe to the router on first listener, unsubscribe when all removed
62
- - `createTransitionSource` — **eager**: subscribes immediately (needs to track `TRANSITION_START`)
63
- - `createErrorSource` — **eager**: subscribes immediately (needs to track `TRANSITION_ERROR`)
74
+ - `createTransitionSource` / `getTransitionSource` — **eager**: subscribes immediately (needs to track `TRANSITION_START`)
75
+ - `createErrorSource` / `getErrorSource` — **eager**: subscribes immediately (needs to track `TRANSITION_ERROR`)
64
76
 
65
77
  ### `createActiveRouteSource` Options
66
78
 
@@ -71,6 +83,8 @@ const source = createActiveRouteSource(router, "users", undefined, {
71
83
  });
72
84
  ```
73
85
 
86
+ Params are hashed with `canonicalJson()`, so `{a: 1, b: 2}` and `{b: 2, a: 1}` hit the same cache entry. `BigInt`/`Symbol`/circular refs fall back to a fresh non-cached source.
87
+
74
88
  ## Usage Examples
75
89
 
76
90
  ### With React (`useSyncExternalStore`)
@@ -106,9 +120,11 @@ unsubscribe(); // automatically unsubscribes from router
106
120
  ### Transition Tracking
107
121
 
108
122
  ```typescript
109
- import { createTransitionSource } from "@real-router/sources";
123
+ import { getTransitionSource } from "@real-router/sources";
110
124
 
111
- const source = createTransitionSource(router);
125
+ // getTransitionSource per-router cached. Safe to call destroy() multiple
126
+ // times; shared across all consumers in the same process.
127
+ const source = getTransitionSource(router);
112
128
 
113
129
  source.subscribe(() => {
114
130
  const { isTransitioning, toRoute, fromRoute } = source.getSnapshot();
@@ -123,9 +139,9 @@ source.subscribe(() => {
123
139
  ### Error Tracking
124
140
 
125
141
  ```typescript
126
- import { createErrorSource } from "@real-router/sources";
142
+ import { getErrorSource } from "@real-router/sources";
127
143
 
128
- const source = createErrorSource(router);
144
+ const source = getErrorSource(router);
129
145
 
130
146
  source.subscribe(() => {
131
147
  const { error, toRoute } = source.getSnapshot();
@@ -30,6 +30,18 @@ interface RouterErrorSnapshot {
30
30
  fromRoute: State | null;
31
31
  version: number;
32
32
  }
33
+ interface DismissableErrorSnapshot {
34
+ /** Currently visible error, or `null` if none (never seen or dismissed). */
35
+ error: RouterError | null;
36
+ /** Target route of the failed navigation. */
37
+ toRoute: State | null;
38
+ /** Source route at the time of failure. */
39
+ fromRoute: State | null;
40
+ /** Monotonic version counter from the underlying error source. */
41
+ version: number;
42
+ /** Dismisses the current error. Next error (new version) becomes visible again. */
43
+ resetError: () => void;
44
+ }
33
45
  //#endregion
34
46
  //#region src/createRouteSource.d.ts
35
47
  /**
@@ -45,20 +57,174 @@ declare function createRouteSource(router: Router): RouterSource<RouteSnapshot>;
45
57
  /**
46
58
  * Creates a source scoped to a specific route node.
47
59
  *
60
+ * **Per-router + per-nodeName cache:** repeated calls with the same
61
+ * `(router, nodeName)` return the same shared instance. `N` consumers
62
+ * calling `createRouteNodeSource(r, "users")` produce one router subscription
63
+ * shared across all of them.
64
+ *
48
65
  * Uses a lazy-connection pattern: the router subscription is created when the
49
66
  * first listener subscribes and removed when the last listener unsubscribes.
50
67
  * This is compatible with React's useSyncExternalStore and Strict Mode.
68
+ *
69
+ * `destroy()` on the returned source is a no-op — the shared instance lives
70
+ * as long as the router itself (the WeakMap entry releases automatically on
71
+ * router GC). Callers that need an isolated instance with working teardown
72
+ * can use `buildRouteNodeSource` internally (not exported).
51
73
  */
52
74
  declare function createRouteNodeSource(router: Router, nodeName: string): RouterSource<RouteNodeSnapshot>;
53
75
  //#endregion
54
76
  //#region src/createActiveRouteSource.d.ts
77
+ /**
78
+ * Creates a source tracking whether a route (with given params/options) is active.
79
+ *
80
+ * **Per-router + canonical-args cache:** repeated calls with equivalent
81
+ * arguments return the same shared instance. Param key order doesn't matter
82
+ * (`{ a:1, b:2 }` and `{ b:2, a:1 }` hit the same cache entry via
83
+ * `canonicalJson`).
84
+ *
85
+ * `destroy()` is a no-op — shared sources live with the router. The router
86
+ * subscription stays active while any consumer subscribes; when the router
87
+ * is garbage-collected, the WeakMap entry releases automatically.
88
+ *
89
+ * Edge cases: `Symbol`/`BigInt` in params bypass `canonicalJson` and produce
90
+ * an unstable cache key — these will simply miss the cache and create a new
91
+ * source on each call. Practical params are primitives, so this is not a
92
+ * concern in real usage.
93
+ */
55
94
  declare function createActiveRouteSource(router: Router, routeName: string, params?: Params, options?: ActiveRouteSourceOptions): RouterSource<boolean>;
56
95
  //#endregion
57
96
  //#region src/createTransitionSource.d.ts
58
97
  declare function createTransitionSource(router: Router): RouterSource<RouterTransitionSnapshot>;
98
+ /**
99
+ * Returns a per-router cached transition source shared across all consumers.
100
+ *
101
+ * Safe to call destroy() — the cached source ignores external destroy() calls
102
+ * and lives until the router itself is garbage-collected (the WeakMap entry
103
+ * releases automatically).
104
+ *
105
+ * Use this in framework adapters (React/Preact/Solid/Vue/Svelte/Angular) to
106
+ * share a single TransitionSource instance across all mount/unmount cycles.
107
+ *
108
+ * For isolated/advanced use (ad-hoc, short-lived, per-owner teardown), call
109
+ * `createTransitionSource(router)` directly — it returns a fresh instance with
110
+ * a working `destroy()`.
111
+ */
112
+ declare function getTransitionSource(router: Router): RouterSource<RouterTransitionSnapshot>;
59
113
  //#endregion
60
114
  //#region src/createErrorSource.d.ts
61
115
  declare function createErrorSource(router: Router): RouterSource<RouterErrorSnapshot>;
116
+ /**
117
+ * Returns a per-router cached error source shared across all consumers.
118
+ *
119
+ * Safe to call destroy() — the cached source ignores external destroy() calls
120
+ * and lives until the router itself is garbage-collected (the WeakMap entry
121
+ * releases automatically).
122
+ *
123
+ * Use this in framework adapters (React/Preact/Solid/Vue/Svelte/Angular) to
124
+ * share a single ErrorSource instance across all mount/unmount cycles.
125
+ *
126
+ * For isolated/advanced use (ad-hoc, short-lived, per-owner teardown), call
127
+ * `createErrorSource(router)` directly — it returns a fresh instance with a
128
+ * working `destroy()`.
129
+ */
130
+ declare function getErrorSource(router: Router): RouterSource<RouterErrorSnapshot>;
131
+ //#endregion
132
+ //#region src/createDismissableError.d.ts
133
+ /**
134
+ * Returns a per-router cached source that wraps `getErrorSource(router)` with
135
+ * an integrated "dismissed" version counter, exposing a single reactive
136
+ * snapshot `{ error, toRoute, fromRoute, version, resetError }`.
137
+ *
138
+ * Each `RouterErrorBoundary` in a framework adapter subscribes to this one
139
+ * source instead of re-implementing the `dismissedVersion` state pattern
140
+ * locally. The 6-copy duplicate across adapters collapses to this helper.
141
+ *
142
+ * **Semantics:**
143
+ * - `error` is non-null only when `underlying.version > dismissedVersion`.
144
+ * - `resetError()` sets `dismissedVersion = current underlying version`,
145
+ * immediately clearing `error` to `null` and notifying all listeners.
146
+ * - A subsequent `TRANSITION_ERROR` advances `version` beyond `dismissedVersion`,
147
+ * so `error` becomes non-null again — no additional plumbing needed.
148
+ *
149
+ * **Cached:** one instance per router. `destroy()` on the returned source is
150
+ * a no-op. Shared across all `RouterErrorBoundary` consumers.
151
+ */
152
+ declare function createDismissableError(router: Router): RouterSource<DismissableErrorSnapshot>;
153
+ //#endregion
154
+ //#region src/createActiveNameSelector.d.ts
155
+ interface ActiveNameSelector {
156
+ /**
157
+ * Subscribes to active-state changes of a specific route name.
158
+ * Listener is called only when `isActive(routeName)` for this name transitions.
159
+ * Returns an unsubscribe function.
160
+ */
161
+ subscribe: (routeName: string, listener: () => void) => () => void;
162
+ /**
163
+ * O(1) active check for the given route name (non-strict by default —
164
+ * matches descendants). Uses the shared underlying router subscription.
165
+ */
166
+ isActive: (routeName: string) => boolean;
167
+ /** No-op on the cached wrapper. */
168
+ destroy: () => void;
169
+ }
170
+ /**
171
+ * Per-router cached selector providing O(1) active-route checks with one
172
+ * shared router subscription for any number of distinct `routeName`
173
+ * consumers.
174
+ *
175
+ * **When to use:** framework `Link` components that need an active boolean
176
+ * without custom `params` / `activeStrict` / `ignoreQueryParams` — e.g. the
177
+ * common navigation-link case. Multiple `<Link>` components with different
178
+ * `routeName` share ONE `router.subscribe` handle instead of creating one
179
+ * per Link (which is what `createActiveRouteSource(router, name)` does — it
180
+ * caches per-name, so N names = N subscriptions).
181
+ *
182
+ * **When NOT to use:** Link needs `activeStrict: true`, custom `routeParams`,
183
+ * or `ignoreQueryParams: false`. Fall back to `createActiveRouteSource` —
184
+ * its cache handles the full argument surface.
185
+ *
186
+ * Based on the `routeSelector` pattern pioneered by `@real-router/solid`'s
187
+ * `RouterProvider` (`createSelector` + `areRoutesRelated`). This helper
188
+ * ports it to framework-agnostic API so Vue / React / Preact / Svelte /
189
+ * Angular Link components can adopt the same fast path.
190
+ *
191
+ * @see Solid reference implementation — `packages/solid/src/RouterProvider.tsx`
192
+ */
193
+ declare function createActiveNameSelector(router: Router): ActiveNameSelector;
194
+ //#endregion
195
+ //#region src/normalizeActiveOptions.d.ts
196
+ /**
197
+ * Default options for `createActiveRouteSource` and adapter-level helpers.
198
+ *
199
+ * Frozen to prevent accidental mutation by consumers.
200
+ */
201
+ declare const DEFAULT_ACTIVE_OPTIONS: Readonly<Required<ActiveRouteSourceOptions>>;
202
+ /**
203
+ * Normalizes partial `ActiveRouteSourceOptions` into a fully-defaulted object.
204
+ *
205
+ * Use this to produce a stable options record for comparison, caching, or
206
+ * downstream consumers that require all fields present.
207
+ */
208
+ declare function normalizeActiveOptions(options?: ActiveRouteSourceOptions): Required<ActiveRouteSourceOptions>;
209
+ //#endregion
210
+ //#region src/canonicalJson.d.ts
211
+ /**
212
+ * Serializes a value into a stable JSON string — object keys are sorted at
213
+ * every level so that `{ a: 1, b: 2 }` and `{ b: 2, a: 1 }` produce the same
214
+ * output.
215
+ *
216
+ * Used as a cache key for `createActiveRouteSource` so that equivalent params
217
+ * objects share the same cached source regardless of key order.
218
+ *
219
+ * Edge cases:
220
+ * - Arrays preserve order (canonical: index-ordered already).
221
+ * - `undefined` values are dropped (standard JSON behaviour).
222
+ * - `Symbol`, `BigInt`, `Date`, `Map`, `Set` etc. fall through to
223
+ * `JSON.stringify` defaults — `Symbol` becomes `undefined`, `BigInt` throws.
224
+ * In practice, route params carry primitives (`string | number | boolean`)
225
+ * and such edge cases would hit a fresh source on cache miss — acceptable.
226
+ */
227
+ declare function canonicalJson(value: unknown): string;
62
228
  //#endregion
63
- export { type ActiveRouteSourceOptions, type RouteNodeSnapshot, type RouteSnapshot, type RouterErrorSnapshot, type RouterSource, type RouterTransitionSnapshot, createActiveRouteSource, createErrorSource, createRouteNodeSource, createRouteSource, createTransitionSource };
229
+ export { type ActiveNameSelector, type ActiveRouteSourceOptions, DEFAULT_ACTIVE_OPTIONS, type DismissableErrorSnapshot, type RouteNodeSnapshot, type RouteSnapshot, type RouterErrorSnapshot, type RouterSource, type RouterTransitionSnapshot, canonicalJson, createActiveNameSelector, createActiveRouteSource, createDismissableError, createErrorSource, createRouteNodeSource, createRouteSource, createTransitionSource, getErrorSource, getTransitionSource, normalizeActiveOptions };
64
230
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/types.ts","../../src/createRouteSource.ts","../../src/createRouteNodeSource.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts","../../src/createErrorSource.ts"],"mappings":";;;UAEiB,aAAA;EACf,KAAA,EAAO,KAAA;EACP,aAAA,EAAe,KAAA;AAAA;AAAA,UAGA,iBAAA;EACf,KAAA,EAAO,KAAA;EACP,aAAA,EAAe,KAAA;AAAA;AAAA,UAGA,YAAA;EACf,SAAA,GAAY,QAAA;EACZ,WAAA,QAAmB,CAAA;EACnB,OAAA;AAAA;AAAA,UAGe,wBAAA;EACf,MAAA;EACA,iBAAA;AAAA;AAAA,UAGe,wBAAA;EACf,eAAA;EACA,eAAA;EACA,OAAA,EAAS,KAAA;EACT,SAAA,EAAW,KAAA;AAAA;AAAA,UAGI,mBAAA;EACf,KAAA,EAAO,WAAA;EACP,OAAA,EAAS,KAAA;EACT,SAAA,EAAW,KAAA;EACX,OAAA;AAAA;;;;AAhCF;;;;;;iBCWgB,iBAAA,CAAkB,MAAA,EAAQ,MAAA,GAAS,YAAA,CAAa,aAAA;;;;ADXhE;;;;;;iBEYgB,qBAAA,CACd,MAAA,EAAQ,MAAA,EACR,QAAA,WACC,YAAA,CAAa,iBAAA;;;iBCVA,uBAAA,CACd,MAAA,EAAQ,MAAA,EACR,SAAA,UACA,MAAA,GAAS,MAAA,EACT,OAAA,GAAU,wBAAA,GACT,YAAA;;;iBCIa,sBAAA,CACd,MAAA,EAAQ,MAAA,GACP,YAAA,CAAa,wBAAA;;;iBCHA,iBAAA,CACd,MAAA,EAAQ,MAAA,GACP,YAAA,CAAa,mBAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/types.ts","../../src/createRouteSource.ts","../../src/createRouteNodeSource.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts","../../src/createErrorSource.ts","../../src/createDismissableError.ts","../../src/createActiveNameSelector.ts","../../src/normalizeActiveOptions.ts","../../src/canonicalJson.ts"],"mappings":";;;UAEiB,aAAA;EACf,KAAA,EAAO,KAAA;EACP,aAAA,EAAe,KAAA;AAAA;AAAA,UAGA,iBAAA;EACf,KAAA,EAAO,KAAA;EACP,aAAA,EAAe,KAAA;AAAA;AAAA,UAGA,YAAA;EACf,SAAA,GAAY,QAAA;EACZ,WAAA,QAAmB,CAAA;EACnB,OAAA;AAAA;AAAA,UAGe,wBAAA;EACf,MAAA;EACA,iBAAA;AAAA;AAAA,UAGe,wBAAA;EACf,eAAA;EACA,eAAA;EACA,OAAA,EAAS,KAAA;EACT,SAAA,EAAW,KAAA;AAAA;AAAA,UAGI,mBAAA;EACf,KAAA,EAAO,WAAA;EACP,OAAA,EAAS,KAAA;EACT,SAAA,EAAW,KAAA;EACX,OAAA;AAAA;AAAA,UAGe,wBAAA;EAvBI;EAyBnB,KAAA,EAAO,WAAA;EAxBA;EA0BP,OAAA,EAAS,KAAA;EAvBM;EAyBf,SAAA,EAAW,KAAA;;EAEX,OAAA;EAzBiB;EA2BjB,UAAA;AAAA;;;;AA7CF;;;;;;iBCWgB,iBAAA,CAAkB,MAAA,EAAQ,MAAA,GAAS,YAAA,CAAa,aAAA;;;;ADXhE;;;;;;;;;;AAKA;;;;;;iBEqBgB,qBAAA,CACd,MAAA,EAAQ,MAAA,EACR,QAAA,WACC,YAAA,CAAa,iBAAA;;;;AF7BhB;;;;;;;;;;AAKA;;;;;;iBGwBgB,uBAAA,CACd,MAAA,EAAQ,MAAA,EACR,SAAA,UACA,MAAA,GAAS,MAAA,EACT,OAAA,GAAU,wBAAA,GACT,YAAA;;;iBCfa,sBAAA,CACd,MAAA,EAAQ,MAAA,GACP,YAAA,CAAa,wBAAA;AJrBhB;;;;;;;;;;AAKA;;;;AALA,iBI8FgB,mBAAA,CACd,MAAA,EAAQ,MAAA,GACP,YAAA,CAAa,wBAAA;;;iBC9EA,iBAAA,CACd,MAAA,EAAQ,MAAA,GACP,YAAA,CAAa,mBAAA;ALpBhB;;;;;;;;;;AAKA;;;;AALA,iBKoFgB,cAAA,CACd,MAAA,EAAQ,MAAA,GACP,YAAA,CAAa,mBAAA;;;;ALtFhB;;;;;;;;;;AAKA;;;;;;;;iBMuBgB,sBAAA,CACd,MAAA,EAAQ,MAAA,GACP,YAAA,CAAa,wBAAA;;;UC5BC,kBAAA;;APFjB;;;;EOQE,SAAA,GAAY,SAAA,UAAmB,QAAA;EPPxB;;;;EOYP,QAAA,GAAW,SAAA;EPRI;EOUf,OAAA;AAAA;;;;;;;;APLF;;;;;;;;;;;;AAMA;;;;iBO2BgB,wBAAA,CAAyB,MAAA,EAAQ,MAAA,GAAS,kBAAA;;;;;AP3C1D;;;cQKa,sBAAA,EAAwB,QAAA,CACnC,QAAA,CAAS,wBAAA;;;;;;;iBAYK,sBAAA,CACd,OAAA,GAAU,wBAAA,GACT,QAAA,CAAS,wBAAA;;;;;;ARpBZ;;;;;;;;;;AAKA;;;iBSSgB,aAAA,CAAc,KAAA"}
package/dist/cjs/index.js CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@real-router/route-utils`),t=require(`@real-router/core`),n=require(`@real-router/core/api`);var r=class{#e;#t=!1;#n=new Set;#r;#i;#a;constructor(e,t){this.#e=e,this.#r=t?.onFirstSubscribe,this.#i=t?.onLastUnsubscribe,this.#a=t?.onDestroy,this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(e){return this.#t?()=>{}:(this.#n.size===0&&this.#r&&this.#r(),this.#n.add(e),()=>{this.#n.delete(e),!this.#t&&this.#n.size===0&&this.#i&&this.#i()})}getSnapshot(){return this.#e}updateSnapshot(e){this.#t||(this.#e=e,this.#n.forEach(e=>{e()}))}destroy(){this.#t||(this.#t=!0,this.#a?.(),this.#n.clear())}};function i(e,t){return e===t||e?.path===t?.path?e:t}function a(e){let t=null,n=()=>{let e=t;t=null,e?.()},a=new r({route:e.getState(),previousRoute:void 0},{onFirstSubscribe:()=>{t=e.subscribe(e=>{let t=a.getSnapshot(),n=i(t.route,e.route),r=i(t.previousRoute,e.previousRoute);(n!==t.route||r!==t.previousRoute)&&a.updateSnapshot({route:n,previousRoute:r})})},onLastUnsubscribe:n,onDestroy:n});return a}function o(e,t,n,r){let a=r?.route??t.getState(),o=r?.previousRoute,s=n===``||a!==void 0&&(a.name===n||a.name.startsWith(`${n}.`))?a:void 0;if(s===e.route&&o===e.previousRoute)return e;let c=i(e.route,s),l=i(e.previousRoute,o);return c===e.route&&l===e.previousRoute?e:{route:c,previousRoute:l}}const s=new WeakMap;function c(e,t){let n=s.get(e);n||(n=new Map,s.set(e,n));let r=n.get(t);return r||(r=e.shouldUpdateNode(t),n.set(t,r)),r}function l(e,t){let n=null,i=c(e,t),a={route:void 0,previousRoute:void 0},s=()=>{let e=n;n=null,e?.()},l=new r(o(a,e,t),{onFirstSubscribe:()=>{let r=o(l.getSnapshot(),e,t);Object.is(r,l.getSnapshot())||l.updateSnapshot(r),n=e.subscribe(n=>{if(!i(n.route,n.previousRoute))return;let r=o(l.getSnapshot(),e,t,n);Object.is(l.getSnapshot(),r)||l.updateSnapshot(r)})},onLastUnsubscribe:s,onDestroy:s});return l}function u(t,n,i,a){let o=a?.strict??!1,s=a?.ignoreQueryParams??!0,c=new r(t.isActiveRoute(n,i,o,s),{onDestroy:()=>{l()}}),l=t.subscribe(r=>{let a=(0,e.areRoutesRelated)(n,r.route.name),l=r.previousRoute&&(0,e.areRoutesRelated)(n,r.previousRoute.name);if(!a&&!l)return;let u=a?t.isActiveRoute(n,i,o,s):!1;Object.is(c.getSnapshot(),u)||c.updateSnapshot(u)});return c}const d={isTransitioning:!1,isLeaveApproved:!1,toRoute:null,fromRoute:null};function f(e){let a=new r(d,{onDestroy:()=>{c.forEach(e=>{e()})}}),o=(0,n.getPluginApi)(e),s=()=>{a.updateSnapshot(d)},c=[o.addEventListener(t.events.TRANSITION_START,(e,t)=>{let n=a.getSnapshot(),r=i(n.toRoute,e),o=i(n.fromRoute,t??null);(!n.isTransitioning||r!==n.toRoute||o!==n.fromRoute)&&a.updateSnapshot({isTransitioning:!0,isLeaveApproved:!1,toRoute:r,fromRoute:o})}),o.addEventListener(t.events.TRANSITION_LEAVE_APPROVE,(e,t)=>{let n=a.getSnapshot();a.updateSnapshot({isTransitioning:!0,isLeaveApproved:!0,toRoute:i(n.toRoute,e),fromRoute:i(n.fromRoute,t??null)})}),o.addEventListener(t.events.TRANSITION_SUCCESS,s),o.addEventListener(t.events.TRANSITION_ERROR,s),o.addEventListener(t.events.TRANSITION_CANCEL,s)];return a}const p={error:null,toRoute:null,fromRoute:null,version:0};function m(e){let i=0,a=new r(p,{onDestroy:()=>{s.forEach(e=>{e()})}}),o=(0,n.getPluginApi)(e),s=[o.addEventListener(t.events.TRANSITION_ERROR,(e,t,n)=>{i++,a.updateSnapshot({error:n,toRoute:e??null,fromRoute:t??null,version:i})}),o.addEventListener(t.events.TRANSITION_SUCCESS,()=>{a.getSnapshot().error!==null&&a.updateSnapshot({error:null,toRoute:null,fromRoute:null,version:i})})];return a}exports.createActiveRouteSource=u,exports.createErrorSource=m,exports.createRouteNodeSource=l,exports.createRouteSource=a,exports.createTransitionSource=f;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@real-router/route-utils`),t=require(`@real-router/core`),n=require(`@real-router/core/api`);var r=class{#e;#t=!1;#n=new Set;#r;#i;#a;constructor(e,t){this.#e=e,this.#r=t?.onFirstSubscribe,this.#i=t?.onLastUnsubscribe,this.#a=t?.onDestroy,this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(e){if(this.#t)return()=>{};let t=this.#n.size===0;return this.#n.add(e),t&&this.#r&&this.#r(),()=>{this.#n.delete(e),!this.#t&&this.#n.size===0&&this.#i&&this.#i()}}getSnapshot(){return this.#e}updateSnapshot(e){this.#t||(this.#e=e,this.#n.forEach(e=>{e()}))}destroy(){this.#t||(this.#t=!0,this.#a?.(),this.#n.clear())}};function i(e,t){return e===t||e?.path===t?.path?e:t}function a(e){let t=null,n=()=>{let e=t;t=null,e?.()},a=new r({route:e.getState(),previousRoute:void 0},{onFirstSubscribe:()=>{t=e.subscribe(e=>{let t=a.getSnapshot(),n=i(t.route,e.route),r=i(t.previousRoute,e.previousRoute);(n!==t.route||r!==t.previousRoute)&&a.updateSnapshot({route:n,previousRoute:r})})},onLastUnsubscribe:n,onDestroy:n});return a}function o(e,t,n,r){let a=r?.route??t.getState(),o=r?.previousRoute,s=n===``||a!==void 0&&(a.name===n||a.name.startsWith(`${n}.`))?a:void 0;if(s===e.route&&o===e.previousRoute)return e;let c=i(e.route,s),l=i(e.previousRoute,o);return c===e.route&&l===e.previousRoute?e:{route:c,previousRoute:l}}const s=new WeakMap;function c(e,t){let n=s.get(e);n||(n=new Map,s.set(e,n));let r=n.get(t);if(!r){let i=l(e,t);r={subscribe:i.subscribe,getSnapshot:i.getSnapshot,destroy:u},n.set(t,r)}return r}function l(e,t){let n=null,i=e.shouldUpdateNode(t),a=new r(o({route:void 0,previousRoute:void 0},e,t),{onFirstSubscribe:()=>{let r=o(a.getSnapshot(),e,t);Object.is(r,a.getSnapshot())||a.updateSnapshot(r),n=e.subscribe(n=>{if(!i(n.route,n.previousRoute))return;let r=o(a.getSnapshot(),e,t,n);Object.is(a.getSnapshot(),r)||a.updateSnapshot(r)})},onLastUnsubscribe:()=>{let e=n;n=null,e?.()}});return a}function u(){}function d(e){return JSON.stringify(e,f)}function f(e,t){if(typeof t==`object`&&t&&!Array.isArray(t)){let e={},n=Object.keys(t).toSorted((e,t)=>e.localeCompare(t));for(let r of n)e[r]=t[r];return e}return t}const p=Object.freeze({strict:!1,ignoreQueryParams:!0});function m(e){return{strict:e?.strict??p.strict,ignoreQueryParams:e?.ignoreQueryParams??p.ignoreQueryParams}}const h=new WeakMap;function g(e,t,n,r){let{strict:i,ignoreQueryParams:a}=m(r),o;try{o=`${t}|${d(n)}|${String(i)}|${String(a)}`}catch{o=void 0}if(o===void 0){let r=_(e,t,n,i,a);return{subscribe:r.subscribe,getSnapshot:r.getSnapshot,destroy:v}}let s=h.get(e);s||(s=new Map,h.set(e,s));let c=s.get(o);if(!c){let r=_(e,t,n,i,a);c={subscribe:r.subscribe,getSnapshot:r.getSnapshot,destroy:v},s.set(o,c)}return c}function _(t,n,i,a,o){let s=new r(t.isActiveRoute(n,i,a,o));return t.subscribe(r=>{let c=(0,e.areRoutesRelated)(n,r.route.name),l=r.previousRoute&&(0,e.areRoutesRelated)(n,r.previousRoute.name);if(!c&&!l)return;let u=c?t.isActiveRoute(n,i,a,o):!1;Object.is(s.getSnapshot(),u)||s.updateSnapshot(u)}),s}function v(){}const y={isTransitioning:!1,isLeaveApproved:!1,toRoute:null,fromRoute:null},b=new WeakMap;function x(e){let a=new r(y,{onDestroy:()=>{c.forEach(e=>{e()})}}),o=(0,n.getPluginApi)(e),s=()=>{a.updateSnapshot(y)},c=[o.addEventListener(t.events.TRANSITION_START,(e,t)=>{let n=a.getSnapshot(),r=i(n.toRoute,e),o=i(n.fromRoute,t??null);(!n.isTransitioning||r!==n.toRoute||o!==n.fromRoute)&&a.updateSnapshot({isTransitioning:!0,isLeaveApproved:!1,toRoute:r,fromRoute:o})}),o.addEventListener(t.events.TRANSITION_LEAVE_APPROVE,(e,t)=>{let n=a.getSnapshot();a.updateSnapshot({isTransitioning:!0,isLeaveApproved:!0,toRoute:i(n.toRoute,e),fromRoute:i(n.fromRoute,t??null)})}),o.addEventListener(t.events.TRANSITION_SUCCESS,s),o.addEventListener(t.events.TRANSITION_ERROR,s),o.addEventListener(t.events.TRANSITION_CANCEL,s)];return a}function S(e){let t=b.get(e);if(!t){let n=x(e);t={subscribe:n.subscribe,getSnapshot:n.getSnapshot,destroy:C},b.set(e,t)}return t}function C(){}const w={error:null,toRoute:null,fromRoute:null,version:0},T=new WeakMap;function E(e){let i=0,a=new r(w,{onDestroy:()=>{s.forEach(e=>{e()})}}),o=(0,n.getPluginApi)(e),s=[o.addEventListener(t.events.TRANSITION_ERROR,(e,t,n)=>{i++,a.updateSnapshot({error:n,toRoute:e??null,fromRoute:t??null,version:i})}),o.addEventListener(t.events.TRANSITION_SUCCESS,()=>{a.getSnapshot().error!==null&&a.updateSnapshot({error:null,toRoute:null,fromRoute:null,version:i})})];return a}function D(e){let t=T.get(e);if(!t){let n=E(e);t={subscribe:n.subscribe,getSnapshot:n.getSnapshot,destroy:O},T.set(e,t)}return t}function O(){}const k=new WeakMap;function A(e){let t=k.get(e);if(t)return t;let n=D(e),i=-1,a=()=>{let e=n.getSnapshot(),t=e.version<=i;return{error:t?null:e.error,toRoute:t?null:e.toRoute,fromRoute:t?null:e.fromRoute,version:e.version,resetError:c}},o=new r(a(),{onFirstSubscribe:()=>{s=n.subscribe(()=>{o.updateSnapshot(a())})},onLastUnsubscribe:()=>{l()}}),s=null;function c(){i=n.getSnapshot().version,o.updateSnapshot(a())}function l(){let e=s;s=null,e?.()}let u={subscribe:o.subscribe,getSnapshot:o.getSnapshot,destroy:j};return k.set(e,u),u}function j(){}const M=new WeakMap;function N(t){let n=M.get(t);if(n)return n;let r=new Map,i=new Map,a=null,o=e=>{let n=t.getState();return n?n.name===e||n.name.startsWith(`${e}.`):!1},s=()=>{a=t.subscribe(t=>{for(let[n,a]of r){let r=(0,e.areRoutesRelated)(n,t.route.name),s=t.previousRoute&&(0,e.areRoutesRelated)(n,t.previousRoute.name);if(!r&&!s)continue;let c=i.get(n)===!0,l=o(n);if(c!==l){i.set(n,l);for(let e of a)e()}}})},c=()=>{let e=a;a=null,e?.()},l={subscribe:(e,t)=>{let n=r.get(e);n||(n=new Set,r.set(e,n),i.set(e,o(e))),n.add(t),a||s();let l=!1;return()=>{l||(l=!0,n.delete(t),n.size===0&&(r.delete(e),i.delete(e)),r.size===0&&c())}},isActive:e=>{let t=i.get(e);return t===void 0?o(e):t},destroy:P};return M.set(t,l),l}function P(){}exports.DEFAULT_ACTIVE_OPTIONS=p,exports.canonicalJson=d,exports.createActiveNameSelector=N,exports.createActiveRouteSource=g,exports.createDismissableError=A,exports.createErrorSource=E,exports.createRouteNodeSource=c,exports.createRouteSource=a,exports.createTransitionSource=x,exports.getErrorSource=D,exports.getTransitionSource=S,exports.normalizeActiveOptions=m;
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["#listeners","#onFirstSubscribe","#onLastUnsubscribe","#onDestroy","#currentSnapshot","#destroyed","events","events"],"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"],"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 isLeaveApproved: 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 isLeaveApproved: false,\n toRoute: newToRoute,\n fromRoute: newFromRoute,\n });\n }\n },\n ),\n api.addEventListener(\n events.TRANSITION_LEAVE_APPROVE,\n (toState: State, fromState?: State) => {\n const prev = source.getSnapshot();\n\n source.updateSnapshot({\n isTransitioning: true,\n isLeaveApproved: true,\n toRoute: stabilizeState(prev.toRoute, toState),\n fromRoute: stabilizeState(prev.fromRoute, fromState ?? null),\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"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["#listeners","#onFirstSubscribe","#onLastUnsubscribe","#onDestroy","#currentSnapshot","#destroyed","noopDestroy","noopDestroy","events","noopDestroy","events","noopDestroy","noopDestroy"],"sources":["../../src/BaseSource.ts","../../src/stabilizeState.ts","../../src/createRouteSource.ts","../../src/computeSnapshot.ts","../../src/createRouteNodeSource.ts","../../src/canonicalJson.ts","../../src/normalizeActiveOptions.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts","../../src/createErrorSource.ts","../../src/createDismissableError.ts","../../src/createActiveNameSelector.ts"],"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 const wasFirst = this.#listeners.size === 0;\n\n // Add listener BEFORE onFirstSubscribe so that if the reconciliation in\n // onFirstSubscribe calls updateSnapshot(), this listener receives the\n // notification. Critical for useSyncExternalStore in adapters — without\n // this the post-reconnection snapshot is missed and consumers render\n // stale data. (See Preact RouteView nested remount test.)\n this.#listeners.add(listener);\n\n if (wasFirst && this.#onFirstSubscribe) {\n this.#onFirstSubscribe();\n }\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 { BaseSource } from \"./BaseSource\";\nimport { computeSnapshot } from \"./computeSnapshot.js\";\n\nimport type { RouteNodeSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\nconst nodeSourceCache = new WeakMap<\n Router,\n Map<string, RouterSource<RouteNodeSnapshot>>\n>();\n\n/**\n * Creates a source scoped to a specific route node.\n *\n * **Per-router + per-nodeName cache:** repeated calls with the same\n * `(router, nodeName)` return the same shared instance. `N` consumers\n * calling `createRouteNodeSource(r, \"users\")` produce one router subscription\n * shared across all of them.\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 *\n * `destroy()` on the returned source is a no-op — the shared instance lives\n * as long as the router itself (the WeakMap entry releases automatically on\n * router GC). Callers that need an isolated instance with working teardown\n * can use `buildRouteNodeSource` internally (not exported).\n */\nexport function createRouteNodeSource(\n router: Router,\n nodeName: string,\n): RouterSource<RouteNodeSnapshot> {\n let perRouter = nodeSourceCache.get(router);\n\n if (!perRouter) {\n perRouter = new Map();\n nodeSourceCache.set(router, perRouter);\n }\n\n let cached = perRouter.get(nodeName);\n\n if (!cached) {\n const source = buildRouteNodeSource(router, nodeName);\n\n // Wrap with no-op destroy. The shared source lives with the router.\n cached = {\n subscribe: source.subscribe,\n getSnapshot: source.getSnapshot,\n destroy: noopDestroy,\n };\n perRouter.set(nodeName, cached);\n }\n\n return cached;\n}\n\nfunction buildRouteNodeSource(\n router: Router,\n nodeName: string,\n): RouterSource<RouteNodeSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n // Built once per cached source instance; safe — createRouteNodeSource is\n // itself per-(router, nodeName) cached, so shouldUpdate is called once.\n const shouldUpdate = router.shouldUpdateNode(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 },\n );\n\n return source;\n}\n\nfunction noopDestroy(): void {\n // Shared cached source — external destroy() is a no-op.\n}\n","/**\n * Serializes a value into a stable JSON string — object keys are sorted at\n * every level so that `{ a: 1, b: 2 }` and `{ b: 2, a: 1 }` produce the same\n * output.\n *\n * Used as a cache key for `createActiveRouteSource` so that equivalent params\n * objects share the same cached source regardless of key order.\n *\n * Edge cases:\n * - Arrays preserve order (canonical: index-ordered already).\n * - `undefined` values are dropped (standard JSON behaviour).\n * - `Symbol`, `BigInt`, `Date`, `Map`, `Set` etc. fall through to\n * `JSON.stringify` defaults — `Symbol` becomes `undefined`, `BigInt` throws.\n * In practice, route params carry primitives (`string | number | boolean`)\n * and such edge cases would hit a fresh source on cache miss — acceptable.\n */\nexport function canonicalJson(value: unknown): string {\n return JSON.stringify(value, replacer);\n}\n\nfunction replacer(_key: string, val: unknown): unknown {\n if (val !== null && typeof val === \"object\" && !Array.isArray(val)) {\n const sorted: Record<string, unknown> = {};\n const keys = Object.keys(val as Record<string, unknown>).toSorted(\n (left, right) => left.localeCompare(right),\n );\n\n for (const key of keys) {\n sorted[key] = (val as Record<string, unknown>)[key];\n }\n\n return sorted;\n }\n\n return val;\n}\n","import type { ActiveRouteSourceOptions } from \"./types.js\";\n\n/**\n * Default options for `createActiveRouteSource` and adapter-level helpers.\n *\n * Frozen to prevent accidental mutation by consumers.\n */\nexport const DEFAULT_ACTIVE_OPTIONS: Readonly<\n Required<ActiveRouteSourceOptions>\n> = Object.freeze({\n strict: false,\n ignoreQueryParams: true,\n});\n\n/**\n * Normalizes partial `ActiveRouteSourceOptions` into a fully-defaulted object.\n *\n * Use this to produce a stable options record for comparison, caching, or\n * downstream consumers that require all fields present.\n */\nexport function normalizeActiveOptions(\n options?: ActiveRouteSourceOptions,\n): Required<ActiveRouteSourceOptions> {\n return {\n strict: options?.strict ?? DEFAULT_ACTIVE_OPTIONS.strict,\n ignoreQueryParams:\n options?.ignoreQueryParams ?? DEFAULT_ACTIVE_OPTIONS.ignoreQueryParams,\n };\n}\n","import { areRoutesRelated } from \"@real-router/route-utils\";\n\nimport { BaseSource } from \"./BaseSource\";\nimport { canonicalJson } from \"./canonicalJson.js\";\nimport { normalizeActiveOptions } from \"./normalizeActiveOptions.js\";\n\nimport type { ActiveRouteSourceOptions, RouterSource } from \"./types.js\";\nimport type { Params, Router } from \"@real-router/core\";\n\nconst activeSourceCache = new WeakMap<\n Router,\n Map<string, RouterSource<boolean>>\n>();\n\n/**\n * Creates a source tracking whether a route (with given params/options) is active.\n *\n * **Per-router + canonical-args cache:** repeated calls with equivalent\n * arguments return the same shared instance. Param key order doesn't matter\n * (`{ a:1, b:2 }` and `{ b:2, a:1 }` hit the same cache entry via\n * `canonicalJson`).\n *\n * `destroy()` is a no-op — shared sources live with the router. The router\n * subscription stays active while any consumer subscribes; when the router\n * is garbage-collected, the WeakMap entry releases automatically.\n *\n * Edge cases: `Symbol`/`BigInt` in params bypass `canonicalJson` and produce\n * an unstable cache key — these will simply miss the cache and create a new\n * source on each call. Practical params are primitives, so this is not a\n * concern in real usage.\n */\nexport function createActiveRouteSource(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n): RouterSource<boolean> {\n const { strict, ignoreQueryParams } = normalizeActiveOptions(options);\n\n // BigInt/Symbol/circular refs cannot be serialized — fall back to creating\n // a fresh (non-cached) source. Callers pass these edge-case params rarely;\n // the extra allocation is acceptable.\n let key: string | undefined;\n\n try {\n key = `${routeName}|${canonicalJson(params)}|${String(strict)}|${String(ignoreQueryParams)}`;\n } catch {\n key = undefined;\n }\n\n if (key === undefined) {\n const source = buildActiveRouteSource(\n router,\n routeName,\n params,\n strict,\n ignoreQueryParams,\n );\n\n return {\n subscribe: source.subscribe,\n getSnapshot: source.getSnapshot,\n destroy: noopDestroy,\n };\n }\n\n let perRouter = activeSourceCache.get(router);\n\n if (!perRouter) {\n perRouter = new Map();\n activeSourceCache.set(router, perRouter);\n }\n\n let cached = perRouter.get(key);\n\n if (!cached) {\n const source = buildActiveRouteSource(\n router,\n routeName,\n params,\n strict,\n ignoreQueryParams,\n );\n\n cached = {\n subscribe: source.subscribe,\n getSnapshot: source.getSnapshot,\n destroy: noopDestroy,\n };\n perRouter.set(key, cached);\n }\n\n return cached;\n}\n\nfunction buildActiveRouteSource(\n router: Router,\n routeName: string,\n params: Params | undefined,\n strict: boolean,\n ignoreQueryParams: boolean,\n): RouterSource<boolean> {\n const initialValue = router.isActiveRoute(\n routeName,\n params,\n strict,\n ignoreQueryParams,\n );\n\n const source = new BaseSource(initialValue);\n\n // Eager connection: subscribe to router immediately. This source is only\n // ever reached through the cached public `createActiveRouteSource`, whose\n // returned wrapper has a no-op destroy. The source lives with the router;\n // the router.subscribe handle is released on router GC.\n 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\nfunction noopDestroy(): void {\n // Shared cached source — external destroy() is a no-op.\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 isLeaveApproved: false,\n toRoute: null,\n fromRoute: null,\n};\n\nconst transitionSourceCache = new WeakMap<\n Router,\n RouterSource<RouterTransitionSnapshot>\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 isLeaveApproved: false,\n toRoute: newToRoute,\n fromRoute: newFromRoute,\n });\n }\n },\n ),\n api.addEventListener(\n events.TRANSITION_LEAVE_APPROVE,\n (toState: State, fromState?: State) => {\n const prev = source.getSnapshot();\n\n source.updateSnapshot({\n isTransitioning: true,\n isLeaveApproved: true,\n toRoute: stabilizeState(prev.toRoute, toState),\n fromRoute: stabilizeState(prev.fromRoute, fromState ?? null),\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\n/**\n * Returns a per-router cached transition source shared across all consumers.\n *\n * Safe to call destroy() — the cached source ignores external destroy() calls\n * and lives until the router itself is garbage-collected (the WeakMap entry\n * releases automatically).\n *\n * Use this in framework adapters (React/Preact/Solid/Vue/Svelte/Angular) to\n * share a single TransitionSource instance across all mount/unmount cycles.\n *\n * For isolated/advanced use (ad-hoc, short-lived, per-owner teardown), call\n * `createTransitionSource(router)` directly — it returns a fresh instance with\n * a working `destroy()`.\n */\nexport function getTransitionSource(\n router: Router,\n): RouterSource<RouterTransitionSnapshot> {\n let cached = transitionSourceCache.get(router);\n\n if (!cached) {\n const source = createTransitionSource(router);\n\n // Wrap with no-op destroy. The underlying source is shared across all\n // consumers; letting any one consumer call destroy() would tear it down\n // for the rest. The source lives as long as the router (WeakMap key).\n cached = {\n subscribe: source.subscribe,\n getSnapshot: source.getSnapshot,\n destroy: noopDestroy,\n };\n transitionSourceCache.set(router, cached);\n }\n\n return cached;\n}\n\nfunction noopDestroy(): void {\n // Shared cached source — external destroy() is a no-op.\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\nconst errorSourceCache = new WeakMap<\n Router,\n RouterSource<RouterErrorSnapshot>\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\n/**\n * Returns a per-router cached error source shared across all consumers.\n *\n * Safe to call destroy() — the cached source ignores external destroy() calls\n * and lives until the router itself is garbage-collected (the WeakMap entry\n * releases automatically).\n *\n * Use this in framework adapters (React/Preact/Solid/Vue/Svelte/Angular) to\n * share a single ErrorSource instance across all mount/unmount cycles.\n *\n * For isolated/advanced use (ad-hoc, short-lived, per-owner teardown), call\n * `createErrorSource(router)` directly — it returns a fresh instance with a\n * working `destroy()`.\n */\nexport function getErrorSource(\n router: Router,\n): RouterSource<RouterErrorSnapshot> {\n let cached = errorSourceCache.get(router);\n\n if (!cached) {\n const source = createErrorSource(router);\n\n // Wrap with no-op destroy. The underlying source is shared across all\n // consumers; letting any one consumer call destroy() would tear it down\n // for the rest. The source lives as long as the router (WeakMap key).\n cached = {\n subscribe: source.subscribe,\n getSnapshot: source.getSnapshot,\n destroy: noopDestroy,\n };\n errorSourceCache.set(router, cached);\n }\n\n return cached;\n}\n\nfunction noopDestroy(): void {\n // Shared cached source — external destroy() is a no-op.\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { getErrorSource } from \"./createErrorSource\";\n\nimport type { DismissableErrorSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\nconst dismissableCache = new WeakMap<\n Router,\n RouterSource<DismissableErrorSnapshot>\n>();\n\n/**\n * Returns a per-router cached source that wraps `getErrorSource(router)` with\n * an integrated \"dismissed\" version counter, exposing a single reactive\n * snapshot `{ error, toRoute, fromRoute, version, resetError }`.\n *\n * Each `RouterErrorBoundary` in a framework adapter subscribes to this one\n * source instead of re-implementing the `dismissedVersion` state pattern\n * locally. The 6-copy duplicate across adapters collapses to this helper.\n *\n * **Semantics:**\n * - `error` is non-null only when `underlying.version > dismissedVersion`.\n * - `resetError()` sets `dismissedVersion = current underlying version`,\n * immediately clearing `error` to `null` and notifying all listeners.\n * - A subsequent `TRANSITION_ERROR` advances `version` beyond `dismissedVersion`,\n * so `error` becomes non-null again — no additional plumbing needed.\n *\n * **Cached:** one instance per router. `destroy()` on the returned source is\n * a no-op. Shared across all `RouterErrorBoundary` consumers.\n */\nexport function createDismissableError(\n router: Router,\n): RouterSource<DismissableErrorSnapshot> {\n const cached = dismissableCache.get(router);\n\n if (cached) {\n return cached;\n }\n\n const errorSource = getErrorSource(router);\n\n let dismissedVersion = -1;\n\n const computeSnapshot = (): DismissableErrorSnapshot => {\n const snap = errorSource.getSnapshot();\n const isDismissed = snap.version <= dismissedVersion;\n\n return {\n error: isDismissed ? null : snap.error,\n toRoute: isDismissed ? null : snap.toRoute,\n fromRoute: isDismissed ? null : snap.fromRoute,\n version: snap.version,\n resetError,\n };\n };\n\n const source = new BaseSource<DismissableErrorSnapshot>(computeSnapshot(), {\n onFirstSubscribe: () => {\n unsubFromError = errorSource.subscribe(() => {\n source.updateSnapshot(computeSnapshot());\n });\n },\n onLastUnsubscribe: () => {\n disconnect();\n },\n });\n\n let unsubFromError: (() => void) | null = null;\n\n function resetError(): void {\n dismissedVersion = errorSource.getSnapshot().version;\n source.updateSnapshot(computeSnapshot());\n }\n\n function disconnect(): void {\n const unsub = unsubFromError;\n\n unsubFromError = null;\n unsub?.();\n }\n\n const wrapper: RouterSource<DismissableErrorSnapshot> = {\n subscribe: source.subscribe,\n getSnapshot: source.getSnapshot,\n destroy: noopDestroy,\n };\n\n dismissableCache.set(router, wrapper);\n\n return wrapper;\n}\n\nfunction noopDestroy(): void {\n // Shared cached source — external destroy() is a no-op.\n}\n","import { areRoutesRelated } from \"@real-router/route-utils\";\n\nimport type { Router } from \"@real-router/core\";\n\nexport interface ActiveNameSelector {\n /**\n * Subscribes to active-state changes of a specific route name.\n * Listener is called only when `isActive(routeName)` for this name transitions.\n * Returns an unsubscribe function.\n */\n subscribe: (routeName: string, listener: () => void) => () => void;\n /**\n * O(1) active check for the given route name (non-strict by default —\n * matches descendants). Uses the shared underlying router subscription.\n */\n isActive: (routeName: string) => boolean;\n /** No-op on the cached wrapper. */\n destroy: () => void;\n}\n\nconst selectorCache = new WeakMap<Router, ActiveNameSelector>();\n\n/**\n * Per-router cached selector providing O(1) active-route checks with one\n * shared router subscription for any number of distinct `routeName`\n * consumers.\n *\n * **When to use:** framework `Link` components that need an active boolean\n * without custom `params` / `activeStrict` / `ignoreQueryParams` — e.g. the\n * common navigation-link case. Multiple `<Link>` components with different\n * `routeName` share ONE `router.subscribe` handle instead of creating one\n * per Link (which is what `createActiveRouteSource(router, name)` does — it\n * caches per-name, so N names = N subscriptions).\n *\n * **When NOT to use:** Link needs `activeStrict: true`, custom `routeParams`,\n * or `ignoreQueryParams: false`. Fall back to `createActiveRouteSource` —\n * its cache handles the full argument surface.\n *\n * Based on the `routeSelector` pattern pioneered by `@real-router/solid`'s\n * `RouterProvider` (`createSelector` + `areRoutesRelated`). This helper\n * ports it to framework-agnostic API so Vue / React / Preact / Svelte /\n * Angular Link components can adopt the same fast path.\n *\n * @see Solid reference implementation — `packages/solid/src/RouterProvider.tsx`\n */\nexport function createActiveNameSelector(router: Router): ActiveNameSelector {\n const cached = selectorCache.get(router);\n\n if (cached) {\n return cached;\n }\n\n // listeners per-name — re-evaluated on every router transition\n const listenersByName = new Map<string, Set<() => void>>();\n // cached active state per-name — used to diff before notifying\n const activeByName = new Map<string, boolean>();\n\n let routerUnsubscribe: (() => void) | null = null;\n\n const isActiveNonStrict = (routeName: string): boolean => {\n const current = router.getState();\n\n if (!current) {\n return false;\n }\n\n return (\n current.name === routeName || current.name.startsWith(`${routeName}.`)\n );\n };\n\n const connect = (): void => {\n routerUnsubscribe = router.subscribe((next) => {\n for (const [routeName, listeners] of listenersByName) {\n // Cheap pre-filter: if neither new nor previous route is related\n // to this name, its active state cannot have changed.\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 continue;\n }\n\n // activeByName always has an entry for names present in listenersByName —\n // subscribe() seeds it, and we only iterate over listenersByName.\n const prevActive = activeByName.get(routeName) === true;\n const nextActive = isActiveNonStrict(routeName);\n\n if (prevActive === nextActive) {\n continue;\n }\n\n activeByName.set(routeName, nextActive);\n for (const listener of listeners) {\n listener();\n }\n }\n });\n };\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const subscribe = (routeName: string, listener: () => void): (() => void) => {\n let listeners = listenersByName.get(routeName);\n\n if (!listeners) {\n listeners = new Set();\n listenersByName.set(routeName, listeners);\n activeByName.set(routeName, isActiveNonStrict(routeName));\n }\n\n listeners.add(listener);\n\n if (!routerUnsubscribe) {\n connect();\n }\n\n let unsubscribed = false;\n\n return () => {\n if (unsubscribed) {\n return;\n }\n\n unsubscribed = true;\n listeners.delete(listener);\n\n if (listeners.size === 0) {\n listenersByName.delete(routeName);\n activeByName.delete(routeName);\n }\n\n if (listenersByName.size === 0) {\n disconnect();\n }\n };\n };\n\n const isActive = (routeName: string): boolean => {\n const cachedActive = activeByName.get(routeName);\n\n if (cachedActive !== undefined) {\n return cachedActive;\n }\n\n // Not subscribed — compute on demand.\n return isActiveNonStrict(routeName);\n };\n\n const selector: ActiveNameSelector = {\n subscribe,\n isActive,\n destroy: noopDestroy,\n };\n\n selectorCache.set(router, selector);\n\n return selector;\n}\n\nfunction noopDestroy(): void {\n // Shared cached selector — external destroy() is a no-op.\n}\n"],"mappings":"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"}