@real-router/sources 0.2.6 → 0.2.8
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/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/metafile-cjs.json +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/metafile-esm.json +1 -1
- package/package.json +4 -3
- package/src/computeSnapshot.ts +16 -1
- package/src/createRouteNodeSource.ts +8 -3
- package/src/createRouteSource.ts +17 -4
- package/src/createTransitionSource.ts +16 -5
- package/src/stabilizeState.ts +31 -0
package/dist/cjs/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var t=require("@real-router/route-utils"),e=require("@real-router/core"),s=require("@real-router/core/api"),o=class{#t;#e=!1;#s=new Set;#o;#r;#n;constructor(t,e){this.#t=t,this.#o=e?.onFirstSubscribe,this.#r=e?.onLastUnsubscribe,this.#n=e?.onDestroy,this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#e?()=>{}:(0===this.#s.size&&this.#o&&this.#o(),this.#s.add(t),()=>{this.#s.delete(t),!this.#e&&0===this.#s.size&&this.#r&&this.#r()})}getSnapshot(){return this.#t}updateSnapshot(t){this.#e||(this.#t=t,this.#s.forEach(t=>{t()}))}destroy(){this.#e||(this.#e=!0,this.#n?.(),this.#s.clear())}};function r(t,e){return t===e?t:t?.path!==e?.path?e:t}function n(t,e,s,o){const n=o?.route??e.getState(),i=o?.previousRoute,u=""===s||void 0!==n&&(n.name===s||n.name.startsWith(`${s}.`))?n:void 0;if(u===t.route&&i===t.previousRoute)return t;const a=r(t.route,u),c=r(t.previousRoute,i);return a===t.route&&c===t.previousRoute?t:{route:a,previousRoute:c}}var i=new WeakMap,u={isTransitioning:!1,toRoute:null,fromRoute:null};exports.createActiveRouteSource=function(e,s,r,n){const i=n?.strict??!1,u=n?.ignoreQueryParams??!0,a=e.isActiveRoute(s,r,i,u),c=new o(a,{onDestroy:()=>{h()}}),h=e.subscribe(o=>{const n=t.areRoutesRelated(s,o.route.name),a=o.previousRoute&&t.areRoutesRelated(s,o.previousRoute.name);if(!n&&!a)return;const h=!!n&&e.isActiveRoute(s,r,i,u);Object.is(c.getSnapshot(),h)||c.updateSnapshot(h)});return c},exports.createRouteNodeSource=function(t,e){let s=null;const r=function(t,e){let s=i.get(t);s||(s=new Map,i.set(t,s));let o=s.get(e);return o||(o=t.shouldUpdateNode(e),s.set(e,o)),o}(t,e),u=()=>{const t=s;s=null,t?.()},a=new o(n({route:void 0,previousRoute:void 0},t,e),{onFirstSubscribe:()=>{const o=n(a.getSnapshot(),t,e);Object.is(o,a.getSnapshot())||a.updateSnapshot(o),s=t.subscribe(s=>{if(!r(s.route,s.previousRoute))return;const o=n(a.getSnapshot(),t,e,s);Object.is(a.getSnapshot(),o)||a.updateSnapshot(o)})},onLastUnsubscribe:u,onDestroy:u});return a},exports.createRouteSource=function(t){let e=null;const s=()=>{const t=e;e=null,t?.()},n=new o({route:t.getState(),previousRoute:void 0},{onFirstSubscribe:()=>{e=t.subscribe(t=>{const e=n.getSnapshot(),s=r(e.route,t.route),o=r(e.previousRoute,t.previousRoute);s===e.route&&o===e.previousRoute||n.updateSnapshot({route:s,previousRoute:o})})},onLastUnsubscribe:s,onDestroy:s});return n},exports.createTransitionSource=function(t){const n=new o(u,{onDestroy:()=>{c.forEach(t=>{t()})}}),i=s.getPluginApi(t),a=()=>{n.updateSnapshot(u)},c=[i.addEventListener(e.events.TRANSITION_START,(t,e)=>{const s=n.getSnapshot(),o=r(s.toRoute,t),i=r(s.fromRoute,e??null);s.isTransitioning&&o===s.toRoute&&i===s.fromRoute||n.updateSnapshot({isTransitioning:!0,toRoute:o,fromRoute:i})}),i.addEventListener(e.events.TRANSITION_SUCCESS,a),i.addEventListener(e.events.TRANSITION_ERROR,a),i.addEventListener(e.events.TRANSITION_CANCEL,a)];return n};//# sourceMappingURL=index.js.map
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/BaseSource.ts","../../src/createRouteSource.ts","../../src/computeSnapshot.ts","../../src/shouldUpdateCache.ts","../../src/createRouteNodeSource.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts"],"names":["areRoutesRelated","api","getPluginApi","events"],"mappings":"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","file":"index.js","sourcesContent":["export interface BaseSourceOptions {\n onFirstSubscribe?: () => void;\n onLastUnsubscribe?: () => void;\n onDestroy?: () => void;\n}\n\nexport class BaseSource<T> {\n #currentSnapshot: T;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n readonly #onFirstSubscribe: (() => void) | undefined;\n readonly #onLastUnsubscribe: (() => void) | undefined;\n readonly #onDestroy: (() => void) | undefined;\n\n constructor(initialSnapshot: T, options?: BaseSourceOptions) {\n this.#currentSnapshot = initialSnapshot;\n this.#onFirstSubscribe = options?.onFirstSubscribe;\n this.#onLastUnsubscribe = options?.onLastUnsubscribe;\n this.#onDestroy = options?.onDestroy;\n\n this.subscribe = this.subscribe.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n this.destroy = this.destroy.bind(this);\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#destroyed) {\n return () => {};\n }\n\n if (this.#listeners.size === 0 && this.#onFirstSubscribe) {\n this.#onFirstSubscribe();\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (\n !this.#destroyed &&\n this.#listeners.size === 0 &&\n this.#onLastUnsubscribe\n ) {\n this.#onLastUnsubscribe();\n }\n };\n }\n\n getSnapshot(): T {\n return this.#currentSnapshot;\n }\n\n updateSnapshot(snapshot: T): void {\n /* v8 ignore next 2 -- @preserve: defensive guard unreachable via public API (destroy() removes router subscription first) */\n if (this.#destroyed) {\n return;\n }\n\n this.#currentSnapshot = snapshot;\n this.#listeners.forEach((listener) => {\n listener();\n });\n }\n\n destroy(): void {\n if (this.#destroyed) {\n return;\n }\n\n this.#destroyed = true;\n this.#onDestroy?.();\n this.#listeners.clear();\n }\n}\n","import { BaseSource } from \"./BaseSource\";\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 source.updateSnapshot({\n route: next.route,\n previousRoute: next.previousRoute,\n });\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\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 { 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 source.updateSnapshot(\n computeSnapshot(source.getSnapshot(), router, nodeName),\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 /* v8 ignore next 3 -- @preserve: dedup guard; shouldUpdateNode filters accurately so computeSnapshot always returns new ref */\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\";\n\nimport type { RouterTransitionSnapshot, RouterSource } from \"./types.js\";\nimport type { Router, State } from \"@real-router/core\";\n\nconst IDLE_SNAPSHOT: RouterTransitionSnapshot = {\n isTransitioning: false,\n toRoute: null,\n fromRoute: null,\n};\n\nexport function createTransitionSource(\n router: Router,\n): RouterSource<RouterTransitionSnapshot> {\n const source = new BaseSource(IDLE_SNAPSHOT, {\n onDestroy: () => {\n unsubs.forEach((unsub) => {\n unsub();\n });\n },\n });\n\n const api = getPluginApi(router);\n\n const resetToIdle = (): void => {\n source.updateSnapshot(IDLE_SNAPSHOT);\n };\n\n // Eager connection: subscribe to router events immediately\n const unsubs = [\n api.addEventListener(\n events.TRANSITION_START,\n (toState: State, fromState?: State) => {\n 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 return source;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/BaseSource.ts","../../src/stabilizeState.ts","../../src/createRouteSource.ts","../../src/computeSnapshot.ts","../../src/shouldUpdateCache.ts","../../src/createRouteNodeSource.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts"],"names":["areRoutesRelated","api","getPluginApi","events"],"mappings":"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","file":"index.js","sourcesContent":["export interface BaseSourceOptions {\n onFirstSubscribe?: () => void;\n onLastUnsubscribe?: () => void;\n onDestroy?: () => void;\n}\n\nexport class BaseSource<T> {\n #currentSnapshot: T;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n readonly #onFirstSubscribe: (() => void) | undefined;\n readonly #onLastUnsubscribe: (() => void) | undefined;\n readonly #onDestroy: (() => void) | undefined;\n\n constructor(initialSnapshot: T, options?: BaseSourceOptions) {\n this.#currentSnapshot = initialSnapshot;\n this.#onFirstSubscribe = options?.onFirstSubscribe;\n this.#onLastUnsubscribe = options?.onLastUnsubscribe;\n this.#onDestroy = options?.onDestroy;\n\n this.subscribe = this.subscribe.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n this.destroy = this.destroy.bind(this);\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#destroyed) {\n return () => {};\n }\n\n if (this.#listeners.size === 0 && this.#onFirstSubscribe) {\n this.#onFirstSubscribe();\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (\n !this.#destroyed &&\n this.#listeners.size === 0 &&\n this.#onLastUnsubscribe\n ) {\n this.#onLastUnsubscribe();\n }\n };\n }\n\n getSnapshot(): T {\n return this.#currentSnapshot;\n }\n\n updateSnapshot(snapshot: T): void {\n /* v8 ignore next 2 -- @preserve: defensive guard unreachable via public API (destroy() removes router subscription first) */\n if (this.#destroyed) {\n return;\n }\n\n this.#currentSnapshot = snapshot;\n this.#listeners.forEach((listener) => {\n listener();\n });\n }\n\n destroy(): void {\n if (this.#destroyed) {\n return;\n }\n\n this.#destroyed = true;\n this.#onDestroy?.();\n this.#listeners.clear();\n }\n}\n","import type { State } from \"@real-router/core\";\n\n/**\n * State-aware stabilization for route snapshots.\n *\n * Compares `path` — the canonical representation of rendering-relevant\n * State fields (name + params). When path matches, returns `prev`\n * (preserving reference), enabling frameworks to skip re-renders.\n *\n * Ignores `meta` (internal: auto-increment id) and `transition`\n * (reference data: from, segments, reload) — they don't affect\n * what is rendered.\n *\n * Accepts `null` for compatibility with `RouterTransitionSnapshot`\n * (toRoute/fromRoute are `State | null`).\n *\n * @internal Not exported from package public API.\n */\nexport function stabilizeState<T extends State | null | undefined>(\n prev: T,\n next: T,\n): T {\n if (prev === next) {\n return prev;\n }\n if (prev?.path !== next?.path) {\n return next;\n }\n\n return prev;\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouteSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\n/**\n * Creates a source for the full route state.\n *\n * Uses a lazy-connection pattern: the router subscription is created when the\n * first listener subscribes and removed when the last listener unsubscribes.\n * This is compatible with React's useSyncExternalStore and Strict Mode.\n */\nexport function createRouteSource(router: Router): RouterSource<RouteSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const source = new BaseSource<RouteSnapshot>(\n {\n route: router.getState(),\n previousRoute: undefined,\n },\n {\n onFirstSubscribe: () => {\n routerUnsubscribe = router.subscribe((next) => {\n const prev = source.getSnapshot();\n const newRoute = stabilizeState(prev.route, next.route);\n const newPreviousRoute = stabilizeState(\n prev.previousRoute,\n next.previousRoute,\n );\n\n if (\n newRoute !== prev.route ||\n newPreviousRoute !== prev.previousRoute\n ) {\n source.updateSnapshot({\n route: newRoute,\n previousRoute: newPreviousRoute,\n });\n }\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\n}\n","import { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouteNodeSnapshot } from \"./types.js\";\nimport type { Router, SubscribeState } from \"@real-router/core\";\n\nexport function computeSnapshot(\n currentSnapshot: RouteNodeSnapshot,\n router: Router,\n nodeName: string,\n next?: SubscribeState,\n): RouteNodeSnapshot {\n const currentRoute = next?.route ?? router.getState();\n const previousRoute = next?.previousRoute;\n\n const isNodeActive =\n nodeName === \"\" ||\n (currentRoute !== undefined &&\n (currentRoute.name === nodeName ||\n currentRoute.name.startsWith(`${nodeName}.`)));\n\n const route = isNodeActive ? currentRoute : undefined;\n\n if (\n route === currentSnapshot.route &&\n previousRoute === currentSnapshot.previousRoute\n ) {\n return currentSnapshot;\n }\n\n const newRoute = stabilizeState(currentSnapshot.route, route);\n const newPreviousRoute = stabilizeState(\n currentSnapshot.previousRoute,\n previousRoute,\n );\n\n if (\n newRoute === currentSnapshot.route &&\n newPreviousRoute === currentSnapshot.previousRoute\n ) {\n return currentSnapshot;\n }\n\n return { route: newRoute, previousRoute: newPreviousRoute };\n}\n","import type { Router, State } from \"@real-router/core\";\n\nconst shouldUpdateCache = new WeakMap<\n Router,\n Map<string, (toState: State, fromState?: State) => boolean>\n>();\n\nexport function getCachedShouldUpdate(\n router: Router,\n nodeName: string,\n): (toState: State, fromState?: State) => boolean {\n let routerCache = shouldUpdateCache.get(router);\n\n if (!routerCache) {\n routerCache = new Map();\n shouldUpdateCache.set(router, routerCache);\n }\n\n let fn = routerCache.get(nodeName);\n\n if (!fn) {\n fn = router.shouldUpdateNode(nodeName);\n routerCache.set(nodeName, fn);\n }\n\n return fn;\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { computeSnapshot } from \"./computeSnapshot.js\";\nimport { getCachedShouldUpdate } from \"./shouldUpdateCache.js\";\n\nimport type { RouteNodeSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\n/**\n * Creates a source scoped to a specific route node.\n *\n * Uses a lazy-connection pattern: the router subscription is created when the\n * first listener subscribes and removed when the last listener unsubscribes.\n * This is compatible with React's useSyncExternalStore and Strict Mode.\n */\nexport function createRouteNodeSource(\n router: Router,\n nodeName: string,\n): RouterSource<RouteNodeSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n const shouldUpdate = getCachedShouldUpdate(router, nodeName);\n\n const initialSnapshot: RouteNodeSnapshot = {\n route: undefined,\n previousRoute: undefined,\n };\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const source = new BaseSource<RouteNodeSnapshot>(\n computeSnapshot(initialSnapshot, router, nodeName),\n {\n onFirstSubscribe: () => {\n // Reconcile snapshot with current router state before connecting.\n // Covers reconnection after Activity hide/show cycles where the\n // source was disconnected and missed navigation events.\n const reconciled = computeSnapshot(\n source.getSnapshot(),\n router,\n nodeName,\n );\n\n if (!Object.is(reconciled, source.getSnapshot())) {\n source.updateSnapshot(reconciled);\n }\n\n // Connect to router on first subscription\n routerUnsubscribe = router.subscribe((next) => {\n if (!shouldUpdate(next.route, next.previousRoute)) {\n return;\n }\n\n const newSnapshot = computeSnapshot(\n source.getSnapshot(),\n router,\n nodeName,\n next,\n );\n\n if (!Object.is(source.getSnapshot(), newSnapshot)) {\n source.updateSnapshot(newSnapshot);\n }\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\n}\n","import { areRoutesRelated } from \"@real-router/route-utils\";\n\nimport { BaseSource } from \"./BaseSource\";\n\nimport type { ActiveRouteSourceOptions, RouterSource } from \"./types.js\";\nimport type { Params, Router } from \"@real-router/core\";\n\nexport function createActiveRouteSource(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n): RouterSource<boolean> {\n const strict = options?.strict ?? false;\n const ignoreQueryParams = options?.ignoreQueryParams ?? true;\n\n const initialValue = router.isActiveRoute(\n routeName,\n params,\n strict,\n ignoreQueryParams,\n );\n\n const source = new BaseSource(initialValue, {\n onDestroy: () => {\n unsubscribe();\n },\n });\n\n // Eager connection: subscribe to router immediately\n const unsubscribe = router.subscribe((next) => {\n const isNewRelated = areRoutesRelated(routeName, next.route.name);\n const isPrevRelated =\n next.previousRoute &&\n areRoutesRelated(routeName, next.previousRoute.name);\n\n if (!isNewRelated && !isPrevRelated) {\n return;\n }\n\n // If new route is not related, we know the route is inactive —\n // avoid calling isActiveRoute for the optimization\n const newValue = isNewRelated\n ? router.isActiveRoute(routeName, params, strict, ignoreQueryParams)\n : false;\n\n if (!Object.is(source.getSnapshot(), newValue)) {\n source.updateSnapshot(newValue);\n }\n });\n\n return source;\n}\n","import { events } from \"@real-router/core\";\nimport { getPluginApi } from \"@real-router/core/api\";\n\nimport { BaseSource } from \"./BaseSource\";\nimport { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouterTransitionSnapshot, RouterSource } from \"./types.js\";\nimport type { Router, State } from \"@real-router/core\";\n\nconst IDLE_SNAPSHOT: RouterTransitionSnapshot = {\n isTransitioning: false,\n toRoute: null,\n fromRoute: null,\n};\n\nexport function createTransitionSource(\n router: Router,\n): RouterSource<RouterTransitionSnapshot> {\n const source = new BaseSource(IDLE_SNAPSHOT, {\n onDestroy: () => {\n unsubs.forEach((unsub) => {\n unsub();\n });\n },\n });\n\n const api = getPluginApi(router);\n\n const resetToIdle = (): void => {\n source.updateSnapshot(IDLE_SNAPSHOT);\n };\n\n // Eager connection: subscribe to router events immediately\n const unsubs = [\n api.addEventListener(\n events.TRANSITION_START,\n (toState: State, fromState?: State) => {\n const prev = source.getSnapshot();\n const newToRoute = stabilizeState(prev.toRoute, toState);\n const newFromRoute = stabilizeState(prev.fromRoute, fromState ?? null);\n\n if (\n !prev.isTransitioning ||\n newToRoute !== prev.toRoute ||\n newFromRoute !== prev.fromRoute\n ) {\n source.updateSnapshot({\n isTransitioning: true,\n toRoute: newToRoute,\n fromRoute: newFromRoute,\n });\n }\n },\n ),\n api.addEventListener(events.TRANSITION_SUCCESS, resetToIdle),\n api.addEventListener(events.TRANSITION_ERROR, resetToIdle),\n api.addEventListener(events.TRANSITION_CANCEL, resetToIdle),\n ];\n\n return source;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/BaseSource.ts":{"bytes":1857,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createRouteSource.ts":{"bytes":
|
|
1
|
+
{"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/BaseSource.ts":{"bytes":1857,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/stabilizeState.ts":{"bytes":856,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createRouteSource.ts":{"bytes":1599,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/computeSnapshot.ts":{"bytes":1184,"imports":[{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/shouldUpdateCache.ts":{"bytes":610,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createRouteNodeSource.ts":{"bytes":2239,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/computeSnapshot.ts","kind":"import-statement","original":"./computeSnapshot.js"},{"path":"src/shouldUpdateCache.ts","kind":"import-statement","original":"./shouldUpdateCache.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createActiveRouteSource.ts":{"bytes":1461,"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/createTransitionSource.ts":{"bytes":1731,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":407,"imports":[{"path":"src/createRouteSource.ts","kind":"import-statement","original":"./createRouteSource"},{"path":"src/createRouteNodeSource.ts","kind":"import-statement","original":"./createRouteNodeSource"},{"path":"src/createActiveRouteSource.ts","kind":"import-statement","original":"./createActiveRouteSource"},{"path":"src/createTransitionSource.ts","kind":"import-statement","original":"./createTransitionSource"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":17153},"dist/cjs/index.js":{"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true}],"exports":["createActiveRouteSource","createRouteNodeSource","createRouteSource","createTransitionSource"],"entryPoint":"src/index.ts","inputs":{"src/BaseSource.ts":{"bytesInOutput":1375},"src/stabilizeState.ts":{"bytesInOutput":155},"src/createRouteSource.ts":{"bytesInOutput":966},"src/index.ts":{"bytesInOutput":0},"src/computeSnapshot.ts":{"bytesInOutput":860},"src/shouldUpdateCache.ts":{"bytesInOutput":425},"src/createRouteNodeSource.ts":{"bytesInOutput":1251},"src/createActiveRouteSource.ts":{"bytesInOutput":977},"src/createTransitionSource.ts":{"bytesInOutput":1279}},"bytes":7758}}}
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{areRoutesRelated as t}from"@real-router/route-utils";import{events as e}from"@real-router/core";import{getPluginApi as s}from"@real-router/core/api";var o=class{#t;#e=!1;#s=new Set;#o;#r;#n;constructor(t,e){this.#t=t,this.#o=e?.onFirstSubscribe,this.#r=e?.onLastUnsubscribe,this.#n=e?.onDestroy,this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#e?()=>{}:(0===this.#s.size&&this.#o&&this.#o(),this.#s.add(t),()=>{this.#s.delete(t),!this.#e&&0===this.#s.size&&this.#r&&this.#r()})}getSnapshot(){return this.#t}updateSnapshot(t){this.#e||(this.#t=t,this.#s.forEach(t=>{t()}))}destroy(){this.#e||(this.#e=!0,this.#n?.(),this.#s.clear())}};function r(t){let e=null;const s=()=>{const t=e;e=null,t?.()},
|
|
1
|
+
import{areRoutesRelated as t}from"@real-router/route-utils";import{events as e}from"@real-router/core";import{getPluginApi as s}from"@real-router/core/api";var o=class{#t;#e=!1;#s=new Set;#o;#r;#n;constructor(t,e){this.#t=t,this.#o=e?.onFirstSubscribe,this.#r=e?.onLastUnsubscribe,this.#n=e?.onDestroy,this.subscribe=this.subscribe.bind(this),this.getSnapshot=this.getSnapshot.bind(this),this.destroy=this.destroy.bind(this)}subscribe(t){return this.#e?()=>{}:(0===this.#s.size&&this.#o&&this.#o(),this.#s.add(t),()=>{this.#s.delete(t),!this.#e&&0===this.#s.size&&this.#r&&this.#r()})}getSnapshot(){return this.#t}updateSnapshot(t){this.#e||(this.#t=t,this.#s.forEach(t=>{t()}))}destroy(){this.#e||(this.#e=!0,this.#n?.(),this.#s.clear())}};function r(t,e){return t===e?t:t?.path!==e?.path?e:t}function n(t){let e=null;const s=()=>{const t=e;e=null,t?.()},n=new o({route:t.getState(),previousRoute:void 0},{onFirstSubscribe:()=>{e=t.subscribe(t=>{const e=n.getSnapshot(),s=r(e.route,t.route),o=r(e.previousRoute,t.previousRoute);s===e.route&&o===e.previousRoute||n.updateSnapshot({route:s,previousRoute:o})})},onLastUnsubscribe:s,onDestroy:s});return n}function i(t,e,s,o){const n=o?.route??e.getState(),i=o?.previousRoute,u=""===s||void 0!==n&&(n.name===s||n.name.startsWith(`${s}.`))?n:void 0;if(u===t.route&&i===t.previousRoute)return t;const a=r(t.route,u),c=r(t.previousRoute,i);return a===t.route&&c===t.previousRoute?t:{route:a,previousRoute:c}}var u=new WeakMap;function a(t,e){let s=null;const r=function(t,e){let s=u.get(t);s||(s=new Map,u.set(t,s));let o=s.get(e);return o||(o=t.shouldUpdateNode(e),s.set(e,o)),o}(t,e),n=()=>{const t=s;s=null,t?.()},a=new o(i({route:void 0,previousRoute:void 0},t,e),{onFirstSubscribe:()=>{const o=i(a.getSnapshot(),t,e);Object.is(o,a.getSnapshot())||a.updateSnapshot(o),s=t.subscribe(s=>{if(!r(s.route,s.previousRoute))return;const o=i(a.getSnapshot(),t,e,s);Object.is(a.getSnapshot(),o)||a.updateSnapshot(o)})},onLastUnsubscribe:n,onDestroy:n});return a}function c(e,s,r,n){const i=n?.strict??!1,u=n?.ignoreQueryParams??!0,a=e.isActiveRoute(s,r,i,u),c=new o(a,{onDestroy:()=>{h()}}),h=e.subscribe(o=>{const n=t(s,o.route.name),a=o.previousRoute&&t(s,o.previousRoute.name);if(!n&&!a)return;const h=!!n&&e.isActiveRoute(s,r,i,u);Object.is(c.getSnapshot(),h)||c.updateSnapshot(h)});return c}var h={isTransitioning:!1,toRoute:null,fromRoute:null};function p(t){const n=new o(h,{onDestroy:()=>{a.forEach(t=>{t()})}}),i=s(t),u=()=>{n.updateSnapshot(h)},a=[i.addEventListener(e.TRANSITION_START,(t,e)=>{const s=n.getSnapshot(),o=r(s.toRoute,t),i=r(s.fromRoute,e??null);s.isTransitioning&&o===s.toRoute&&i===s.fromRoute||n.updateSnapshot({isTransitioning:!0,toRoute:o,fromRoute:i})}),i.addEventListener(e.TRANSITION_SUCCESS,u),i.addEventListener(e.TRANSITION_ERROR,u),i.addEventListener(e.TRANSITION_CANCEL,u)];return n}export{c as createActiveRouteSource,a as createRouteNodeSource,n as createRouteSource,p as createTransitionSource};//# sourceMappingURL=index.mjs.map
|
package/dist/esm/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/BaseSource.ts","../../src/createRouteSource.ts","../../src/computeSnapshot.ts","../../src/shouldUpdateCache.ts","../../src/createRouteNodeSource.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["export interface BaseSourceOptions {\n onFirstSubscribe?: () => void;\n onLastUnsubscribe?: () => void;\n onDestroy?: () => void;\n}\n\nexport class BaseSource<T> {\n #currentSnapshot: T;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n readonly #onFirstSubscribe: (() => void) | undefined;\n readonly #onLastUnsubscribe: (() => void) | undefined;\n readonly #onDestroy: (() => void) | undefined;\n\n constructor(initialSnapshot: T, options?: BaseSourceOptions) {\n this.#currentSnapshot = initialSnapshot;\n this.#onFirstSubscribe = options?.onFirstSubscribe;\n this.#onLastUnsubscribe = options?.onLastUnsubscribe;\n this.#onDestroy = options?.onDestroy;\n\n this.subscribe = this.subscribe.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n this.destroy = this.destroy.bind(this);\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#destroyed) {\n return () => {};\n }\n\n if (this.#listeners.size === 0 && this.#onFirstSubscribe) {\n this.#onFirstSubscribe();\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (\n !this.#destroyed &&\n this.#listeners.size === 0 &&\n this.#onLastUnsubscribe\n ) {\n this.#onLastUnsubscribe();\n }\n };\n }\n\n getSnapshot(): T {\n return this.#currentSnapshot;\n }\n\n updateSnapshot(snapshot: T): void {\n /* v8 ignore next 2 -- @preserve: defensive guard unreachable via public API (destroy() removes router subscription first) */\n if (this.#destroyed) {\n return;\n }\n\n this.#currentSnapshot = snapshot;\n this.#listeners.forEach((listener) => {\n listener();\n });\n }\n\n destroy(): void {\n if (this.#destroyed) {\n return;\n }\n\n this.#destroyed = true;\n this.#onDestroy?.();\n this.#listeners.clear();\n }\n}\n","import { BaseSource } from \"./BaseSource\";\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 source.updateSnapshot({\n route: next.route,\n previousRoute: next.previousRoute,\n });\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\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 { 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 source.updateSnapshot(\n computeSnapshot(source.getSnapshot(), router, nodeName),\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 /* v8 ignore next 3 -- @preserve: dedup guard; shouldUpdateNode filters accurately so computeSnapshot always returns new ref */\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\";\n\nimport type { RouterTransitionSnapshot, RouterSource } from \"./types.js\";\nimport type { Router, State } from \"@real-router/core\";\n\nconst IDLE_SNAPSHOT: RouterTransitionSnapshot = {\n isTransitioning: false,\n toRoute: null,\n fromRoute: null,\n};\n\nexport function createTransitionSource(\n router: Router,\n): RouterSource<RouterTransitionSnapshot> {\n const source = new BaseSource(IDLE_SNAPSHOT, {\n onDestroy: () => {\n unsubs.forEach((unsub) => {\n unsub();\n });\n },\n });\n\n const api = getPluginApi(router);\n\n const resetToIdle = (): void => {\n source.updateSnapshot(IDLE_SNAPSHOT);\n };\n\n // Eager connection: subscribe to router events immediately\n const unsubs = [\n api.addEventListener(\n events.TRANSITION_START,\n (toState: State, fromState?: State) => {\n 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 return source;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/BaseSource.ts","../../src/stabilizeState.ts","../../src/createRouteSource.ts","../../src/computeSnapshot.ts","../../src/shouldUpdateCache.ts","../../src/createRouteNodeSource.ts","../../src/createActiveRouteSource.ts","../../src/createTransitionSource.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["export interface BaseSourceOptions {\n onFirstSubscribe?: () => void;\n onLastUnsubscribe?: () => void;\n onDestroy?: () => void;\n}\n\nexport class BaseSource<T> {\n #currentSnapshot: T;\n #destroyed = false;\n\n readonly #listeners = new Set<() => void>();\n readonly #onFirstSubscribe: (() => void) | undefined;\n readonly #onLastUnsubscribe: (() => void) | undefined;\n readonly #onDestroy: (() => void) | undefined;\n\n constructor(initialSnapshot: T, options?: BaseSourceOptions) {\n this.#currentSnapshot = initialSnapshot;\n this.#onFirstSubscribe = options?.onFirstSubscribe;\n this.#onLastUnsubscribe = options?.onLastUnsubscribe;\n this.#onDestroy = options?.onDestroy;\n\n this.subscribe = this.subscribe.bind(this);\n this.getSnapshot = this.getSnapshot.bind(this);\n this.destroy = this.destroy.bind(this);\n }\n\n subscribe(listener: () => void): () => void {\n if (this.#destroyed) {\n return () => {};\n }\n\n if (this.#listeners.size === 0 && this.#onFirstSubscribe) {\n this.#onFirstSubscribe();\n }\n\n this.#listeners.add(listener);\n\n return () => {\n this.#listeners.delete(listener);\n\n if (\n !this.#destroyed &&\n this.#listeners.size === 0 &&\n this.#onLastUnsubscribe\n ) {\n this.#onLastUnsubscribe();\n }\n };\n }\n\n getSnapshot(): T {\n return this.#currentSnapshot;\n }\n\n updateSnapshot(snapshot: T): void {\n /* v8 ignore next 2 -- @preserve: defensive guard unreachable via public API (destroy() removes router subscription first) */\n if (this.#destroyed) {\n return;\n }\n\n this.#currentSnapshot = snapshot;\n this.#listeners.forEach((listener) => {\n listener();\n });\n }\n\n destroy(): void {\n if (this.#destroyed) {\n return;\n }\n\n this.#destroyed = true;\n this.#onDestroy?.();\n this.#listeners.clear();\n }\n}\n","import type { State } from \"@real-router/core\";\n\n/**\n * State-aware stabilization for route snapshots.\n *\n * Compares `path` — the canonical representation of rendering-relevant\n * State fields (name + params). When path matches, returns `prev`\n * (preserving reference), enabling frameworks to skip re-renders.\n *\n * Ignores `meta` (internal: auto-increment id) and `transition`\n * (reference data: from, segments, reload) — they don't affect\n * what is rendered.\n *\n * Accepts `null` for compatibility with `RouterTransitionSnapshot`\n * (toRoute/fromRoute are `State | null`).\n *\n * @internal Not exported from package public API.\n */\nexport function stabilizeState<T extends State | null | undefined>(\n prev: T,\n next: T,\n): T {\n if (prev === next) {\n return prev;\n }\n if (prev?.path !== next?.path) {\n return next;\n }\n\n return prev;\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouteSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\n/**\n * Creates a source for the full route state.\n *\n * Uses a lazy-connection pattern: the router subscription is created when the\n * first listener subscribes and removed when the last listener unsubscribes.\n * This is compatible with React's useSyncExternalStore and Strict Mode.\n */\nexport function createRouteSource(router: Router): RouterSource<RouteSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const source = new BaseSource<RouteSnapshot>(\n {\n route: router.getState(),\n previousRoute: undefined,\n },\n {\n onFirstSubscribe: () => {\n routerUnsubscribe = router.subscribe((next) => {\n const prev = source.getSnapshot();\n const newRoute = stabilizeState(prev.route, next.route);\n const newPreviousRoute = stabilizeState(\n prev.previousRoute,\n next.previousRoute,\n );\n\n if (\n newRoute !== prev.route ||\n newPreviousRoute !== prev.previousRoute\n ) {\n source.updateSnapshot({\n route: newRoute,\n previousRoute: newPreviousRoute,\n });\n }\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\n}\n","import { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouteNodeSnapshot } from \"./types.js\";\nimport type { Router, SubscribeState } from \"@real-router/core\";\n\nexport function computeSnapshot(\n currentSnapshot: RouteNodeSnapshot,\n router: Router,\n nodeName: string,\n next?: SubscribeState,\n): RouteNodeSnapshot {\n const currentRoute = next?.route ?? router.getState();\n const previousRoute = next?.previousRoute;\n\n const isNodeActive =\n nodeName === \"\" ||\n (currentRoute !== undefined &&\n (currentRoute.name === nodeName ||\n currentRoute.name.startsWith(`${nodeName}.`)));\n\n const route = isNodeActive ? currentRoute : undefined;\n\n if (\n route === currentSnapshot.route &&\n previousRoute === currentSnapshot.previousRoute\n ) {\n return currentSnapshot;\n }\n\n const newRoute = stabilizeState(currentSnapshot.route, route);\n const newPreviousRoute = stabilizeState(\n currentSnapshot.previousRoute,\n previousRoute,\n );\n\n if (\n newRoute === currentSnapshot.route &&\n newPreviousRoute === currentSnapshot.previousRoute\n ) {\n return currentSnapshot;\n }\n\n return { route: newRoute, previousRoute: newPreviousRoute };\n}\n","import type { Router, State } from \"@real-router/core\";\n\nconst shouldUpdateCache = new WeakMap<\n Router,\n Map<string, (toState: State, fromState?: State) => boolean>\n>();\n\nexport function getCachedShouldUpdate(\n router: Router,\n nodeName: string,\n): (toState: State, fromState?: State) => boolean {\n let routerCache = shouldUpdateCache.get(router);\n\n if (!routerCache) {\n routerCache = new Map();\n shouldUpdateCache.set(router, routerCache);\n }\n\n let fn = routerCache.get(nodeName);\n\n if (!fn) {\n fn = router.shouldUpdateNode(nodeName);\n routerCache.set(nodeName, fn);\n }\n\n return fn;\n}\n","import { BaseSource } from \"./BaseSource\";\nimport { computeSnapshot } from \"./computeSnapshot.js\";\nimport { getCachedShouldUpdate } from \"./shouldUpdateCache.js\";\n\nimport type { RouteNodeSnapshot, RouterSource } from \"./types.js\";\nimport type { Router } from \"@real-router/core\";\n\n/**\n * Creates a source scoped to a specific route node.\n *\n * Uses a lazy-connection pattern: the router subscription is created when the\n * first listener subscribes and removed when the last listener unsubscribes.\n * This is compatible with React's useSyncExternalStore and Strict Mode.\n */\nexport function createRouteNodeSource(\n router: Router,\n nodeName: string,\n): RouterSource<RouteNodeSnapshot> {\n let routerUnsubscribe: (() => void) | null = null;\n\n const shouldUpdate = getCachedShouldUpdate(router, nodeName);\n\n const initialSnapshot: RouteNodeSnapshot = {\n route: undefined,\n previousRoute: undefined,\n };\n\n const disconnect = (): void => {\n const unsub = routerUnsubscribe;\n\n routerUnsubscribe = null;\n unsub?.();\n };\n\n const source = new BaseSource<RouteNodeSnapshot>(\n computeSnapshot(initialSnapshot, router, nodeName),\n {\n onFirstSubscribe: () => {\n // Reconcile snapshot with current router state before connecting.\n // Covers reconnection after Activity hide/show cycles where the\n // source was disconnected and missed navigation events.\n const reconciled = computeSnapshot(\n source.getSnapshot(),\n router,\n nodeName,\n );\n\n if (!Object.is(reconciled, source.getSnapshot())) {\n source.updateSnapshot(reconciled);\n }\n\n // Connect to router on first subscription\n routerUnsubscribe = router.subscribe((next) => {\n if (!shouldUpdate(next.route, next.previousRoute)) {\n return;\n }\n\n const newSnapshot = computeSnapshot(\n source.getSnapshot(),\n router,\n nodeName,\n next,\n );\n\n if (!Object.is(source.getSnapshot(), newSnapshot)) {\n source.updateSnapshot(newSnapshot);\n }\n });\n },\n onLastUnsubscribe: disconnect,\n onDestroy: disconnect,\n },\n );\n\n return source;\n}\n","import { areRoutesRelated } from \"@real-router/route-utils\";\n\nimport { BaseSource } from \"./BaseSource\";\n\nimport type { ActiveRouteSourceOptions, RouterSource } from \"./types.js\";\nimport type { Params, Router } from \"@real-router/core\";\n\nexport function createActiveRouteSource(\n router: Router,\n routeName: string,\n params?: Params,\n options?: ActiveRouteSourceOptions,\n): RouterSource<boolean> {\n const strict = options?.strict ?? false;\n const ignoreQueryParams = options?.ignoreQueryParams ?? true;\n\n const initialValue = router.isActiveRoute(\n routeName,\n params,\n strict,\n ignoreQueryParams,\n );\n\n const source = new BaseSource(initialValue, {\n onDestroy: () => {\n unsubscribe();\n },\n });\n\n // Eager connection: subscribe to router immediately\n const unsubscribe = router.subscribe((next) => {\n const isNewRelated = areRoutesRelated(routeName, next.route.name);\n const isPrevRelated =\n next.previousRoute &&\n areRoutesRelated(routeName, next.previousRoute.name);\n\n if (!isNewRelated && !isPrevRelated) {\n return;\n }\n\n // If new route is not related, we know the route is inactive —\n // avoid calling isActiveRoute for the optimization\n const newValue = isNewRelated\n ? router.isActiveRoute(routeName, params, strict, ignoreQueryParams)\n : false;\n\n if (!Object.is(source.getSnapshot(), newValue)) {\n source.updateSnapshot(newValue);\n }\n });\n\n return source;\n}\n","import { events } from \"@real-router/core\";\nimport { getPluginApi } from \"@real-router/core/api\";\n\nimport { BaseSource } from \"./BaseSource\";\nimport { stabilizeState } from \"./stabilizeState.js\";\n\nimport type { RouterTransitionSnapshot, RouterSource } from \"./types.js\";\nimport type { Router, State } from \"@real-router/core\";\n\nconst IDLE_SNAPSHOT: RouterTransitionSnapshot = {\n isTransitioning: false,\n toRoute: null,\n fromRoute: null,\n};\n\nexport function createTransitionSource(\n router: Router,\n): RouterSource<RouterTransitionSnapshot> {\n const source = new BaseSource(IDLE_SNAPSHOT, {\n onDestroy: () => {\n unsubs.forEach((unsub) => {\n unsub();\n });\n },\n });\n\n const api = getPluginApi(router);\n\n const resetToIdle = (): void => {\n source.updateSnapshot(IDLE_SNAPSHOT);\n };\n\n // Eager connection: subscribe to router events immediately\n const unsubs = [\n api.addEventListener(\n events.TRANSITION_START,\n (toState: State, fromState?: State) => {\n const prev = source.getSnapshot();\n const newToRoute = stabilizeState(prev.toRoute, toState);\n const newFromRoute = stabilizeState(prev.fromRoute, fromState ?? null);\n\n if (\n !prev.isTransitioning ||\n newToRoute !== prev.toRoute ||\n newFromRoute !== prev.fromRoute\n ) {\n source.updateSnapshot({\n isTransitioning: true,\n toRoute: newToRoute,\n fromRoute: newFromRoute,\n });\n }\n },\n ),\n api.addEventListener(events.TRANSITION_SUCCESS, resetToIdle),\n api.addEventListener(events.TRANSITION_ERROR, resetToIdle),\n api.addEventListener(events.TRANSITION_CANCEL, resetToIdle),\n ];\n\n return source;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"src/BaseSource.ts":{"bytes":1857,"imports":[],"format":"esm"},"src/createRouteSource.ts":{"bytes":
|
|
1
|
+
{"inputs":{"src/BaseSource.ts":{"bytes":1857,"imports":[],"format":"esm"},"src/stabilizeState.ts":{"bytes":856,"imports":[],"format":"esm"},"src/createRouteSource.ts":{"bytes":1599,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"}],"format":"esm"},"src/computeSnapshot.ts":{"bytes":1184,"imports":[{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"}],"format":"esm"},"src/shouldUpdateCache.ts":{"bytes":610,"imports":[],"format":"esm"},"src/createRouteNodeSource.ts":{"bytes":2239,"imports":[{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/computeSnapshot.ts","kind":"import-statement","original":"./computeSnapshot.js"},{"path":"src/shouldUpdateCache.ts","kind":"import-statement","original":"./shouldUpdateCache.js"}],"format":"esm"},"src/createActiveRouteSource.ts":{"bytes":1461,"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"}],"format":"esm"},"src/createTransitionSource.ts":{"bytes":1731,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true},{"path":"src/BaseSource.ts","kind":"import-statement","original":"./BaseSource"},{"path":"src/stabilizeState.ts","kind":"import-statement","original":"./stabilizeState.js"}],"format":"esm"},"src/index.ts":{"bytes":407,"imports":[{"path":"src/createRouteSource.ts","kind":"import-statement","original":"./createRouteSource"},{"path":"src/createRouteNodeSource.ts","kind":"import-statement","original":"./createRouteNodeSource"},{"path":"src/createActiveRouteSource.ts","kind":"import-statement","original":"./createActiveRouteSource"},{"path":"src/createTransitionSource.ts","kind":"import-statement","original":"./createTransitionSource"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":17153},"dist/esm/index.mjs":{"imports":[{"path":"@real-router/route-utils","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core/api","kind":"import-statement","external":true}],"exports":["createActiveRouteSource","createRouteNodeSource","createRouteSource","createTransitionSource"],"entryPoint":"src/index.ts","inputs":{"src/BaseSource.ts":{"bytesInOutput":1375},"src/stabilizeState.ts":{"bytesInOutput":155},"src/createRouteSource.ts":{"bytesInOutput":966},"src/index.ts":{"bytesInOutput":0},"src/computeSnapshot.ts":{"bytesInOutput":860},"src/shouldUpdateCache.ts":{"bytesInOutput":425},"src/createRouteNodeSource.ts":{"bytesInOutput":1251},"src/createActiveRouteSource.ts":{"bytesInOutput":977},"src/createTransitionSource.ts":{"bytesInOutput":1279}},"bytes":7758}}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@real-router/sources",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"description": "Framework-agnostic subscription layer for Real-Router state",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"homepage": "https://github.com/greydragon888/real-router",
|
|
44
44
|
"sideEffects": false,
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@real-router/core": "^0.
|
|
46
|
+
"@real-router/core": "^0.40.1",
|
|
47
47
|
"@real-router/route-utils": "^0.1.6"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"lint:types": "attw --pack .",
|
|
59
59
|
"test:properties": "vitest --config vitest.config.properties.mts --run",
|
|
60
60
|
"test:stress": "vitest run --config vitest.config.stress.mts",
|
|
61
|
-
"bench": "NODE_OPTIONS='--expose-gc --max-old-space-size=4096' npx tsx tests/benchmarks/index.ts"
|
|
61
|
+
"bench": "NODE_OPTIONS='--expose-gc --max-old-space-size=4096' npx tsx tests/benchmarks/index.ts",
|
|
62
|
+
"build:dist-only": "tsup"
|
|
62
63
|
}
|
|
63
64
|
}
|
package/src/computeSnapshot.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { stabilizeState } from "./stabilizeState.js";
|
|
2
|
+
|
|
1
3
|
import type { RouteNodeSnapshot } from "./types.js";
|
|
2
4
|
import type { Router, SubscribeState } from "@real-router/core";
|
|
3
5
|
|
|
@@ -25,5 +27,18 @@ export function computeSnapshot(
|
|
|
25
27
|
return currentSnapshot;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
const newRoute = stabilizeState(currentSnapshot.route, route);
|
|
31
|
+
const newPreviousRoute = stabilizeState(
|
|
32
|
+
currentSnapshot.previousRoute,
|
|
33
|
+
previousRoute,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
if (
|
|
37
|
+
newRoute === currentSnapshot.route &&
|
|
38
|
+
newPreviousRoute === currentSnapshot.previousRoute
|
|
39
|
+
) {
|
|
40
|
+
return currentSnapshot;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return { route: newRoute, previousRoute: newPreviousRoute };
|
|
29
44
|
}
|
|
@@ -39,10 +39,16 @@ export function createRouteNodeSource(
|
|
|
39
39
|
// Reconcile snapshot with current router state before connecting.
|
|
40
40
|
// Covers reconnection after Activity hide/show cycles where the
|
|
41
41
|
// source was disconnected and missed navigation events.
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const reconciled = computeSnapshot(
|
|
43
|
+
source.getSnapshot(),
|
|
44
|
+
router,
|
|
45
|
+
nodeName,
|
|
44
46
|
);
|
|
45
47
|
|
|
48
|
+
if (!Object.is(reconciled, source.getSnapshot())) {
|
|
49
|
+
source.updateSnapshot(reconciled);
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
// Connect to router on first subscription
|
|
47
53
|
routerUnsubscribe = router.subscribe((next) => {
|
|
48
54
|
if (!shouldUpdate(next.route, next.previousRoute)) {
|
|
@@ -56,7 +62,6 @@ export function createRouteNodeSource(
|
|
|
56
62
|
next,
|
|
57
63
|
);
|
|
58
64
|
|
|
59
|
-
/* v8 ignore next 3 -- @preserve: dedup guard; shouldUpdateNode filters accurately so computeSnapshot always returns new ref */
|
|
60
65
|
if (!Object.is(source.getSnapshot(), newSnapshot)) {
|
|
61
66
|
source.updateSnapshot(newSnapshot);
|
|
62
67
|
}
|
package/src/createRouteSource.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseSource } from "./BaseSource";
|
|
2
|
+
import { stabilizeState } from "./stabilizeState.js";
|
|
2
3
|
|
|
3
4
|
import type { RouteSnapshot, RouterSource } from "./types.js";
|
|
4
5
|
import type { Router } from "@real-router/core";
|
|
@@ -28,10 +29,22 @@ export function createRouteSource(router: Router): RouterSource<RouteSnapshot> {
|
|
|
28
29
|
{
|
|
29
30
|
onFirstSubscribe: () => {
|
|
30
31
|
routerUnsubscribe = router.subscribe((next) => {
|
|
31
|
-
source.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
const prev = source.getSnapshot();
|
|
33
|
+
const newRoute = stabilizeState(prev.route, next.route);
|
|
34
|
+
const newPreviousRoute = stabilizeState(
|
|
35
|
+
prev.previousRoute,
|
|
36
|
+
next.previousRoute,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if (
|
|
40
|
+
newRoute !== prev.route ||
|
|
41
|
+
newPreviousRoute !== prev.previousRoute
|
|
42
|
+
) {
|
|
43
|
+
source.updateSnapshot({
|
|
44
|
+
route: newRoute,
|
|
45
|
+
previousRoute: newPreviousRoute,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
35
48
|
});
|
|
36
49
|
},
|
|
37
50
|
onLastUnsubscribe: disconnect,
|
|
@@ -2,6 +2,7 @@ import { events } from "@real-router/core";
|
|
|
2
2
|
import { getPluginApi } from "@real-router/core/api";
|
|
3
3
|
|
|
4
4
|
import { BaseSource } from "./BaseSource";
|
|
5
|
+
import { stabilizeState } from "./stabilizeState.js";
|
|
5
6
|
|
|
6
7
|
import type { RouterTransitionSnapshot, RouterSource } from "./types.js";
|
|
7
8
|
import type { Router, State } from "@real-router/core";
|
|
@@ -34,11 +35,21 @@ export function createTransitionSource(
|
|
|
34
35
|
api.addEventListener(
|
|
35
36
|
events.TRANSITION_START,
|
|
36
37
|
(toState: State, fromState?: State) => {
|
|
37
|
-
source.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
const prev = source.getSnapshot();
|
|
39
|
+
const newToRoute = stabilizeState(prev.toRoute, toState);
|
|
40
|
+
const newFromRoute = stabilizeState(prev.fromRoute, fromState ?? null);
|
|
41
|
+
|
|
42
|
+
if (
|
|
43
|
+
!prev.isTransitioning ||
|
|
44
|
+
newToRoute !== prev.toRoute ||
|
|
45
|
+
newFromRoute !== prev.fromRoute
|
|
46
|
+
) {
|
|
47
|
+
source.updateSnapshot({
|
|
48
|
+
isTransitioning: true,
|
|
49
|
+
toRoute: newToRoute,
|
|
50
|
+
fromRoute: newFromRoute,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
42
53
|
},
|
|
43
54
|
),
|
|
44
55
|
api.addEventListener(events.TRANSITION_SUCCESS, resetToIdle),
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { State } from "@real-router/core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* State-aware stabilization for route snapshots.
|
|
5
|
+
*
|
|
6
|
+
* Compares `path` — the canonical representation of rendering-relevant
|
|
7
|
+
* State fields (name + params). When path matches, returns `prev`
|
|
8
|
+
* (preserving reference), enabling frameworks to skip re-renders.
|
|
9
|
+
*
|
|
10
|
+
* Ignores `meta` (internal: auto-increment id) and `transition`
|
|
11
|
+
* (reference data: from, segments, reload) — they don't affect
|
|
12
|
+
* what is rendered.
|
|
13
|
+
*
|
|
14
|
+
* Accepts `null` for compatibility with `RouterTransitionSnapshot`
|
|
15
|
+
* (toRoute/fromRoute are `State | null`).
|
|
16
|
+
*
|
|
17
|
+
* @internal Not exported from package public API.
|
|
18
|
+
*/
|
|
19
|
+
export function stabilizeState<T extends State | null | undefined>(
|
|
20
|
+
prev: T,
|
|
21
|
+
next: T,
|
|
22
|
+
): T {
|
|
23
|
+
if (prev === next) {
|
|
24
|
+
return prev;
|
|
25
|
+
}
|
|
26
|
+
if (prev?.path !== next?.path) {
|
|
27
|
+
return next;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return prev;
|
|
31
|
+
}
|