@real-router/sources 0.1.4 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -60,7 +60,7 @@ const source = createRouteSource(router);
60
60
 
61
61
  ### `createRouteNodeSource(router, nodeName)`
62
62
 
63
- Creates a source scoped to a specific route node. Only updates when the node is in the transition path, avoiding unnecessary re-renders for unrelated navigations.\
63
+ Creates a source scoped to a specific route node. Only updates when the node is in the transition path, avoiding unnecessary re-renders for unrelated navigations. Uses a lazy-connection pattern: subscribes to the router on the first listener and unsubscribes when all listeners are removed.\
64
64
  `router: Router` — router instance\
65
65
  `nodeName: string` — route node name to scope updates to\
66
66
  Returns: `RouterSource<RouteNodeSnapshot>`
@@ -69,8 +69,6 @@ Returns: `RouterSource<RouteNodeSnapshot>`
69
69
  const source = createRouteNodeSource(router, "users");
70
70
  ```
71
71
 
72
- Call `source.destroy()` when the source is no longer needed.
73
-
74
72
  ---
75
73
 
76
74
  ### `createActiveRouteSource(router, routeName, params?, options?)`
@@ -101,9 +99,30 @@ Call `source.destroy()` when the source is no longer needed.
101
99
 
102
100
  ---
103
101
 
102
+ ### `createTransitionSource(router)`
103
+
104
+ Creates a source that tracks the router's transition lifecycle. Updates on `TRANSITION_START`, `TRANSITION_SUCCESS`, `TRANSITION_ERROR`, and `TRANSITION_CANCEL` events. Unlike other sources, this uses eager subscription (subscribes to events immediately, not lazily on first listener).\
105
+ `router: Router` — router instance\
106
+ Returns: `RouterSource<RouterTransitionSnapshot>`
107
+
108
+ ```typescript
109
+ const source = createTransitionSource(router);
110
+
111
+ source.subscribe(() => {
112
+ const { isTransitioning, toRoute, fromRoute } = source.getSnapshot();
113
+ if (isTransitioning) {
114
+ console.log(`Navigating: ${fromRoute?.name} → ${toRoute?.name}`);
115
+ }
116
+ });
117
+ ```
118
+
119
+ Call `source.destroy()` when the source is no longer needed.
120
+
121
+ ---
122
+
104
123
  ### `RouterSource<T>` Interface
105
124
 
106
- All three factories return a `RouterSource<T>`:
125
+ All four factories return a `RouterSource<T>`:
107
126
 
108
127
  ```typescript
109
128
  interface RouterSource<T> {
@@ -126,14 +145,16 @@ import type {
126
145
  RouterSource,
127
146
  RouteSnapshot,
128
147
  RouteNodeSnapshot,
148
+ RouterTransitionSnapshot,
129
149
  ActiveRouteSourceOptions,
130
150
  } from "@real-router/sources";
131
151
  ```
132
152
 
133
153
  `RouteSnapshot` — full router state: `{ route: State | undefined, previousRoute: State | undefined }`\
134
154
  `RouteNodeSnapshot` — node-scoped state: `{ route: State | undefined, previousRoute: State | undefined }`\
155
+ `RouterTransitionSnapshot` — transition state: `{ isTransitioning: boolean, toRoute: State | null, fromRoute: State | null }`\
135
156
  `ActiveRouteSourceOptions` — options for `createActiveRouteSource`: `{ strict?: boolean, ignoreQueryParams?: boolean }`\
136
- `RouterSource<T>` — the source interface returned by all three factories
157
+ `RouterSource<T>` — the source interface returned by all four factories
137
158
 
138
159
  ---
139
160
 
@@ -186,8 +207,8 @@ const unsubscribe = source.subscribe(() => {
186
207
  console.log("Users section route:", route?.name);
187
208
  });
188
209
 
189
- // Tear down completely when the component unmounts
190
- source.destroy();
210
+ // Later, clean up (automatically unsubscribes from router)
211
+ unsubscribe();
191
212
  ```
192
213
 
193
214
  ---
@@ -1,4 +1,4 @@
1
- import { State, Router, Params } from '@real-router/types';
1
+ import { State, Router, Params } from '@real-router/core';
2
2
 
3
3
  interface RouteSnapshot {
4
4
  route: State | undefined;
@@ -17,6 +17,11 @@ interface ActiveRouteSourceOptions {
17
17
  strict?: boolean;
18
18
  ignoreQueryParams?: boolean;
19
19
  }
20
+ interface RouterTransitionSnapshot {
21
+ isTransitioning: boolean;
22
+ toRoute: State | null;
23
+ fromRoute: State | null;
24
+ }
20
25
 
21
26
  /**
22
27
  * Creates a source for the full route state.
@@ -27,8 +32,17 @@ interface ActiveRouteSourceOptions {
27
32
  */
28
33
  declare function createRouteSource(router: Router): RouterSource<RouteSnapshot>;
29
34
 
35
+ /**
36
+ * Creates a source scoped to a specific route node.
37
+ *
38
+ * Uses a lazy-connection pattern: the router subscription is created when the
39
+ * first listener subscribes and removed when the last listener unsubscribes.
40
+ * This is compatible with React's useSyncExternalStore and Strict Mode.
41
+ */
30
42
  declare function createRouteNodeSource(router: Router, nodeName: string): RouterSource<RouteNodeSnapshot>;
31
43
 
32
44
  declare function createActiveRouteSource(router: Router, routeName: string, params?: Params, options?: ActiveRouteSourceOptions): RouterSource<boolean>;
33
45
 
34
- export { type ActiveRouteSourceOptions, type RouteNodeSnapshot, type RouteSnapshot, type RouterSource, createActiveRouteSource, createRouteNodeSource, createRouteSource };
46
+ declare function createTransitionSource(router: Router): RouterSource<RouterTransitionSnapshot>;
47
+
48
+ export { type ActiveRouteSourceOptions, type RouteNodeSnapshot, type RouteSnapshot, type RouterSource, type RouterTransitionSnapshot, createActiveRouteSource, createRouteNodeSource, createRouteSource, createTransitionSource };
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- var t=require("@real-router/route-utils"),s=class{#t=null;#s;#e=new Set;#r;constructor(t){this.#r=t,this.#s={route:t.getState(),previousRoute:void 0},this.subscribe=this.subscribe.bind(this),this.destroy=this.destroy.bind(this),this.getSnapshot=this.getSnapshot.bind(this)}subscribe(t){return 0===this.#e.size&&(this.#t=this.#r.subscribe(t=>{this.#s={route:t.route,previousRoute:t.previousRoute},this.#e.forEach(t=>{t()})})),this.#e.add(t),()=>{this.#e.delete(t),0===this.#e.size&&this.#t&&(this.#t(),this.#t=null)}}getSnapshot(){return this.#s}destroy(){this.#t&&(this.#t(),this.#t=null),this.#e.clear()}},e=class{#s;#i=!1;#e=new Set;constructor(t){this.#s=t}subscribe(t){return this.#i?()=>{}:(this.#e.add(t),()=>{this.#e.delete(t)})}getSnapshot(){return this.#s}updateSnapshot(t){this.#i||(this.#s=t,this.#e.forEach(t=>{t()}))}destroy(){this.#i||(this.#i=!0,this.#e.clear())}};function r(t,s,e,r){const i=r?.route??s.getState(),o=r?.previousRoute,u=""===e||void 0!==i&&(i.name===e||i.name.startsWith(`${e}.`))?i:void 0;return u===t.route&&o===t.previousRoute?t:{route:u,previousRoute:o}}var i=new WeakMap,o=class{#o;#u;constructor(t,s){const o=r({route:void 0,previousRoute:void 0},t,s);this.#o=new e(o);const u=function(t,s){let e=i.get(t);e||(e=new Map,i.set(t,e));let r=e.get(s);return r||(r=t.shouldUpdateNode(s),e.set(s,r)),r}(t,s);this.#u=t.subscribe(e=>{if(!u(e.route,e.previousRoute))return;const i=r(this.#o.getSnapshot(),t,s,e);Object.is(this.#o.getSnapshot(),i)||this.#o.updateSnapshot(i)}),this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#o.subscribe(t)}getSnapshot(){return this.#o.getSnapshot()}destroy(){this.#u(),this.#o.destroy()}},u=class{#o;#u;constructor(s,r,i,o){const u=o?.strict??!1,n=o?.ignoreQueryParams??!0,h=s.isActiveRoute(r,i,u,n);this.#o=new e(h),this.#u=s.subscribe(e=>{const o=t.areRoutesRelated(r,e.route.name),h=e.previousRoute&&t.areRoutesRelated(r,e.previousRoute.name);if(!o&&!h)return;const c=!!o&&s.isActiveRoute(r,i,u,n);Object.is(this.#o.getSnapshot(),c)||this.#o.updateSnapshot(c)}),this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#o.subscribe(t)}getSnapshot(){return this.#o.getSnapshot()}destroy(){this.#u(),this.#o.destroy()}};exports.createActiveRouteSource=function(t,s,e,r){return new u(t,s,e,r)},exports.createRouteNodeSource=function(t,s){return new o(t,s)},exports.createRouteSource=function(t){return new s(t)};//# sourceMappingURL=index.js.map
1
+ var t=require("@real-router/route-utils"),s=require("@real-router/core"),e=class{#t=null;#s;#e=new Set;#r;constructor(t){this.#r=t,this.#s={route:t.getState(),previousRoute:void 0},this.subscribe=this.subscribe.bind(this),this.destroy=this.destroy.bind(this),this.getSnapshot=this.getSnapshot.bind(this)}subscribe(t){return 0===this.#e.size&&(this.#t=this.#r.subscribe(t=>{this.#s={route:t.route,previousRoute:t.previousRoute},this.#e.forEach(t=>{t()})})),this.#e.add(t),()=>{this.#e.delete(t),0===this.#e.size&&this.#t&&(this.#t(),this.#t=null)}}getSnapshot(){return this.#s}destroy(){this.#t&&(this.#t(),this.#t=null),this.#e.clear()}};function r(t,s,e,r){const i=r?.route??s.getState(),o=r?.previousRoute,u=""===e||void 0!==i&&(i.name===e||i.name.startsWith(`${e}.`))?i:void 0;return u===t.route&&o===t.previousRoute?t:{route:u,previousRoute:o}}var i=new WeakMap,o=class{#t=null;#s;#i=!1;#e=new Set;#r;#o;#u;constructor(t,s){this.#r=t,this.#o=s,this.#u=function(t,s){let e=i.get(t);e||(e=new Map,i.set(t,e));let r=e.get(s);return r||(r=t.shouldUpdateNode(s),e.set(s,r)),r}(t,s),this.#s=r({route:void 0,previousRoute:void 0},t,s),this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#i?()=>{}:(0===this.#e.size&&(this.#s=r(this.#s,this.#r,this.#o),this.#t=this.#r.subscribe(t=>{if(!this.#u(t.route,t.previousRoute))return;const s=r(this.#s,this.#r,this.#o,t);Object.is(this.#s,s)||(this.#s=s,this.#e.forEach(t=>{t()}))})),this.#e.add(t),()=>{this.#e.delete(t),0===this.#e.size&&this.#t&&(this.#t(),this.#t=null)})}getSnapshot(){return this.#s}destroy(){this.#i||(this.#i=!0,this.#t&&(this.#t(),this.#t=null),this.#e.clear())}},u=class{#s;#i=!1;#e=new Set;constructor(t){this.#s=t}subscribe(t){return this.#i?()=>{}:(this.#e.add(t),()=>{this.#e.delete(t)})}getSnapshot(){return this.#s}updateSnapshot(t){this.#i||(this.#s=t,this.#e.forEach(t=>{t()}))}destroy(){this.#i||(this.#i=!0,this.#e.clear())}},n=class{#n;#h;constructor(s,e,r,i){const o=i?.strict??!1,n=i?.ignoreQueryParams??!0,h=s.isActiveRoute(e,r,o,n);this.#n=new u(h),this.#h=s.subscribe(i=>{const u=t.areRoutesRelated(e,i.route.name),h=i.previousRoute&&t.areRoutesRelated(e,i.previousRoute.name);if(!u&&!h)return;const c=!!u&&s.isActiveRoute(e,r,o,n);Object.is(this.#n.getSnapshot(),c)||this.#n.updateSnapshot(c)}),this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#n.subscribe(t)}getSnapshot(){return this.#n.getSnapshot()}destroy(){this.#h(),this.#n.destroy()}},h={isTransitioning:!1,toRoute:null,fromRoute:null},c=class{#n;#c;constructor(t){this.#n=new u(h);const e=s.getPluginApi(t),r=()=>{this.#n.updateSnapshot(h)};this.#c=[e.addEventListener(s.events.TRANSITION_START,(t,s)=>{this.#n.updateSnapshot({isTransitioning:!0,toRoute:t,fromRoute:s??null})}),e.addEventListener(s.events.TRANSITION_SUCCESS,r),e.addEventListener(s.events.TRANSITION_ERROR,r),e.addEventListener(s.events.TRANSITION_CANCEL,r)],this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#n.subscribe(t)}getSnapshot(){return this.#n.getSnapshot()}destroy(){this.#c.forEach(t=>{t()}),this.#n.destroy()}};exports.createActiveRouteSource=function(t,s,e,r){return new n(t,s,e,r)},exports.createRouteNodeSource=function(t,s){return new o(t,s)},exports.createRouteSource=function(t){return new e(t)},exports.createTransitionSource=function(t){return new c(t)};//# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/createRouteSource.ts","../../src/BaseSource.ts","../../src/computeSnapshot.ts","../../src/shouldUpdateCache.ts","../../src/createRouteNodeSource.ts","../../src/createActiveRouteSource.ts"],"names":["areRoutesRelated"],"mappings":";AAGA,IAAM,cAAN,MAAyD;AAAA,EACvD,kBAAA,GAA0C,IAAA;AAAA,EAC1C,gBAAA;AAAA,EAES,UAAA,uBAAiB,GAAA,EAAgB;AAAA,EACjC,OAAA;AAAA,EAET,YAAY,MAAA,EAAgB;AAC1B,IAAA,IAAA,CAAK,OAAA,GAAU,MAAA;AAEf,IAAA,IAAA,CAAK,gBAAA,GAAmB;AAAA,MACtB,KAAA,EAAO,OAAO,QAAA,EAAS;AAAA,MACvB,aAAA,EAAe;AAAA,KACjB;AAEA,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA;AACzC,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA;AACrC,IAAA,IAAA,CAAK,WAAA,GAAc,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA;AAAA,EAC/C;AAAA,EAEA,UAAU,QAAA,EAAkC;AAC1C,IAAA,IAAI,IAAA,CAAK,UAAA,CAAW,IAAA,KAAS,CAAA,EAAG;AAE9B,MAAA,IAAA,CAAK,kBAAA,GAAqB,IAAA,CAAK,OAAA,CAAQ,SAAA,CAAU,CAAC,IAAA,KAAS;AACzD,QAAA,IAAA,CAAK,gBAAA,GAAmB;AAAA,UACtB,OAAO,IAAA,CAAK,KAAA;AAAA,UACZ,eAAe,IAAA,CAAK;AAAA,SACtB;AACA,QAAA,IAAA,CAAK,UAAA,CAAW,OAAA,CAAQ,CAAC,EAAA,KAAO;AAC9B,UAAA,EAAA,EAAG;AAAA,QACL,CAAC,CAAA;AAAA,MACH,CAAC,CAAA;AAAA,IACH;AAEA,IAAA,IAAA,CAAK,UAAA,CAAW,IAAI,QAAQ,CAAA;AAE5B,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,UAAA,CAAW,OAAO,QAAQ,CAAA;AAE/B,MAAA,IAAI,IAAA,CAAK,UAAA,CAAW,IAAA,KAAS,CAAA,IAAK,KAAK,kBAAA,EAAoB;AACzD,QAAA,IAAA,CAAK,kBAAA,EAAmB;AACxB,QAAA,IAAA,CAAK,kBAAA,GAAqB,IAAA;AAAA,MAC5B;AAAA,IACF,CAAA;AAAA,EACF;AAAA,EAEA,WAAA,GAA6B;AAC3B,IAAA,OAAO,IAAA,CAAK,gBAAA;AAAA,EACd;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAI,KAAK,kBAAA,EAAoB;AAC3B,MAAA,IAAA,CAAK,kBAAA,EAAmB;AACxB,MAAA,IAAA,CAAK,kBAAA,GAAqB,IAAA;AAAA,IAC5B;AAEA,IAAA,IAAA,CAAK,WAAW,KAAA,EAAM;AAAA,EACxB;AACF,CAAA;AASO,SAAS,kBAAkB,MAAA,EAA6C;AAC7E,EAAA,OAAO,IAAI,YAAY,MAAM,CAAA;AAC/B;;;ACxEO,IAAM,aAAN,MAAoB;AAAA,EACzB,gBAAA;AAAA,EACA,UAAA,GAAa,KAAA;AAAA,EAEJ,UAAA,uBAAiB,GAAA,EAAgB;AAAA,EAE1C,YAAY,eAAA,EAAoB;AAC9B,IAAA,IAAA,CAAK,gBAAA,GAAmB,eAAA;AAAA,EAC1B;AAAA,EAEA,UAAU,QAAA,EAAkC;AAC1C,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA,OAAO,MAAM;AAAA,MAAC,CAAA;AAAA,IAChB;AAEA,IAAA,IAAA,CAAK,UAAA,CAAW,IAAI,QAAQ,CAAA;AAE5B,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,UAAA,CAAW,OAAO,QAAQ,CAAA;AAAA,IACjC,CAAA;AAAA,EACF;AAAA,EAEA,WAAA,GAAiB;AACf,IAAA,OAAO,IAAA,CAAK,gBAAA;AAAA,EACd;AAAA,EAEA,eAAe,QAAA,EAAmB;AAEhC,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,gBAAA,GAAmB,QAAA;AACxB,IAAA,IAAA,CAAK,UAAA,CAAW,OAAA,CAAQ,CAAC,QAAA,KAAa;AACpC,MAAA,QAAA,EAAS;AAAA,IACX,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,IAAA,IAAA,CAAK,WAAW,KAAA,EAAM;AAAA,EACxB;AACF,CAAA;;;AC3CO,SAAS,eAAA,CACd,eAAA,EACA,MAAA,EACA,QAAA,EACA,IAAA,EACmB;AACnB,EAAA,MAAM,YAAA,GAAe,IAAA,EAAM,KAAA,IAAS,MAAA,CAAO,QAAA,EAAS;AACpD,EAAA,MAAM,gBAAgB,IAAA,EAAM,aAAA;AAE5B,EAAA,MAAM,YAAA,GACJ,QAAA,KAAa,EAAA,IACZ,YAAA,KAAiB,MAAA,KACf,YAAA,CAAa,IAAA,KAAS,QAAA,IACrB,YAAA,CAAa,IAAA,CAAK,UAAA,CAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,CAAG,CAAA,CAAA;AAEjD,EAAA,MAAM,KAAA,GAAQ,eAAe,YAAA,GAAe,MAAA;AAE5C,EAAA,IACE,KAAA,KAAU,eAAA,CAAgB,KAAA,IAC1B,aAAA,KAAkB,gBAAgB,aAAA,EAClC;AACA,IAAA,OAAO,eAAA;AAAA,EACT;AAEA,EAAA,OAAO,EAAE,OAAO,aAAA,EAAc;AAChC;;;AC1BA,IAAM,iBAAA,uBAAwB,OAAA,EAG5B;AAEK,SAAS,qBAAA,CACd,QACA,QAAA,EACgD;AAChD,EAAA,IAAI,WAAA,GAAc,iBAAA,CAAkB,GAAA,CAAI,MAAM,CAAA;AAE9C,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,WAAA,uBAAkB,GAAA,EAAI;AACtB,IAAA,iBAAA,CAAkB,GAAA,CAAI,QAAQ,WAAW,CAAA;AAAA,EAC3C;AAEA,EAAA,IAAI,EAAA,GAAK,WAAA,CAAY,GAAA,CAAI,QAAQ,CAAA;AAEjC,EAAA,IAAI,CAAC,EAAA,EAAI;AACP,IAAA,EAAA,GAAK,MAAA,CAAO,iBAAiB,QAAQ,CAAA;AACrC,IAAA,WAAA,CAAY,GAAA,CAAI,UAAU,EAAE,CAAA;AAAA,EAC9B;AAEA,EAAA,OAAO,EAAA;AACT;;;ACnBA,IAAM,kBAAN,MAAiE;AAAA,EACtD,OAAA;AAAA,EACA,YAAA;AAAA,EAET,WAAA,CAAY,QAAgB,QAAA,EAAkB;AAC5C,IAAA,MAAM,eAAA,GAAqC;AAAA,MACzC,KAAA,EAAO,MAAA;AAAA,MACP,aAAA,EAAe;AAAA,KACjB;AACA,IAAA,MAAM,eAAA,GAAkB,eAAA,CAAgB,eAAA,EAAiB,MAAA,EAAQ,QAAQ,CAAA;AAEzE,IAAA,IAAA,CAAK,OAAA,GAAU,IAAI,UAAA,CAAW,eAAe,CAAA;AAC7C,IAAA,MAAM,YAAA,GAAe,qBAAA,CAAsB,MAAA,EAAQ,QAAQ,CAAA;AAE3D,IAAA,IAAA,CAAK,YAAA,GAAe,MAAA,CAAO,SAAA,CAAU,CAAC,IAAA,KAAS;AAC7C,MAAA,IAAI,CAAC,YAAA,CAAa,IAAA,CAAK,KAAA,EAAO,IAAA,CAAK,aAAa,CAAA,EAAG;AACjD,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,WAAA,GAAc,eAAA;AAAA,QAClB,IAAA,CAAK,QAAQ,WAAA,EAAY;AAAA,QACzB,MAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACF;AAGA,MAAA,IAAI,CAAC,OAAO,EAAA,CAAG,IAAA,CAAK,QAAQ,WAAA,EAAY,EAAG,WAAW,CAAA,EAAG;AACvD,QAAA,IAAA,CAAK,OAAA,CAAQ,eAAe,WAAW,CAAA;AAAA,MACzC;AAAA,IACF,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA;AACzC,IAAA,IAAA,CAAK,WAAA,GAAc,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA;AAC7C,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA;AAAA,EACvC;AAAA,EAEA,UAAU,QAAA,EAAkC;AAC1C,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,CAAU,QAAQ,CAAA;AAAA,EACxC;AAAA,EAEA,WAAA,GAAiC;AAC/B,IAAA,OAAO,IAAA,CAAK,QAAQ,WAAA,EAAY;AAAA,EAClC;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAA,CAAK,YAAA,EAAa;AAClB,IAAA,IAAA,CAAK,QAAQ,OAAA,EAAQ;AAAA,EACvB;AACF,CAAA;AAEO,SAAS,qBAAA,CACd,QACA,QAAA,EACiC;AACjC,EAAA,OAAO,IAAI,eAAA,CAAgB,MAAA,EAAQ,QAAQ,CAAA;AAC7C;ACxDA,IAAM,oBAAN,MAAyD;AAAA,EAC9C,OAAA;AAAA,EACA,YAAA;AAAA,EAET,WAAA,CACE,MAAA,EACA,SAAA,EACA,MAAA,EACA,OAAA,EACA;AACA,IAAA,MAAM,MAAA,GAAS,SAAS,MAAA,IAAU,KAAA;AAClC,IAAA,MAAM,iBAAA,GAAoB,SAAS,iBAAA,IAAqB,IAAA;AAExD,IAAA,MAAM,eAAe,MAAA,CAAO,aAAA;AAAA,MAC1B,SAAA;AAAA,MACA,MAAA;AAAA,MACA,MAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,IAAA,CAAK,OAAA,GAAU,IAAI,UAAA,CAAW,YAAY,CAAA;AAE1C,IAAA,IAAA,CAAK,YAAA,GAAe,MAAA,CAAO,SAAA,CAAU,CAAC,IAAA,KAAS;AAC7C,MAAA,MAAM,YAAA,GAAeA,2BAAA,CAAiB,SAAA,EAAW,IAAA,CAAK,MAAM,IAAI,CAAA;AAChE,MAAA,MAAM,gBACJ,IAAA,CAAK,aAAA,IACLA,4BAAiB,SAAA,EAAW,IAAA,CAAK,cAAc,IAAI,CAAA;AAErD,MAAA,IAAI,CAAC,YAAA,IAAgB,CAAC,aAAA,EAAe;AACnC,QAAA;AAAA,MACF;AAIA,MAAA,MAAM,QAAA,GAAW,eACb,MAAA,CAAO,aAAA,CAAc,WAAW,MAAA,EAAQ,MAAA,EAAQ,iBAAiB,CAAA,GACjE,KAAA;AAEJ,MAAA,IAAI,CAAC,OAAO,EAAA,CAAG,IAAA,CAAK,QAAQ,WAAA,EAAY,EAAG,QAAQ,CAAA,EAAG;AACpD,QAAA,IAAA,CAAK,OAAA,CAAQ,eAAe,QAAQ,CAAA;AAAA,MACtC;AAAA,IACF,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA;AACzC,IAAA,IAAA,CAAK,WAAA,GAAc,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA;AAC7C,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA;AAAA,EACvC;AAAA,EAEA,UAAU,QAAA,EAAkC;AAC1C,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,CAAU,QAAQ,CAAA;AAAA,EACxC;AAAA,EAEA,WAAA,GAAuB;AACrB,IAAA,OAAO,IAAA,CAAK,QAAQ,WAAA,EAAY;AAAA,EAClC;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAA,CAAK,YAAA,EAAa;AAClB,IAAA,IAAA,CAAK,QAAQ,OAAA,EAAQ;AAAA,EACvB;AACF,CAAA;AAEO,SAAS,uBAAA,CACd,MAAA,EACA,SAAA,EACA,MAAA,EACA,OAAA,EACuB;AACvB,EAAA,OAAO,IAAI,iBAAA,CAAkB,MAAA,EAAQ,SAAA,EAAW,QAAQ,OAAO,CAAA;AACjE","file":"index.js","sourcesContent":["import type { RouteSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/types\";\n\nclass RouteSource implements RouterSource<RouteSnapshot> {\n #routerUnsubscribe: (() => void) | null = null;\n #currentSnapshot: RouteSnapshot;\n\n readonly #listeners = new Set<() => void>();\n readonly #router: Router;\n\n constructor(router: Router) {\n this.#router = router;\n\n this.#currentSnapshot = {\n route: router.getState(),\n previousRoute: undefined,\n };\n\n this.subscribe = this.subscribe.bind(this);\n this.destroy = this.destroy.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#listeners.size === 0) {\n // Connect to router on first subscription\n this.#routerUnsubscribe = this.#router.subscribe((next) => {\n this.#currentSnapshot = {\n route: next.route,\n previousRoute: next.previousRoute,\n };\n this.#listeners.forEach((cb) => {\n cb();\n });\n });\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (this.#listeners.size === 0 && this.#routerUnsubscribe) {\n this.#routerUnsubscribe();\n this.#routerUnsubscribe = null;\n }\n };\n }\n\n getSnapshot(): RouteSnapshot {\n return this.#currentSnapshot;\n }\n\n destroy(): void {\n if (this.#routerUnsubscribe) {\n this.#routerUnsubscribe();\n this.#routerUnsubscribe = null;\n }\n\n this.#listeners.clear();\n }\n}\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 return new RouteSource(router);\n}\n","export class BaseSource<T> {\n #currentSnapshot: T;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n\n constructor(initialSnapshot: T) {\n this.#currentSnapshot = initialSnapshot;\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#destroyed) {\n return () => {};\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\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.#listeners.clear();\n }\n}\n","import type { RouteNodeSnapshot } from \"./types.js\";\nimport type { Router, SubscribeState } from \"@real-router/types\";\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 return { route, previousRoute };\n}\n","import type { Router, State } from \"@real-router/types\";\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/types\";\n\nclass RouteNodeSource implements RouterSource<RouteNodeSnapshot> {\n readonly #source: BaseSource<RouteNodeSnapshot>;\n readonly #unsubscribe: () => void;\n\n constructor(router: Router, nodeName: string) {\n const initialSnapshot: RouteNodeSnapshot = {\n route: undefined,\n previousRoute: undefined,\n };\n const computedInitial = computeSnapshot(initialSnapshot, router, nodeName);\n\n this.#source = new BaseSource(computedInitial);\n const shouldUpdate = getCachedShouldUpdate(router, nodeName);\n\n this.#unsubscribe = router.subscribe((next) => {\n if (!shouldUpdate(next.route, next.previousRoute)) {\n return;\n }\n\n const newSnapshot = computeSnapshot(\n this.#source.getSnapshot(),\n router,\n nodeName,\n next,\n );\n\n /* v8 ignore next 3 -- @preserve: dedup guard; shouldUpdateNode filters accurately so computeSnapshot always returns new ref */\n if (!Object.is(this.#source.getSnapshot(), newSnapshot)) {\n this.#source.updateSnapshot(newSnapshot);\n }\n });\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 return this.#source.subscribe(listener);\n }\n\n getSnapshot(): RouteNodeSnapshot {\n return this.#source.getSnapshot();\n }\n\n destroy(): void {\n this.#unsubscribe();\n this.#source.destroy();\n }\n}\n\nexport function createRouteNodeSource(\n router: Router,\n nodeName: string,\n): RouterSource<RouteNodeSnapshot> {\n return new RouteNodeSource(router, nodeName);\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/types\";\n\nclass ActiveRouteSource implements RouterSource<boolean> {\n readonly #source: BaseSource<boolean>;\n readonly #unsubscribe: () => void;\n\n constructor(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n ) {\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 this.#source = new BaseSource(initialValue);\n\n this.#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(this.#source.getSnapshot(), newValue)) {\n this.#source.updateSnapshot(newValue);\n }\n });\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 return this.#source.subscribe(listener);\n }\n\n getSnapshot(): boolean {\n return this.#source.getSnapshot();\n }\n\n destroy(): void {\n this.#unsubscribe();\n this.#source.destroy();\n }\n}\n\nexport function createActiveRouteSource(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n): RouterSource<boolean> {\n return new ActiveRouteSource(router, routeName, params, options);\n}\n"]}
1
+ {"version":3,"sources":["../../src/createRouteSource.ts","../../src/computeSnapshot.ts","../../src/shouldUpdateCache.ts","../../src/createRouteNodeSource.ts","../../src/BaseSource.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts"],"names":["areRoutesRelated","getPluginApi","events"],"mappings":"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","file":"index.js","sourcesContent":["import type { RouteSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\nclass RouteSource implements RouterSource<RouteSnapshot> {\n #routerUnsubscribe: (() => void) | null = null;\n #currentSnapshot: RouteSnapshot;\n\n readonly #listeners = new Set<() => void>();\n readonly #router: Router;\n\n constructor(router: Router) {\n this.#router = router;\n\n this.#currentSnapshot = {\n route: router.getState(),\n previousRoute: undefined,\n };\n\n this.subscribe = this.subscribe.bind(this);\n this.destroy = this.destroy.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#listeners.size === 0) {\n // Connect to router on first subscription\n this.#routerUnsubscribe = this.#router.subscribe((next) => {\n this.#currentSnapshot = {\n route: next.route,\n previousRoute: next.previousRoute,\n };\n this.#listeners.forEach((cb) => {\n cb();\n });\n });\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (this.#listeners.size === 0 && this.#routerUnsubscribe) {\n this.#routerUnsubscribe();\n this.#routerUnsubscribe = null;\n }\n };\n }\n\n getSnapshot(): RouteSnapshot {\n return this.#currentSnapshot;\n }\n\n destroy(): void {\n if (this.#routerUnsubscribe) {\n this.#routerUnsubscribe();\n this.#routerUnsubscribe = null;\n }\n\n this.#listeners.clear();\n }\n}\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 return new RouteSource(router);\n}\n","import 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 return { route, previousRoute };\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 { 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\nclass RouteNodeSource implements RouterSource<RouteNodeSnapshot> {\n #routerUnsubscribe: (() => void) | null = null;\n #currentSnapshot: RouteNodeSnapshot;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n readonly #router: Router;\n readonly #nodeName: string;\n readonly #shouldUpdate: ReturnType<typeof getCachedShouldUpdate>;\n\n constructor(router: Router, nodeName: string) {\n this.#router = router;\n this.#nodeName = nodeName;\n this.#shouldUpdate = getCachedShouldUpdate(router, nodeName);\n\n const initialSnapshot: RouteNodeSnapshot = {\n route: undefined,\n previousRoute: undefined,\n };\n\n this.#currentSnapshot = computeSnapshot(initialSnapshot, router, nodeName);\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) {\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 this.#currentSnapshot = computeSnapshot(\n this.#currentSnapshot,\n this.#router,\n this.#nodeName,\n );\n\n // Connect to router on first subscription\n this.#routerUnsubscribe = this.#router.subscribe((next) => {\n if (!this.#shouldUpdate(next.route, next.previousRoute)) {\n return;\n }\n\n const newSnapshot = computeSnapshot(\n this.#currentSnapshot,\n this.#router,\n this.#nodeName,\n next,\n );\n\n /* v8 ignore next 3 -- @preserve: dedup guard; shouldUpdateNode filters accurately so computeSnapshot always returns new ref */\n if (!Object.is(this.#currentSnapshot, newSnapshot)) {\n this.#currentSnapshot = newSnapshot;\n this.#listeners.forEach((cb) => {\n cb();\n });\n }\n });\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (this.#listeners.size === 0 && this.#routerUnsubscribe) {\n this.#routerUnsubscribe();\n this.#routerUnsubscribe = null;\n }\n };\n }\n\n getSnapshot(): RouteNodeSnapshot {\n return this.#currentSnapshot;\n }\n\n destroy(): void {\n if (this.#destroyed) {\n return;\n }\n\n this.#destroyed = true;\n\n if (this.#routerUnsubscribe) {\n this.#routerUnsubscribe();\n this.#routerUnsubscribe = null;\n }\n\n this.#listeners.clear();\n }\n}\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 return new RouteNodeSource(router, nodeName);\n}\n","export class BaseSource<T> {\n #currentSnapshot: T;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n\n constructor(initialSnapshot: T) {\n this.#currentSnapshot = initialSnapshot;\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#destroyed) {\n return () => {};\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\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.#listeners.clear();\n }\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\nclass ActiveRouteSource implements RouterSource<boolean> {\n readonly #source: BaseSource<boolean>;\n readonly #unsubscribe: () => void;\n\n constructor(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n ) {\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 this.#source = new BaseSource(initialValue);\n\n this.#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(this.#source.getSnapshot(), newValue)) {\n this.#source.updateSnapshot(newValue);\n }\n });\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 return this.#source.subscribe(listener);\n }\n\n getSnapshot(): boolean {\n return this.#source.getSnapshot();\n }\n\n destroy(): void {\n this.#unsubscribe();\n this.#source.destroy();\n }\n}\n\nexport function createActiveRouteSource(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n): RouterSource<boolean> {\n return new ActiveRouteSource(router, routeName, params, options);\n}\n","import { getPluginApi, events } from \"@real-router/core\";\n\nimport { BaseSource } from \"./BaseSource\";\n\nimport type { RouterTransitionSnapshot, RouterSource } from \"./types.js\";\nimport type { Router, State } from \"@real-router/core\";\n\nconst IDLE_SNAPSHOT: RouterTransitionSnapshot = {\n isTransitioning: false,\n toRoute: null,\n fromRoute: null,\n};\n\nclass TransitionSource implements RouterSource<RouterTransitionSnapshot> {\n readonly #source: BaseSource<RouterTransitionSnapshot>;\n readonly #unsubs: (() => void)[];\n\n constructor(router: Router) {\n this.#source = new BaseSource(IDLE_SNAPSHOT);\n\n const api = getPluginApi(router);\n\n const resetToIdle = (): void => {\n this.#source.updateSnapshot(IDLE_SNAPSHOT);\n };\n\n this.#unsubs = [\n api.addEventListener(\n events.TRANSITION_START,\n (toState: State, fromState?: State) => {\n this.#source.updateSnapshot({\n isTransitioning: true,\n toRoute: toState,\n 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 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 return this.#source.subscribe(listener);\n }\n\n getSnapshot(): RouterTransitionSnapshot {\n return this.#source.getSnapshot();\n }\n\n destroy(): void {\n this.#unsubs.forEach((u) => {\n u();\n });\n this.#source.destroy();\n }\n}\n\nexport function createTransitionSource(\n router: Router,\n): RouterSource<RouterTransitionSnapshot> {\n return new TransitionSource(router);\n}\n"]}
@@ -1 +1 @@
1
- {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/createRouteSource.ts":{"bytes":1957,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/BaseSource.ts":{"bytes":941,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/computeSnapshot.ts":{"bytes":791,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/shouldUpdateCache.ts":{"bytes":611,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createRouteNodeSource.ts":{"bytes":1912,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/computeSnapshot.ts","kind":"import-statement","original":"./computeSnapshot.js"},{"path":"src/shouldUpdateCache.ts","kind":"import-statement","original":"./shouldUpdateCache.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createActiveRouteSource.ts":{"bytes":2125,"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":311,"imports":[{"path":"src/createRouteSource.ts","kind":"import-statement","original":"./createRouteSource"},{"path":"src/createRouteNodeSource.ts","kind":"import-statement","original":"./createRouteNodeSource"},{"path":"src/createActiveRouteSource.ts","kind":"import-statement","original":"./createActiveRouteSource"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":13042},"dist/cjs/index.js":{"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true}],"exports":["createActiveRouteSource","createRouteNodeSource","createRouteSource"],"entryPoint":"src/index.ts","inputs":{"src/createRouteSource.ts":{"bytesInOutput":1303},"src/index.ts":{"bytesInOutput":0},"src/BaseSource.ts":{"bytesInOutput":752},"src/computeSnapshot.ts":{"bytesInOutput":536},"src/shouldUpdateCache.ts":{"bytesInOutput":425},"src/createRouteNodeSource.ts":{"bytesInOutput":1234},"src/createActiveRouteSource.ts":{"bytesInOutput":1490}},"bytes":6252}}}
1
+ {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/createRouteSource.ts":{"bytes":1956,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/computeSnapshot.ts":{"bytes":790,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/shouldUpdateCache.ts":{"bytes":610,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createRouteNodeSource.ts":{"bytes":3347,"imports":[{"path":"src/computeSnapshot.ts","kind":"import-statement","original":"./computeSnapshot.js"},{"path":"src/shouldUpdateCache.ts","kind":"import-statement","original":"./shouldUpdateCache.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/BaseSource.ts":{"bytes":941,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createActiveRouteSource.ts":{"bytes":2124,"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createTransitionSource.ts":{"bytes":1845,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":407,"imports":[{"path":"src/createRouteSource.ts","kind":"import-statement","original":"./createRouteSource"},{"path":"src/createRouteNodeSource.ts","kind":"import-statement","original":"./createRouteNodeSource"},{"path":"src/createActiveRouteSource.ts","kind":"import-statement","original":"./createActiveRouteSource"},{"path":"src/createTransitionSource.ts","kind":"import-statement","original":"./createTransitionSource"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":18008},"dist/cjs/index.js":{"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["createActiveRouteSource","createRouteNodeSource","createRouteSource","createTransitionSource"],"entryPoint":"src/index.ts","inputs":{"src/createRouteSource.ts":{"bytesInOutput":1303},"src/index.ts":{"bytesInOutput":0},"src/computeSnapshot.ts":{"bytesInOutput":536},"src/shouldUpdateCache.ts":{"bytesInOutput":425},"src/createRouteNodeSource.ts":{"bytesInOutput":2089},"src/createActiveRouteSource.ts":{"bytesInOutput":1490},"src/BaseSource.ts":{"bytesInOutput":752},"src/createTransitionSource.ts":{"bytesInOutput":1367}},"bytes":8569}}}
@@ -1,4 +1,4 @@
1
- import { State, Router, Params } from '@real-router/types';
1
+ import { State, Router, Params } from '@real-router/core';
2
2
 
3
3
  interface RouteSnapshot {
4
4
  route: State | undefined;
@@ -17,6 +17,11 @@ interface ActiveRouteSourceOptions {
17
17
  strict?: boolean;
18
18
  ignoreQueryParams?: boolean;
19
19
  }
20
+ interface RouterTransitionSnapshot {
21
+ isTransitioning: boolean;
22
+ toRoute: State | null;
23
+ fromRoute: State | null;
24
+ }
20
25
 
21
26
  /**
22
27
  * Creates a source for the full route state.
@@ -27,8 +32,17 @@ interface ActiveRouteSourceOptions {
27
32
  */
28
33
  declare function createRouteSource(router: Router): RouterSource<RouteSnapshot>;
29
34
 
35
+ /**
36
+ * Creates a source scoped to a specific route node.
37
+ *
38
+ * Uses a lazy-connection pattern: the router subscription is created when the
39
+ * first listener subscribes and removed when the last listener unsubscribes.
40
+ * This is compatible with React's useSyncExternalStore and Strict Mode.
41
+ */
30
42
  declare function createRouteNodeSource(router: Router, nodeName: string): RouterSource<RouteNodeSnapshot>;
31
43
 
32
44
  declare function createActiveRouteSource(router: Router, routeName: string, params?: Params, options?: ActiveRouteSourceOptions): RouterSource<boolean>;
33
45
 
34
- export { type ActiveRouteSourceOptions, type RouteNodeSnapshot, type RouteSnapshot, type RouterSource, createActiveRouteSource, createRouteNodeSource, createRouteSource };
46
+ declare function createTransitionSource(router: Router): RouterSource<RouterTransitionSnapshot>;
47
+
48
+ export { type ActiveRouteSourceOptions, type RouteNodeSnapshot, type RouteSnapshot, type RouterSource, type RouterTransitionSnapshot, createActiveRouteSource, createRouteNodeSource, createRouteSource, createTransitionSource };
@@ -1 +1 @@
1
- import{areRoutesRelated as s}from"@real-router/route-utils";var t=class{#s=null;#t;#e=new Set;#r;constructor(s){this.#r=s,this.#t={route:s.getState(),previousRoute:void 0},this.subscribe=this.subscribe.bind(this),this.destroy=this.destroy.bind(this),this.getSnapshot=this.getSnapshot.bind(this)}subscribe(s){return 0===this.#e.size&&(this.#s=this.#r.subscribe(s=>{this.#t={route:s.route,previousRoute:s.previousRoute},this.#e.forEach(s=>{s()})})),this.#e.add(s),()=>{this.#e.delete(s),0===this.#e.size&&this.#s&&(this.#s(),this.#s=null)}}getSnapshot(){return this.#t}destroy(){this.#s&&(this.#s(),this.#s=null),this.#e.clear()}};function e(s){return new t(s)}var r=class{#t;#i=!1;#e=new Set;constructor(s){this.#t=s}subscribe(s){return this.#i?()=>{}:(this.#e.add(s),()=>{this.#e.delete(s)})}getSnapshot(){return this.#t}updateSnapshot(s){this.#i||(this.#t=s,this.#e.forEach(s=>{s()}))}destroy(){this.#i||(this.#i=!0,this.#e.clear())}};function i(s,t,e,r){const i=r?.route??t.getState(),o=r?.previousRoute,u=""===e||void 0!==i&&(i.name===e||i.name.startsWith(`${e}.`))?i:void 0;return u===s.route&&o===s.previousRoute?s:{route:u,previousRoute:o}}var o=new WeakMap,u=class{#o;#u;constructor(s,t){const e=i({route:void 0,previousRoute:void 0},s,t);this.#o=new r(e);const u=function(s,t){let e=o.get(s);e||(e=new Map,o.set(s,e));let r=e.get(t);return r||(r=s.shouldUpdateNode(t),e.set(t,r)),r}(s,t);this.#u=s.subscribe(e=>{if(!u(e.route,e.previousRoute))return;const r=i(this.#o.getSnapshot(),s,t,e);Object.is(this.#o.getSnapshot(),r)||this.#o.updateSnapshot(r)}),this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(s){return this.#o.subscribe(s)}getSnapshot(){return this.#o.getSnapshot()}destroy(){this.#u(),this.#o.destroy()}};function n(s,t){return new u(s,t)}var h=class{#o;#u;constructor(t,e,i,o){const u=o?.strict??!1,n=o?.ignoreQueryParams??!0,h=t.isActiveRoute(e,i,u,n);this.#o=new r(h),this.#u=t.subscribe(r=>{const o=s(e,r.route.name),h=r.previousRoute&&s(e,r.previousRoute.name);if(!o&&!h)return;const c=!!o&&t.isActiveRoute(e,i,u,n);Object.is(this.#o.getSnapshot(),c)||this.#o.updateSnapshot(c)}),this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(s){return this.#o.subscribe(s)}getSnapshot(){return this.#o.getSnapshot()}destroy(){this.#u(),this.#o.destroy()}};function c(s,t,e,r){return new h(s,t,e,r)}export{c as createActiveRouteSource,n as createRouteNodeSource,e as createRouteSource};//# sourceMappingURL=index.mjs.map
1
+ import{areRoutesRelated as t}from"@real-router/route-utils";import{getPluginApi as s,events as e}from"@real-router/core";var r=class{#t=null;#s;#e=new Set;#r;constructor(t){this.#r=t,this.#s={route:t.getState(),previousRoute:void 0},this.subscribe=this.subscribe.bind(this),this.destroy=this.destroy.bind(this),this.getSnapshot=this.getSnapshot.bind(this)}subscribe(t){return 0===this.#e.size&&(this.#t=this.#r.subscribe(t=>{this.#s={route:t.route,previousRoute:t.previousRoute},this.#e.forEach(t=>{t()})})),this.#e.add(t),()=>{this.#e.delete(t),0===this.#e.size&&this.#t&&(this.#t(),this.#t=null)}}getSnapshot(){return this.#s}destroy(){this.#t&&(this.#t(),this.#t=null),this.#e.clear()}};function i(t){return new r(t)}function n(t,s,e,r){const i=r?.route??s.getState(),n=r?.previousRoute,o=""===e||void 0!==i&&(i.name===e||i.name.startsWith(`${e}.`))?i:void 0;return o===t.route&&n===t.previousRoute?t:{route:o,previousRoute:n}}var o=new WeakMap,u=class{#t=null;#s;#i=!1;#e=new Set;#r;#n;#o;constructor(t,s){this.#r=t,this.#n=s,this.#o=function(t,s){let e=o.get(t);e||(e=new Map,o.set(t,e));let r=e.get(s);return r||(r=t.shouldUpdateNode(s),e.set(s,r)),r}(t,s),this.#s=n({route:void 0,previousRoute:void 0},t,s),this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#i?()=>{}:(0===this.#e.size&&(this.#s=n(this.#s,this.#r,this.#n),this.#t=this.#r.subscribe(t=>{if(!this.#o(t.route,t.previousRoute))return;const s=n(this.#s,this.#r,this.#n,t);Object.is(this.#s,s)||(this.#s=s,this.#e.forEach(t=>{t()}))})),this.#e.add(t),()=>{this.#e.delete(t),0===this.#e.size&&this.#t&&(this.#t(),this.#t=null)})}getSnapshot(){return this.#s}destroy(){this.#i||(this.#i=!0,this.#t&&(this.#t(),this.#t=null),this.#e.clear())}};function h(t,s){return new u(t,s)}var c=class{#s;#i=!1;#e=new Set;constructor(t){this.#s=t}subscribe(t){return this.#i?()=>{}:(this.#e.add(t),()=>{this.#e.delete(t)})}getSnapshot(){return this.#s}updateSnapshot(t){this.#i||(this.#s=t,this.#e.forEach(t=>{t()}))}destroy(){this.#i||(this.#i=!0,this.#e.clear())}},b=class{#u;#h;constructor(s,e,r,i){const n=i?.strict??!1,o=i?.ignoreQueryParams??!0,u=s.isActiveRoute(e,r,n,o);this.#u=new c(u),this.#h=s.subscribe(i=>{const u=t(e,i.route.name),h=i.previousRoute&&t(e,i.previousRoute.name);if(!u&&!h)return;const c=!!u&&s.isActiveRoute(e,r,n,o);Object.is(this.#u.getSnapshot(),c)||this.#u.updateSnapshot(c)}),this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#u.subscribe(t)}getSnapshot(){return this.#u.getSnapshot()}destroy(){this.#h(),this.#u.destroy()}};function a(t,s,e,r){return new b(t,s,e,r)}var d={isTransitioning:!1,toRoute:null,fromRoute:null},p=class{#u;#c;constructor(t){this.#u=new c(d);const r=s(t),i=()=>{this.#u.updateSnapshot(d)};this.#c=[r.addEventListener(e.TRANSITION_START,(t,s)=>{this.#u.updateSnapshot({isTransitioning:!0,toRoute:t,fromRoute:s??null})}),r.addEventListener(e.TRANSITION_SUCCESS,i),r.addEventListener(e.TRANSITION_ERROR,i),r.addEventListener(e.TRANSITION_CANCEL,i)],this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#u.subscribe(t)}getSnapshot(){return this.#u.getSnapshot()}destroy(){this.#c.forEach(t=>{t()}),this.#u.destroy()}};function l(t){return new p(t)}export{a as createActiveRouteSource,h as createRouteNodeSource,i as createRouteSource,l as createTransitionSource};//# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/createRouteSource.ts","../../src/BaseSource.ts","../../src/computeSnapshot.ts","../../src/shouldUpdateCache.ts","../../src/createRouteNodeSource.ts","../../src/createActiveRouteSource.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { RouteSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/types\";\n\nclass RouteSource implements RouterSource<RouteSnapshot> {\n #routerUnsubscribe: (() => void) | null = null;\n #currentSnapshot: RouteSnapshot;\n\n readonly #listeners = new Set<() => void>();\n readonly #router: Router;\n\n constructor(router: Router) {\n this.#router = router;\n\n this.#currentSnapshot = {\n route: router.getState(),\n previousRoute: undefined,\n };\n\n this.subscribe = this.subscribe.bind(this);\n this.destroy = this.destroy.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#listeners.size === 0) {\n // Connect to router on first subscription\n this.#routerUnsubscribe = this.#router.subscribe((next) => {\n this.#currentSnapshot = {\n route: next.route,\n previousRoute: next.previousRoute,\n };\n this.#listeners.forEach((cb) => {\n cb();\n });\n });\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (this.#listeners.size === 0 && this.#routerUnsubscribe) {\n this.#routerUnsubscribe();\n this.#routerUnsubscribe = null;\n }\n };\n }\n\n getSnapshot(): RouteSnapshot {\n return this.#currentSnapshot;\n }\n\n destroy(): void {\n if (this.#routerUnsubscribe) {\n this.#routerUnsubscribe();\n this.#routerUnsubscribe = null;\n }\n\n this.#listeners.clear();\n }\n}\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 return new RouteSource(router);\n}\n","export class BaseSource<T> {\n #currentSnapshot: T;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n\n constructor(initialSnapshot: T) {\n this.#currentSnapshot = initialSnapshot;\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#destroyed) {\n return () => {};\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\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.#listeners.clear();\n }\n}\n","import type { RouteNodeSnapshot } from \"./types.js\";\nimport type { Router, SubscribeState } from \"@real-router/types\";\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 return { route, previousRoute };\n}\n","import type { Router, State } from \"@real-router/types\";\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/types\";\n\nclass RouteNodeSource implements RouterSource<RouteNodeSnapshot> {\n readonly #source: BaseSource<RouteNodeSnapshot>;\n readonly #unsubscribe: () => void;\n\n constructor(router: Router, nodeName: string) {\n const initialSnapshot: RouteNodeSnapshot = {\n route: undefined,\n previousRoute: undefined,\n };\n const computedInitial = computeSnapshot(initialSnapshot, router, nodeName);\n\n this.#source = new BaseSource(computedInitial);\n const shouldUpdate = getCachedShouldUpdate(router, nodeName);\n\n this.#unsubscribe = router.subscribe((next) => {\n if (!shouldUpdate(next.route, next.previousRoute)) {\n return;\n }\n\n const newSnapshot = computeSnapshot(\n this.#source.getSnapshot(),\n router,\n nodeName,\n next,\n );\n\n /* v8 ignore next 3 -- @preserve: dedup guard; shouldUpdateNode filters accurately so computeSnapshot always returns new ref */\n if (!Object.is(this.#source.getSnapshot(), newSnapshot)) {\n this.#source.updateSnapshot(newSnapshot);\n }\n });\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 return this.#source.subscribe(listener);\n }\n\n getSnapshot(): RouteNodeSnapshot {\n return this.#source.getSnapshot();\n }\n\n destroy(): void {\n this.#unsubscribe();\n this.#source.destroy();\n }\n}\n\nexport function createRouteNodeSource(\n router: Router,\n nodeName: string,\n): RouterSource<RouteNodeSnapshot> {\n return new RouteNodeSource(router, nodeName);\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/types\";\n\nclass ActiveRouteSource implements RouterSource<boolean> {\n readonly #source: BaseSource<boolean>;\n readonly #unsubscribe: () => void;\n\n constructor(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n ) {\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 this.#source = new BaseSource(initialValue);\n\n this.#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(this.#source.getSnapshot(), newValue)) {\n this.#source.updateSnapshot(newValue);\n }\n });\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 return this.#source.subscribe(listener);\n }\n\n getSnapshot(): boolean {\n return this.#source.getSnapshot();\n }\n\n destroy(): void {\n this.#unsubscribe();\n this.#source.destroy();\n }\n}\n\nexport function createActiveRouteSource(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n): RouterSource<boolean> {\n return new ActiveRouteSource(router, routeName, params, options);\n}\n"]}
1
+ {"version":3,"sources":["../../src/createRouteSource.ts","../../src/computeSnapshot.ts","../../src/shouldUpdateCache.ts","../../src/createRouteNodeSource.ts","../../src/BaseSource.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { RouteSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\nclass RouteSource implements RouterSource<RouteSnapshot> {\n #routerUnsubscribe: (() => void) | null = null;\n #currentSnapshot: RouteSnapshot;\n\n readonly #listeners = new Set<() => void>();\n readonly #router: Router;\n\n constructor(router: Router) {\n this.#router = router;\n\n this.#currentSnapshot = {\n route: router.getState(),\n previousRoute: undefined,\n };\n\n this.subscribe = this.subscribe.bind(this);\n this.destroy = this.destroy.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#listeners.size === 0) {\n // Connect to router on first subscription\n this.#routerUnsubscribe = this.#router.subscribe((next) => {\n this.#currentSnapshot = {\n route: next.route,\n previousRoute: next.previousRoute,\n };\n this.#listeners.forEach((cb) => {\n cb();\n });\n });\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (this.#listeners.size === 0 && this.#routerUnsubscribe) {\n this.#routerUnsubscribe();\n this.#routerUnsubscribe = null;\n }\n };\n }\n\n getSnapshot(): RouteSnapshot {\n return this.#currentSnapshot;\n }\n\n destroy(): void {\n if (this.#routerUnsubscribe) {\n this.#routerUnsubscribe();\n this.#routerUnsubscribe = null;\n }\n\n this.#listeners.clear();\n }\n}\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 return new RouteSource(router);\n}\n","import 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 return { route, previousRoute };\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 { 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\nclass RouteNodeSource implements RouterSource<RouteNodeSnapshot> {\n #routerUnsubscribe: (() => void) | null = null;\n #currentSnapshot: RouteNodeSnapshot;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n readonly #router: Router;\n readonly #nodeName: string;\n readonly #shouldUpdate: ReturnType<typeof getCachedShouldUpdate>;\n\n constructor(router: Router, nodeName: string) {\n this.#router = router;\n this.#nodeName = nodeName;\n this.#shouldUpdate = getCachedShouldUpdate(router, nodeName);\n\n const initialSnapshot: RouteNodeSnapshot = {\n route: undefined,\n previousRoute: undefined,\n };\n\n this.#currentSnapshot = computeSnapshot(initialSnapshot, router, nodeName);\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) {\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 this.#currentSnapshot = computeSnapshot(\n this.#currentSnapshot,\n this.#router,\n this.#nodeName,\n );\n\n // Connect to router on first subscription\n this.#routerUnsubscribe = this.#router.subscribe((next) => {\n if (!this.#shouldUpdate(next.route, next.previousRoute)) {\n return;\n }\n\n const newSnapshot = computeSnapshot(\n this.#currentSnapshot,\n this.#router,\n this.#nodeName,\n next,\n );\n\n /* v8 ignore next 3 -- @preserve: dedup guard; shouldUpdateNode filters accurately so computeSnapshot always returns new ref */\n if (!Object.is(this.#currentSnapshot, newSnapshot)) {\n this.#currentSnapshot = newSnapshot;\n this.#listeners.forEach((cb) => {\n cb();\n });\n }\n });\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (this.#listeners.size === 0 && this.#routerUnsubscribe) {\n this.#routerUnsubscribe();\n this.#routerUnsubscribe = null;\n }\n };\n }\n\n getSnapshot(): RouteNodeSnapshot {\n return this.#currentSnapshot;\n }\n\n destroy(): void {\n if (this.#destroyed) {\n return;\n }\n\n this.#destroyed = true;\n\n if (this.#routerUnsubscribe) {\n this.#routerUnsubscribe();\n this.#routerUnsubscribe = null;\n }\n\n this.#listeners.clear();\n }\n}\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 return new RouteNodeSource(router, nodeName);\n}\n","export class BaseSource<T> {\n #currentSnapshot: T;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n\n constructor(initialSnapshot: T) {\n this.#currentSnapshot = initialSnapshot;\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#destroyed) {\n return () => {};\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\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.#listeners.clear();\n }\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\nclass ActiveRouteSource implements RouterSource<boolean> {\n readonly #source: BaseSource<boolean>;\n readonly #unsubscribe: () => void;\n\n constructor(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n ) {\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 this.#source = new BaseSource(initialValue);\n\n this.#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(this.#source.getSnapshot(), newValue)) {\n this.#source.updateSnapshot(newValue);\n }\n });\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 return this.#source.subscribe(listener);\n }\n\n getSnapshot(): boolean {\n return this.#source.getSnapshot();\n }\n\n destroy(): void {\n this.#unsubscribe();\n this.#source.destroy();\n }\n}\n\nexport function createActiveRouteSource(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n): RouterSource<boolean> {\n return new ActiveRouteSource(router, routeName, params, options);\n}\n","import { getPluginApi, events } from \"@real-router/core\";\n\nimport { BaseSource } from \"./BaseSource\";\n\nimport type { RouterTransitionSnapshot, RouterSource } from \"./types.js\";\nimport type { Router, State } from \"@real-router/core\";\n\nconst IDLE_SNAPSHOT: RouterTransitionSnapshot = {\n isTransitioning: false,\n toRoute: null,\n fromRoute: null,\n};\n\nclass TransitionSource implements RouterSource<RouterTransitionSnapshot> {\n readonly #source: BaseSource<RouterTransitionSnapshot>;\n readonly #unsubs: (() => void)[];\n\n constructor(router: Router) {\n this.#source = new BaseSource(IDLE_SNAPSHOT);\n\n const api = getPluginApi(router);\n\n const resetToIdle = (): void => {\n this.#source.updateSnapshot(IDLE_SNAPSHOT);\n };\n\n this.#unsubs = [\n api.addEventListener(\n events.TRANSITION_START,\n (toState: State, fromState?: State) => {\n this.#source.updateSnapshot({\n isTransitioning: true,\n toRoute: toState,\n 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 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 return this.#source.subscribe(listener);\n }\n\n getSnapshot(): RouterTransitionSnapshot {\n return this.#source.getSnapshot();\n }\n\n destroy(): void {\n this.#unsubs.forEach((u) => {\n u();\n });\n this.#source.destroy();\n }\n}\n\nexport function createTransitionSource(\n router: Router,\n): RouterSource<RouterTransitionSnapshot> {\n return new TransitionSource(router);\n}\n"]}
@@ -1 +1 @@
1
- {"inputs":{"src/createRouteSource.ts":{"bytes":1957,"imports":[],"format":"esm"},"src/BaseSource.ts":{"bytes":941,"imports":[],"format":"esm"},"src/computeSnapshot.ts":{"bytes":791,"imports":[],"format":"esm"},"src/shouldUpdateCache.ts":{"bytes":611,"imports":[],"format":"esm"},"src/createRouteNodeSource.ts":{"bytes":1912,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/computeSnapshot.ts","kind":"import-statement","original":"./computeSnapshot.js"},{"path":"src/shouldUpdateCache.ts","kind":"import-statement","original":"./shouldUpdateCache.js"}],"format":"esm"},"src/createActiveRouteSource.ts":{"bytes":2125,"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"}],"format":"esm"},"src/index.ts":{"bytes":311,"imports":[{"path":"src/createRouteSource.ts","kind":"import-statement","original":"./createRouteSource"},{"path":"src/createRouteNodeSource.ts","kind":"import-statement","original":"./createRouteNodeSource"},{"path":"src/createActiveRouteSource.ts","kind":"import-statement","original":"./createActiveRouteSource"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":13042},"dist/esm/index.mjs":{"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true}],"exports":["createActiveRouteSource","createRouteNodeSource","createRouteSource"],"entryPoint":"src/index.ts","inputs":{"src/createRouteSource.ts":{"bytesInOutput":1303},"src/index.ts":{"bytesInOutput":0},"src/BaseSource.ts":{"bytesInOutput":752},"src/computeSnapshot.ts":{"bytesInOutput":536},"src/shouldUpdateCache.ts":{"bytesInOutput":425},"src/createRouteNodeSource.ts":{"bytesInOutput":1234},"src/createActiveRouteSource.ts":{"bytesInOutput":1490}},"bytes":6252}}}
1
+ {"inputs":{"src/createRouteSource.ts":{"bytes":1956,"imports":[],"format":"esm"},"src/computeSnapshot.ts":{"bytes":790,"imports":[],"format":"esm"},"src/shouldUpdateCache.ts":{"bytes":610,"imports":[],"format":"esm"},"src/createRouteNodeSource.ts":{"bytes":3347,"imports":[{"path":"src/computeSnapshot.ts","kind":"import-statement","original":"./computeSnapshot.js"},{"path":"src/shouldUpdateCache.ts","kind":"import-statement","original":"./shouldUpdateCache.js"}],"format":"esm"},"src/BaseSource.ts":{"bytes":941,"imports":[],"format":"esm"},"src/createActiveRouteSource.ts":{"bytes":2124,"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"}],"format":"esm"},"src/createTransitionSource.ts":{"bytes":1845,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"}],"format":"esm"},"src/index.ts":{"bytes":407,"imports":[{"path":"src/createRouteSource.ts","kind":"import-statement","original":"./createRouteSource"},{"path":"src/createRouteNodeSource.ts","kind":"import-statement","original":"./createRouteNodeSource"},{"path":"src/createActiveRouteSource.ts","kind":"import-statement","original":"./createActiveRouteSource"},{"path":"src/createTransitionSource.ts","kind":"import-statement","original":"./createTransitionSource"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":18008},"dist/esm/index.mjs":{"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["createActiveRouteSource","createRouteNodeSource","createRouteSource","createTransitionSource"],"entryPoint":"src/index.ts","inputs":{"src/createRouteSource.ts":{"bytesInOutput":1303},"src/index.ts":{"bytesInOutput":0},"src/computeSnapshot.ts":{"bytesInOutput":536},"src/shouldUpdateCache.ts":{"bytesInOutput":425},"src/createRouteNodeSource.ts":{"bytesInOutput":2089},"src/createActiveRouteSource.ts":{"bytesInOutput":1490},"src/BaseSource.ts":{"bytesInOutput":752},"src/createTransitionSource.ts":{"bytesInOutput":1367}},"bytes":8569}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/sources",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "type": "commonjs",
5
5
  "description": "Framework-agnostic subscription layer for Real-Router state",
6
6
  "main": "./dist/cjs/index.js",
@@ -43,12 +43,11 @@
43
43
  "homepage": "https://github.com/greydragon888/real-router",
44
44
  "sideEffects": false,
45
45
  "dependencies": {
46
- "@real-router/route-utils": "^0.1.4",
47
- "@real-router/types": "^0.23.0"
46
+ "@real-router/core": "^0.35.1",
47
+ "@real-router/route-utils": "^0.1.4"
48
48
  },
49
49
  "devDependencies": {
50
- "mitata": "1.0.34",
51
- "@real-router/core": "^0.35.0"
50
+ "mitata": "1.0.34"
52
51
  },
53
52
  "scripts": {
54
53
  "test": "vitest",
@@ -1,5 +1,5 @@
1
1
  import type { RouteNodeSnapshot } from "./types.js";
2
- import type { Router, SubscribeState } from "@real-router/types";
2
+ import type { Router, SubscribeState } from "@real-router/core";
3
3
 
4
4
  export function computeSnapshot(
5
5
  currentSnapshot: RouteNodeSnapshot,
@@ -3,7 +3,7 @@ import { areRoutesRelated } from "@real-router/route-utils";
3
3
  import { BaseSource } from "./BaseSource";
4
4
 
5
5
  import type { ActiveRouteSourceOptions, RouterSource } from "./types.js";
6
- import type { Params, Router } from "@real-router/types";
6
+ import type { Params, Router } from "@real-router/core";
7
7
 
8
8
  class ActiveRouteSource implements RouterSource<boolean> {
9
9
  readonly #source: BaseSource<boolean>;
@@ -1,41 +1,30 @@
1
- import { BaseSource } from "./BaseSource";
2
1
  import { computeSnapshot } from "./computeSnapshot.js";
3
2
  import { getCachedShouldUpdate } from "./shouldUpdateCache.js";
4
3
 
5
4
  import type { RouteNodeSnapshot, RouterSource } from "./types.js";
6
- import type { Router } from "@real-router/types";
5
+ import type { Router } from "@real-router/core";
7
6
 
8
7
  class RouteNodeSource implements RouterSource<RouteNodeSnapshot> {
9
- readonly #source: BaseSource<RouteNodeSnapshot>;
10
- readonly #unsubscribe: () => void;
8
+ #routerUnsubscribe: (() => void) | null = null;
9
+ #currentSnapshot: RouteNodeSnapshot;
10
+ #destroyed = false;
11
+
12
+ readonly #listeners = new Set<() => void>();
13
+ readonly #router: Router;
14
+ readonly #nodeName: string;
15
+ readonly #shouldUpdate: ReturnType<typeof getCachedShouldUpdate>;
11
16
 
12
17
  constructor(router: Router, nodeName: string) {
18
+ this.#router = router;
19
+ this.#nodeName = nodeName;
20
+ this.#shouldUpdate = getCachedShouldUpdate(router, nodeName);
21
+
13
22
  const initialSnapshot: RouteNodeSnapshot = {
14
23
  route: undefined,
15
24
  previousRoute: undefined,
16
25
  };
17
- const computedInitial = computeSnapshot(initialSnapshot, router, nodeName);
18
-
19
- this.#source = new BaseSource(computedInitial);
20
- const shouldUpdate = getCachedShouldUpdate(router, nodeName);
21
26
 
22
- this.#unsubscribe = router.subscribe((next) => {
23
- if (!shouldUpdate(next.route, next.previousRoute)) {
24
- return;
25
- }
26
-
27
- const newSnapshot = computeSnapshot(
28
- this.#source.getSnapshot(),
29
- router,
30
- nodeName,
31
- next,
32
- );
33
-
34
- /* v8 ignore next 3 -- @preserve: dedup guard; shouldUpdateNode filters accurately so computeSnapshot always returns new ref */
35
- if (!Object.is(this.#source.getSnapshot(), newSnapshot)) {
36
- this.#source.updateSnapshot(newSnapshot);
37
- }
38
- });
27
+ this.#currentSnapshot = computeSnapshot(initialSnapshot, router, nodeName);
39
28
 
40
29
  this.subscribe = this.subscribe.bind(this);
41
30
  this.getSnapshot = this.getSnapshot.bind(this);
@@ -43,19 +32,82 @@ class RouteNodeSource implements RouterSource<RouteNodeSnapshot> {
43
32
  }
44
33
 
45
34
  subscribe(listener: () => void): () => void {
46
- return this.#source.subscribe(listener);
35
+ if (this.#destroyed) {
36
+ return () => {};
37
+ }
38
+
39
+ if (this.#listeners.size === 0) {
40
+ // Reconcile snapshot with current router state before connecting.
41
+ // Covers reconnection after Activity hide/show cycles where the
42
+ // source was disconnected and missed navigation events.
43
+ this.#currentSnapshot = computeSnapshot(
44
+ this.#currentSnapshot,
45
+ this.#router,
46
+ this.#nodeName,
47
+ );
48
+
49
+ // Connect to router on first subscription
50
+ this.#routerUnsubscribe = this.#router.subscribe((next) => {
51
+ if (!this.#shouldUpdate(next.route, next.previousRoute)) {
52
+ return;
53
+ }
54
+
55
+ const newSnapshot = computeSnapshot(
56
+ this.#currentSnapshot,
57
+ this.#router,
58
+ this.#nodeName,
59
+ next,
60
+ );
61
+
62
+ /* v8 ignore next 3 -- @preserve: dedup guard; shouldUpdateNode filters accurately so computeSnapshot always returns new ref */
63
+ if (!Object.is(this.#currentSnapshot, newSnapshot)) {
64
+ this.#currentSnapshot = newSnapshot;
65
+ this.#listeners.forEach((cb) => {
66
+ cb();
67
+ });
68
+ }
69
+ });
70
+ }
71
+
72
+ this.#listeners.add(listener);
73
+
74
+ return () => {
75
+ this.#listeners.delete(listener);
76
+
77
+ if (this.#listeners.size === 0 && this.#routerUnsubscribe) {
78
+ this.#routerUnsubscribe();
79
+ this.#routerUnsubscribe = null;
80
+ }
81
+ };
47
82
  }
48
83
 
49
84
  getSnapshot(): RouteNodeSnapshot {
50
- return this.#source.getSnapshot();
85
+ return this.#currentSnapshot;
51
86
  }
52
87
 
53
88
  destroy(): void {
54
- this.#unsubscribe();
55
- this.#source.destroy();
89
+ if (this.#destroyed) {
90
+ return;
91
+ }
92
+
93
+ this.#destroyed = true;
94
+
95
+ if (this.#routerUnsubscribe) {
96
+ this.#routerUnsubscribe();
97
+ this.#routerUnsubscribe = null;
98
+ }
99
+
100
+ this.#listeners.clear();
56
101
  }
57
102
  }
58
103
 
104
+ /**
105
+ * Creates a source scoped to a specific route node.
106
+ *
107
+ * Uses a lazy-connection pattern: the router subscription is created when the
108
+ * first listener subscribes and removed when the last listener unsubscribes.
109
+ * This is compatible with React's useSyncExternalStore and Strict Mode.
110
+ */
59
111
  export function createRouteNodeSource(
60
112
  router: Router,
61
113
  nodeName: string,
@@ -1,5 +1,5 @@
1
1
  import type { RouteSnapshot, RouterSource } from "./types.js";
2
- import type { Router } from "@real-router/types";
2
+ import type { Router } from "@real-router/core";
3
3
 
4
4
  class RouteSource implements RouterSource<RouteSnapshot> {
5
5
  #routerUnsubscribe: (() => void) | null = null;
@@ -0,0 +1,68 @@
1
+ import { getPluginApi, events } from "@real-router/core";
2
+
3
+ import { BaseSource } from "./BaseSource";
4
+
5
+ import type { RouterTransitionSnapshot, RouterSource } from "./types.js";
6
+ import type { Router, State } from "@real-router/core";
7
+
8
+ const IDLE_SNAPSHOT: RouterTransitionSnapshot = {
9
+ isTransitioning: false,
10
+ toRoute: null,
11
+ fromRoute: null,
12
+ };
13
+
14
+ class TransitionSource implements RouterSource<RouterTransitionSnapshot> {
15
+ readonly #source: BaseSource<RouterTransitionSnapshot>;
16
+ readonly #unsubs: (() => void)[];
17
+
18
+ constructor(router: Router) {
19
+ this.#source = new BaseSource(IDLE_SNAPSHOT);
20
+
21
+ const api = getPluginApi(router);
22
+
23
+ const resetToIdle = (): void => {
24
+ this.#source.updateSnapshot(IDLE_SNAPSHOT);
25
+ };
26
+
27
+ this.#unsubs = [
28
+ api.addEventListener(
29
+ events.TRANSITION_START,
30
+ (toState: State, fromState?: State) => {
31
+ this.#source.updateSnapshot({
32
+ isTransitioning: true,
33
+ toRoute: toState,
34
+ fromRoute: fromState ?? null,
35
+ });
36
+ },
37
+ ),
38
+ api.addEventListener(events.TRANSITION_SUCCESS, resetToIdle),
39
+ api.addEventListener(events.TRANSITION_ERROR, resetToIdle),
40
+ api.addEventListener(events.TRANSITION_CANCEL, resetToIdle),
41
+ ];
42
+
43
+ this.subscribe = this.subscribe.bind(this);
44
+ this.getSnapshot = this.getSnapshot.bind(this);
45
+ this.destroy = this.destroy.bind(this);
46
+ }
47
+
48
+ subscribe(listener: () => void): () => void {
49
+ return this.#source.subscribe(listener);
50
+ }
51
+
52
+ getSnapshot(): RouterTransitionSnapshot {
53
+ return this.#source.getSnapshot();
54
+ }
55
+
56
+ destroy(): void {
57
+ this.#unsubs.forEach((u) => {
58
+ u();
59
+ });
60
+ this.#source.destroy();
61
+ }
62
+ }
63
+
64
+ export function createTransitionSource(
65
+ router: Router,
66
+ ): RouterSource<RouterTransitionSnapshot> {
67
+ return new TransitionSource(router);
68
+ }
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ export type {
3
3
  RouteSnapshot,
4
4
  RouteNodeSnapshot,
5
5
  ActiveRouteSourceOptions,
6
+ RouterTransitionSnapshot,
6
7
  } from "./types.js";
7
8
 
8
9
  export { createRouteSource } from "./createRouteSource";
@@ -10,3 +11,5 @@ export { createRouteSource } from "./createRouteSource";
10
11
  export { createRouteNodeSource } from "./createRouteNodeSource";
11
12
 
12
13
  export { createActiveRouteSource } from "./createActiveRouteSource";
14
+
15
+ export { createTransitionSource } from "./createTransitionSource";
@@ -1,4 +1,4 @@
1
- import type { Router, State } from "@real-router/types";
1
+ import type { Router, State } from "@real-router/core";
2
2
 
3
3
  const shouldUpdateCache = new WeakMap<
4
4
  Router,
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { State } from "@real-router/types";
1
+ import type { State } from "@real-router/core";
2
2
 
3
3
  export interface RouteSnapshot {
4
4
  route: State | undefined;
@@ -20,3 +20,9 @@ export interface ActiveRouteSourceOptions {
20
20
  strict?: boolean;
21
21
  ignoreQueryParams?: boolean;
22
22
  }
23
+
24
+ export interface RouterTransitionSnapshot {
25
+ isTransitioning: boolean;
26
+ toRoute: State | null;
27
+ fromRoute: State | null;
28
+ }