@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 +30 -14
- package/dist/cjs/index.d.ts +167 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.d.mts +167 -1
- package/dist/esm/index.d.mts.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/BaseSource.ts +10 -3
- package/src/canonicalJson.ts +36 -0
- package/src/createActiveNameSelector.ts +170 -0
- package/src/createActiveRouteSource.ts +98 -9
- package/src/createDismissableError.ts +95 -0
- package/src/createErrorSource.ts +45 -0
- package/src/createRouteNodeSource.ts +50 -3
- package/src/createTransitionSource.ts +45 -0
- package/src/index.ts +19 -2
- package/src/normalizeActiveOptions.ts +29 -0
- package/src/types.ts +13 -0
- package/src/shouldUpdateCache.ts +0 -27
package/README.md
CHANGED
|
@@ -38,13 +38,19 @@ const unsubscribe = source.subscribe(() => {
|
|
|
38
38
|
|
|
39
39
|
## Source Factories
|
|
40
40
|
|
|
41
|
-
| Factory | Snapshot
|
|
42
|
-
| ------------------------------------------------------- |
|
|
43
|
-
| `createRouteSource(router)` | `{ route, previousRoute }`
|
|
44
|
-
| `createRouteNodeSource(router, node)` | `{ route, previousRoute }`
|
|
45
|
-
| `createActiveRouteSource(router, name, params?, opts?)` | `boolean`
|
|
46
|
-
| `createTransitionSource(router)` | `{ isTransitioning, toRoute, fromRoute }`
|
|
47
|
-
| `
|
|
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
|
|
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 {
|
|
123
|
+
import { getTransitionSource } from "@real-router/sources";
|
|
110
124
|
|
|
111
|
-
|
|
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 {
|
|
142
|
+
import { getErrorSource } from "@real-router/sources";
|
|
127
143
|
|
|
128
|
-
const source =
|
|
144
|
+
const source = getErrorSource(router);
|
|
129
145
|
|
|
130
146
|
source.subscribe(() => {
|
|
131
147
|
const { error, toRoute } = source.getSnapshot();
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -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
|
package/dist/cjs/index.d.ts.map
CHANGED
|
@@ -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;;;;
|
|
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){
|
|
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
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -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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"}
|