@real-router/lifecycle-plugin 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,17 +5,17 @@
5
5
  [![bundle size](https://deno.bundlejs.com/?q=@real-router/lifecycle-plugin&treeshake=[*]&badge=detailed)](https://bundlejs.com/?q=@real-router/lifecycle-plugin&treeshake=[*])
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](../../LICENSE)
7
7
 
8
- > Route-level lifecycle hooks for [Real-Router](https://github.com/greydragon888/real-router). Add `onEnter`, `onStay`, `onLeave` callbacks directly to route definitions.
8
+ > Route-level lifecycle hooks for [Real-Router](https://github.com/greydragon888/real-router). Add `onNavigate`, `onEnter`, `onStay`, `onLeave` callbacks directly to route definitions.
9
9
 
10
10
  ```typescript
11
11
  // Without plugin — scattered subscribe() calls with route checks:
12
12
  router.subscribe(({ route, previousRoute }) => {
13
- if (route.name === "dashboard") trackPageView("dashboard");
13
+ if (route.name === "catalog") loadServices(route.params);
14
14
  if (previousRoute?.name === "editor") saveEditorState();
15
15
  });
16
16
 
17
17
  // With plugin — declarative, per-route:
18
- { name: "dashboard", path: "/dashboard", onEnter: () => () => trackPageView("dashboard") }
18
+ { name: "catalog", path: "/catalog?q&sort", onNavigate: () => (s) => loadServices(s.params) }
19
19
  { name: "editor", path: "/editor", onLeave: () => () => saveEditorState() }
20
20
  ```
21
21
 
@@ -35,20 +35,26 @@ import { lifecyclePluginFactory } from "@real-router/lifecycle-plugin";
35
35
 
36
36
  const routes = [
37
37
  {
38
- name: "home",
39
- path: "/",
40
- onLeave: () => (toState, fromState) => {
41
- console.log("Leaving home for", toState.name);
38
+ name: "services.catalog",
39
+ path: "/catalog?q&sort&dir",
40
+ // Fires on entry AND on param-change — recommended default
41
+ onNavigate: () => (toState) => {
42
+ loadServices(toState.params);
42
43
  },
43
44
  },
44
45
  {
45
- name: "users.view",
46
- path: "/users/:id",
46
+ name: "chat",
47
+ path: "/chat/:roomId",
48
+ // Orthogonal: onEnter covers entry-only setup, onNavigate covers
49
+ // every navigation (including entry). Both fire on entry.
47
50
  onEnter: () => (toState) => {
48
- analytics.track("user_profile_viewed", { userId: toState.params.id });
51
+ chatSocket.connect(toState.params.roomId);
49
52
  },
50
- onStay: () => (toState, fromState) => {
51
- console.log("User changed:", fromState.params.id, "→", toState.params.id);
53
+ onNavigate: () => (toState) => {
54
+ loadMessages(toState.params.roomId);
55
+ },
56
+ onLeave: () => () => {
57
+ chatSocket.disconnect();
52
58
  },
53
59
  },
54
60
  ];
@@ -59,13 +65,18 @@ router.usePlugin(lifecyclePluginFactory());
59
65
  await router.start("/");
60
66
  ```
61
67
 
68
+ > **Start with `onNavigate`.** It covers the most common case — running the same logic whenever the route is the navigation target (data loading, analytics, UI reset). Add `onEnter` or `onStay` for extra case-specific logic.
69
+
62
70
  ## Hook Reference
63
71
 
64
- | Hook | Fires when | Typical use case |
65
- | --------- | -------------------------- | -------------------------- |
66
- | `onEnter` | Route is entered | Analytics, data prefetch |
67
- | `onStay` | Same route, params changed | Refresh data, update UI |
68
- | `onLeave` | Route is left | Cleanup timers, save state |
72
+ | Hook | Fires when | Typical use case |
73
+ | ------------ | --------------------------------------- | ------------------------------------------- |
74
+ | `onNavigate` | Any successful navigation to the route | Data loading, analytics, UI reset (default) |
75
+ | `onEnter` | Route is entered | Entry-only setup (open socket, scroll top) |
76
+ | `onStay` | Same route, params changed | Stay-only logic (incremental updates) |
77
+ | `onLeave` | Route is left | Cleanup timers, save state |
78
+
79
+ **Orthogonal dispatch:** `onEnter` / `onStay` / `onNavigate` fire independently based on their own conditions. On entry, `onEnter` **and** `onNavigate` fire. On param-change, `onStay` **and** `onNavigate` fire. Each hook is composable — declaring one never silences another.
69
80
 
70
81
  Each hook field is a **factory function** `(router, getDependency) => (toState, fromState?) => void`. The factory runs once per route; the returned callback is cached and invoked on each matching transition. When you don't need DI, omit the factory params:
71
82
 
@@ -86,6 +97,19 @@ onEnter: (router, getDependency) => (toState) => {
86
97
 
87
98
  ## Use Cases
88
99
 
100
+ ### Data loading (onNavigate — recommended default)
101
+
102
+ ```typescript
103
+ {
104
+ name: "services.catalog",
105
+ path: "/catalog?q&sort&dir",
106
+ onNavigate: () => (toState) => {
107
+ // Fires on entry from another route AND on filter/sort param changes
108
+ loadServices(toState.params);
109
+ },
110
+ }
111
+ ```
112
+
89
113
  ### Analytics tracking
90
114
 
91
115
  ```typescript
@@ -30,6 +30,12 @@ declare module "@real-router/core" {
30
30
  onStay?: LifecycleHookFactory<Dependencies>;
31
31
  /** Factory that returns a hook called when this route segment is deactivated (left). */
32
32
  onLeave?: LifecycleHookFactory<Dependencies>;
33
+ /**
34
+ * Factory that returns a hook called on every successful navigation to this route
35
+ * (both entry and param-change). Acts as a fallback when `onEnter` or `onStay`
36
+ * is not defined for the corresponding case.
37
+ */
38
+ onNavigate?: LifecycleHookFactory<Dependencies>;
33
39
  }
34
40
  }
35
41
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/types.ts","../../src/factory.ts","../../src/index.ts"],"mappings":";;;;;;AAOA;;KAAY,aAAA,IACV,OAAA,EAAS,KAAA,EACT,SAAA,EAAW,KAAA;;;;;;KAQD,oBAAA,sBACW,qBAAA,GAAsB,qBAAA,KAE3C,MAAA,EAAQ,MAAA,CAAO,YAAA,GACf,aAAA,mBAAgC,YAAA,EAAc,GAAA,EAAK,CAAA,KAAM,YAAA,CAAa,CAAA,MACnE,aAAA;;;iBC2CW,sBAAA,CAAA,GAA0B,aAAA;;;;AD1D1C;;;;YEGY,KAAA,sBAA2B,mBAAA;IFFrC;IEIE,OAAA,GAAU,oBAAA,CAAqB,YAAA;IFHjC;IEKE,MAAA,GAAS,oBAAA,CAAqB,YAAA;IFLJ;IEO1B,OAAA,GAAU,oBAAA,CAAqB,YAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/types.ts","../../src/factory.ts","../../src/index.ts"],"mappings":";;;;;;AAOA;;KAAY,aAAA,IACV,OAAA,EAAS,KAAA,EACT,SAAA,EAAW,KAAA;;;;;;KAQD,oBAAA,sBACW,qBAAA,GAAsB,qBAAA,KAE3C,MAAA,EAAQ,MAAA,CAAO,YAAA,GACf,aAAA,mBAAgC,YAAA,EAAc,GAAA,EAAK,CAAA,KAAM,YAAA,CAAa,CAAA,MACnE,aAAA;;;iBC6CW,sBAAA,CAAA,GAA0B,aAAA;;;;AD5D1C;;;;YEGY,KAAA,sBAA2B,mBAAA;IFFrC;IEIE,OAAA,GAAU,oBAAA,CAAqB,YAAA;IFHjC;IEKE,MAAA,GAAS,oBAAA,CAAqB,YAAA;IFLJ;IEO1B,OAAA,GAAU,oBAAA,CAAqB,YAAA;IFCH;;;;;IEK5B,UAAA,GAAa,oBAAA,CAAqB,YAAA;EAAA;AAAA"}
package/dist/cjs/index.js CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@real-router/core/api`);function t(...t){let[n,r]=t,i=(0,e.getPluginApi)(n),a=new Map;function o(e,t){let o=`${e}:${t}`,s=i.getRouteConfig(t),c=typeof s?.[e]==`function`?s[e]:void 0;if(!c){a.delete(o);return}let l=a.get(o);if(l?.factory===c)return l.hook;let u=c(n,r);return a.set(o,{hook:u,factory:c}),u}return{onTransitionLeaveApprove:(e,t)=>{t&&e.name!==t.name&&o(`onLeave`,t.name)?.(e,t)},onTransitionSuccess:(e,t)=>{e.name===t?.name?o(`onStay`,e.name)?.(e,t):o(`onEnter`,e.name)?.(e,t)}}}function n(){return t}exports.lifecyclePluginFactory=n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@real-router/core/api`);function t(...t){let[n,r]=t,i=(0,e.getPluginApi)(n),a=new Map;function o(e,t){let o=`${e}:${t}`,s=i.getRouteConfig(t),c=typeof s?.[e]==`function`?s[e]:void 0;if(!c){a.delete(o);return}let l=a.get(o);if(l?.factory===c)return l.hook;let u=c(n,r);return a.set(o,{hook:u,factory:c}),u}return{onTransitionLeaveApprove:(e,t)=>{t&&e.name!==t.name&&o(`onLeave`,t.name)?.(e,t)},onTransitionSuccess:(e,t)=>{e.name===t?.name?o(`onStay`,e.name)?.(e,t):o(`onEnter`,e.name)?.(e,t),o(`onNavigate`,e.name)?.(e,t)}}}function n(){return t}exports.lifecyclePluginFactory=n;
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/factory.ts"],"sourcesContent":["import { getPluginApi } from \"@real-router/core/api\";\n\nimport type { LifecycleHook, LifecycleHookFactory } from \"./types\";\nimport type { PluginFactory, State } from \"@real-router/core\";\n\nfunction createPlugin(\n ...args: Parameters<PluginFactory>\n): ReturnType<PluginFactory> {\n const [router, getDependency] = args;\n const api = getPluginApi(router);\n const compiledHooks = new Map<\n string,\n { hook: LifecycleHook; factory: LifecycleHookFactory }\n >();\n\n function compileHook(\n hookName: \"onEnter\" | \"onStay\" | \"onLeave\",\n routeName: string,\n ): LifecycleHook | undefined {\n const key = `${hookName}:${routeName}`;\n const config = api.getRouteConfig(routeName);\n const factory =\n typeof config?.[hookName] === \"function\"\n ? (config[hookName] as LifecycleHookFactory)\n : undefined;\n\n if (!factory) {\n compiledHooks.delete(key);\n\n return undefined;\n }\n\n const cached = compiledHooks.get(key);\n\n if (cached?.factory === factory) {\n return cached.hook;\n }\n\n const hook = factory(router, getDependency);\n\n compiledHooks.set(key, { hook, factory });\n\n return hook;\n }\n\n return {\n onTransitionLeaveApprove: (\n toState: State,\n fromState: State | undefined,\n ) => {\n if (fromState && toState.name !== fromState.name) {\n compileHook(\"onLeave\", fromState.name)?.(toState, fromState);\n }\n },\n\n onTransitionSuccess: (toState: State, fromState: State | undefined) => {\n if (toState.name === fromState?.name) {\n compileHook(\"onStay\", toState.name)?.(toState, fromState);\n } else {\n compileHook(\"onEnter\", toState.name)?.(toState, fromState);\n }\n },\n };\n}\n\nexport function lifecyclePluginFactory(): PluginFactory {\n return createPlugin;\n}\n"],"mappings":"0GAKA,SAAS,EACP,GAAG,EACwB,CAC3B,GAAM,CAAC,EAAQ,GAAiB,EAC1B,GAAA,EAAA,EAAA,cAAmB,EAAO,CAC1B,EAAgB,IAAI,IAK1B,SAAS,EACP,EACA,EAC2B,CAC3B,IAAM,EAAM,GAAG,EAAS,GAAG,IACrB,EAAS,EAAI,eAAe,EAAU,CACtC,EACJ,OAAO,IAAS,IAAc,WACzB,EAAO,GACR,IAAA,GAEN,GAAI,CAAC,EAAS,CACZ,EAAc,OAAO,EAAI,CAEzB,OAGF,IAAM,EAAS,EAAc,IAAI,EAAI,CAErC,GAAI,GAAQ,UAAY,EACtB,OAAO,EAAO,KAGhB,IAAM,EAAO,EAAQ,EAAQ,EAAc,CAI3C,OAFA,EAAc,IAAI,EAAK,CAAE,OAAM,UAAS,CAAC,CAElC,EAGT,MAAO,CACL,0BACE,EACA,IACG,CACC,GAAa,EAAQ,OAAS,EAAU,MAC1C,EAAY,UAAW,EAAU,KAAK,GAAG,EAAS,EAAU,EAIhE,qBAAsB,EAAgB,IAAiC,CACjE,EAAQ,OAAS,GAAW,KAC9B,EAAY,SAAU,EAAQ,KAAK,GAAG,EAAS,EAAU,CAEzD,EAAY,UAAW,EAAQ,KAAK,GAAG,EAAS,EAAU,EAG/D,CAGH,SAAgB,GAAwC,CACtD,OAAO"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/factory.ts"],"sourcesContent":["import { getPluginApi } from \"@real-router/core/api\";\n\nimport type { LifecycleHook, LifecycleHookFactory } from \"./types\";\nimport type { PluginFactory, State } from \"@real-router/core\";\n\nfunction createPlugin(\n ...args: Parameters<PluginFactory>\n): ReturnType<PluginFactory> {\n const [router, getDependency] = args;\n const api = getPluginApi(router);\n const compiledHooks = new Map<\n string,\n { hook: LifecycleHook; factory: LifecycleHookFactory }\n >();\n\n function compileHook(\n hookName: \"onEnter\" | \"onStay\" | \"onLeave\" | \"onNavigate\",\n routeName: string,\n ): LifecycleHook | undefined {\n const key = `${hookName}:${routeName}`;\n const config = api.getRouteConfig(routeName);\n const factory =\n typeof config?.[hookName] === \"function\"\n ? (config[hookName] as LifecycleHookFactory)\n : undefined;\n\n if (!factory) {\n compiledHooks.delete(key);\n\n return undefined;\n }\n\n const cached = compiledHooks.get(key);\n\n if (cached?.factory === factory) {\n return cached.hook;\n }\n\n const hook = factory(router, getDependency);\n\n compiledHooks.set(key, { hook, factory });\n\n return hook;\n }\n\n return {\n onTransitionLeaveApprove: (\n toState: State,\n fromState: State | undefined,\n ) => {\n if (fromState && toState.name !== fromState.name) {\n compileHook(\"onLeave\", fromState.name)?.(toState, fromState);\n }\n },\n\n onTransitionSuccess: (toState: State, fromState: State | undefined) => {\n if (toState.name === fromState?.name) {\n compileHook(\"onStay\", toState.name)?.(toState, fromState);\n } else {\n compileHook(\"onEnter\", toState.name)?.(toState, fromState);\n }\n\n compileHook(\"onNavigate\", toState.name)?.(toState, fromState);\n },\n };\n}\n\nexport function lifecyclePluginFactory(): PluginFactory {\n return createPlugin;\n}\n"],"mappings":"0GAKA,SAAS,EACP,GAAG,EACwB,CAC3B,GAAM,CAAC,EAAQ,GAAiB,EAC1B,GAAA,EAAA,EAAA,cAAmB,EAAO,CAC1B,EAAgB,IAAI,IAK1B,SAAS,EACP,EACA,EAC2B,CAC3B,IAAM,EAAM,GAAG,EAAS,GAAG,IACrB,EAAS,EAAI,eAAe,EAAU,CACtC,EACJ,OAAO,IAAS,IAAc,WACzB,EAAO,GACR,IAAA,GAEN,GAAI,CAAC,EAAS,CACZ,EAAc,OAAO,EAAI,CAEzB,OAGF,IAAM,EAAS,EAAc,IAAI,EAAI,CAErC,GAAI,GAAQ,UAAY,EACtB,OAAO,EAAO,KAGhB,IAAM,EAAO,EAAQ,EAAQ,EAAc,CAI3C,OAFA,EAAc,IAAI,EAAK,CAAE,OAAM,UAAS,CAAC,CAElC,EAGT,MAAO,CACL,0BACE,EACA,IACG,CACC,GAAa,EAAQ,OAAS,EAAU,MAC1C,EAAY,UAAW,EAAU,KAAK,GAAG,EAAS,EAAU,EAIhE,qBAAsB,EAAgB,IAAiC,CACjE,EAAQ,OAAS,GAAW,KAC9B,EAAY,SAAU,EAAQ,KAAK,GAAG,EAAS,EAAU,CAEzD,EAAY,UAAW,EAAQ,KAAK,GAAG,EAAS,EAAU,CAG5D,EAAY,aAAc,EAAQ,KAAK,GAAG,EAAS,EAAU,EAEhE,CAGH,SAAgB,GAAwC,CACtD,OAAO"}
@@ -30,6 +30,12 @@ declare module "@real-router/core" {
30
30
  onStay?: LifecycleHookFactory<Dependencies>;
31
31
  /** Factory that returns a hook called when this route segment is deactivated (left). */
32
32
  onLeave?: LifecycleHookFactory<Dependencies>;
33
+ /**
34
+ * Factory that returns a hook called on every successful navigation to this route
35
+ * (both entry and param-change). Acts as a fallback when `onEnter` or `onStay`
36
+ * is not defined for the corresponding case.
37
+ */
38
+ onNavigate?: LifecycleHookFactory<Dependencies>;
33
39
  }
34
40
  }
35
41
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/types.ts","../../src/factory.ts","../../src/index.ts"],"mappings":";;;;;;AAOA;;KAAY,aAAA,IACV,OAAA,EAAS,KAAA,EACT,SAAA,EAAW,KAAA;;;;;;KAQD,oBAAA,sBACW,qBAAA,GAAsB,qBAAA,KAE3C,MAAA,EAAQ,MAAA,CAAO,YAAA,GACf,aAAA,mBAAgC,YAAA,EAAc,GAAA,EAAK,CAAA,KAAM,YAAA,CAAa,CAAA,MACnE,aAAA;;;iBC2CW,sBAAA,CAAA,GAA0B,aAAA;;;;AD1D1C;;;;YEGY,KAAA,sBAA2B,mBAAA;IFFrC;IEIE,OAAA,GAAU,oBAAA,CAAqB,YAAA;IFHjC;IEKE,MAAA,GAAS,oBAAA,CAAqB,YAAA;IFLJ;IEO1B,OAAA,GAAU,oBAAA,CAAqB,YAAA;EAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/types.ts","../../src/factory.ts","../../src/index.ts"],"mappings":";;;;;;AAOA;;KAAY,aAAA,IACV,OAAA,EAAS,KAAA,EACT,SAAA,EAAW,KAAA;;;;;;KAQD,oBAAA,sBACW,qBAAA,GAAsB,qBAAA,KAE3C,MAAA,EAAQ,MAAA,CAAO,YAAA,GACf,aAAA,mBAAgC,YAAA,EAAc,GAAA,EAAK,CAAA,KAAM,YAAA,CAAa,CAAA,MACnE,aAAA;;;iBC6CW,sBAAA,CAAA,GAA0B,aAAA;;;;AD5D1C;;;;YEGY,KAAA,sBAA2B,mBAAA;IFFrC;IEIE,OAAA,GAAU,oBAAA,CAAqB,YAAA;IFHjC;IEKE,MAAA,GAAS,oBAAA,CAAqB,YAAA;IFLJ;IEO1B,OAAA,GAAU,oBAAA,CAAqB,YAAA;IFCH;;;;;IEK5B,UAAA,GAAa,oBAAA,CAAqB,YAAA;EAAA;AAAA"}
@@ -1,2 +1,2 @@
1
- import{getPluginApi as e}from"@real-router/core/api";function t(...t){let[n,r]=t,i=e(n),a=new Map;function o(e,t){let o=`${e}:${t}`,s=i.getRouteConfig(t),c=typeof s?.[e]==`function`?s[e]:void 0;if(!c){a.delete(o);return}let l=a.get(o);if(l?.factory===c)return l.hook;let u=c(n,r);return a.set(o,{hook:u,factory:c}),u}return{onTransitionLeaveApprove:(e,t)=>{t&&e.name!==t.name&&o(`onLeave`,t.name)?.(e,t)},onTransitionSuccess:(e,t)=>{e.name===t?.name?o(`onStay`,e.name)?.(e,t):o(`onEnter`,e.name)?.(e,t)}}}function n(){return t}export{n as lifecyclePluginFactory};
1
+ import{getPluginApi as e}from"@real-router/core/api";function t(...t){let[n,r]=t,i=e(n),a=new Map;function o(e,t){let o=`${e}:${t}`,s=i.getRouteConfig(t),c=typeof s?.[e]==`function`?s[e]:void 0;if(!c){a.delete(o);return}let l=a.get(o);if(l?.factory===c)return l.hook;let u=c(n,r);return a.set(o,{hook:u,factory:c}),u}return{onTransitionLeaveApprove:(e,t)=>{t&&e.name!==t.name&&o(`onLeave`,t.name)?.(e,t)},onTransitionSuccess:(e,t)=>{e.name===t?.name?o(`onStay`,e.name)?.(e,t):o(`onEnter`,e.name)?.(e,t),o(`onNavigate`,e.name)?.(e,t)}}}function n(){return t}export{n as lifecyclePluginFactory};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/factory.ts"],"sourcesContent":["import { getPluginApi } from \"@real-router/core/api\";\n\nimport type { LifecycleHook, LifecycleHookFactory } from \"./types\";\nimport type { PluginFactory, State } from \"@real-router/core\";\n\nfunction createPlugin(\n ...args: Parameters<PluginFactory>\n): ReturnType<PluginFactory> {\n const [router, getDependency] = args;\n const api = getPluginApi(router);\n const compiledHooks = new Map<\n string,\n { hook: LifecycleHook; factory: LifecycleHookFactory }\n >();\n\n function compileHook(\n hookName: \"onEnter\" | \"onStay\" | \"onLeave\",\n routeName: string,\n ): LifecycleHook | undefined {\n const key = `${hookName}:${routeName}`;\n const config = api.getRouteConfig(routeName);\n const factory =\n typeof config?.[hookName] === \"function\"\n ? (config[hookName] as LifecycleHookFactory)\n : undefined;\n\n if (!factory) {\n compiledHooks.delete(key);\n\n return undefined;\n }\n\n const cached = compiledHooks.get(key);\n\n if (cached?.factory === factory) {\n return cached.hook;\n }\n\n const hook = factory(router, getDependency);\n\n compiledHooks.set(key, { hook, factory });\n\n return hook;\n }\n\n return {\n onTransitionLeaveApprove: (\n toState: State,\n fromState: State | undefined,\n ) => {\n if (fromState && toState.name !== fromState.name) {\n compileHook(\"onLeave\", fromState.name)?.(toState, fromState);\n }\n },\n\n onTransitionSuccess: (toState: State, fromState: State | undefined) => {\n if (toState.name === fromState?.name) {\n compileHook(\"onStay\", toState.name)?.(toState, fromState);\n } else {\n compileHook(\"onEnter\", toState.name)?.(toState, fromState);\n }\n },\n };\n}\n\nexport function lifecyclePluginFactory(): PluginFactory {\n return createPlugin;\n}\n"],"mappings":"qDAKA,SAAS,EACP,GAAG,EACwB,CAC3B,GAAM,CAAC,EAAQ,GAAiB,EAC1B,EAAM,EAAa,EAAO,CAC1B,EAAgB,IAAI,IAK1B,SAAS,EACP,EACA,EAC2B,CAC3B,IAAM,EAAM,GAAG,EAAS,GAAG,IACrB,EAAS,EAAI,eAAe,EAAU,CACtC,EACJ,OAAO,IAAS,IAAc,WACzB,EAAO,GACR,IAAA,GAEN,GAAI,CAAC,EAAS,CACZ,EAAc,OAAO,EAAI,CAEzB,OAGF,IAAM,EAAS,EAAc,IAAI,EAAI,CAErC,GAAI,GAAQ,UAAY,EACtB,OAAO,EAAO,KAGhB,IAAM,EAAO,EAAQ,EAAQ,EAAc,CAI3C,OAFA,EAAc,IAAI,EAAK,CAAE,OAAM,UAAS,CAAC,CAElC,EAGT,MAAO,CACL,0BACE,EACA,IACG,CACC,GAAa,EAAQ,OAAS,EAAU,MAC1C,EAAY,UAAW,EAAU,KAAK,GAAG,EAAS,EAAU,EAIhE,qBAAsB,EAAgB,IAAiC,CACjE,EAAQ,OAAS,GAAW,KAC9B,EAAY,SAAU,EAAQ,KAAK,GAAG,EAAS,EAAU,CAEzD,EAAY,UAAW,EAAQ,KAAK,GAAG,EAAS,EAAU,EAG/D,CAGH,SAAgB,GAAwC,CACtD,OAAO"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/factory.ts"],"sourcesContent":["import { getPluginApi } from \"@real-router/core/api\";\n\nimport type { LifecycleHook, LifecycleHookFactory } from \"./types\";\nimport type { PluginFactory, State } from \"@real-router/core\";\n\nfunction createPlugin(\n ...args: Parameters<PluginFactory>\n): ReturnType<PluginFactory> {\n const [router, getDependency] = args;\n const api = getPluginApi(router);\n const compiledHooks = new Map<\n string,\n { hook: LifecycleHook; factory: LifecycleHookFactory }\n >();\n\n function compileHook(\n hookName: \"onEnter\" | \"onStay\" | \"onLeave\" | \"onNavigate\",\n routeName: string,\n ): LifecycleHook | undefined {\n const key = `${hookName}:${routeName}`;\n const config = api.getRouteConfig(routeName);\n const factory =\n typeof config?.[hookName] === \"function\"\n ? (config[hookName] as LifecycleHookFactory)\n : undefined;\n\n if (!factory) {\n compiledHooks.delete(key);\n\n return undefined;\n }\n\n const cached = compiledHooks.get(key);\n\n if (cached?.factory === factory) {\n return cached.hook;\n }\n\n const hook = factory(router, getDependency);\n\n compiledHooks.set(key, { hook, factory });\n\n return hook;\n }\n\n return {\n onTransitionLeaveApprove: (\n toState: State,\n fromState: State | undefined,\n ) => {\n if (fromState && toState.name !== fromState.name) {\n compileHook(\"onLeave\", fromState.name)?.(toState, fromState);\n }\n },\n\n onTransitionSuccess: (toState: State, fromState: State | undefined) => {\n if (toState.name === fromState?.name) {\n compileHook(\"onStay\", toState.name)?.(toState, fromState);\n } else {\n compileHook(\"onEnter\", toState.name)?.(toState, fromState);\n }\n\n compileHook(\"onNavigate\", toState.name)?.(toState, fromState);\n },\n };\n}\n\nexport function lifecyclePluginFactory(): PluginFactory {\n return createPlugin;\n}\n"],"mappings":"qDAKA,SAAS,EACP,GAAG,EACwB,CAC3B,GAAM,CAAC,EAAQ,GAAiB,EAC1B,EAAM,EAAa,EAAO,CAC1B,EAAgB,IAAI,IAK1B,SAAS,EACP,EACA,EAC2B,CAC3B,IAAM,EAAM,GAAG,EAAS,GAAG,IACrB,EAAS,EAAI,eAAe,EAAU,CACtC,EACJ,OAAO,IAAS,IAAc,WACzB,EAAO,GACR,IAAA,GAEN,GAAI,CAAC,EAAS,CACZ,EAAc,OAAO,EAAI,CAEzB,OAGF,IAAM,EAAS,EAAc,IAAI,EAAI,CAErC,GAAI,GAAQ,UAAY,EACtB,OAAO,EAAO,KAGhB,IAAM,EAAO,EAAQ,EAAQ,EAAc,CAI3C,OAFA,EAAc,IAAI,EAAK,CAAE,OAAM,UAAS,CAAC,CAElC,EAGT,MAAO,CACL,0BACE,EACA,IACG,CACC,GAAa,EAAQ,OAAS,EAAU,MAC1C,EAAY,UAAW,EAAU,KAAK,GAAG,EAAS,EAAU,EAIhE,qBAAsB,EAAgB,IAAiC,CACjE,EAAQ,OAAS,GAAW,KAC9B,EAAY,SAAU,EAAQ,KAAK,GAAG,EAAS,EAAU,CAEzD,EAAY,UAAW,EAAQ,KAAK,GAAG,EAAS,EAAU,CAG5D,EAAY,aAAc,EAAQ,KAAK,GAAG,EAAS,EAAU,EAEhE,CAGH,SAAgB,GAAwC,CACtD,OAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/lifecycle-plugin",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "commonjs",
5
5
  "description": "Route-level lifecycle hooks: onEnter, onStay, onLeave",
6
6
  "main": "./dist/cjs/index.js",
package/src/factory.ts CHANGED
@@ -14,7 +14,7 @@ function createPlugin(
14
14
  >();
15
15
 
16
16
  function compileHook(
17
- hookName: "onEnter" | "onStay" | "onLeave",
17
+ hookName: "onEnter" | "onStay" | "onLeave" | "onNavigate",
18
18
  routeName: string,
19
19
  ): LifecycleHook | undefined {
20
20
  const key = `${hookName}:${routeName}`;
@@ -59,6 +59,8 @@ function createPlugin(
59
59
  } else {
60
60
  compileHook("onEnter", toState.name)?.(toState, fromState);
61
61
  }
62
+
63
+ compileHook("onNavigate", toState.name)?.(toState, fromState);
62
64
  },
63
65
  };
64
66
  }
package/src/index.ts CHANGED
@@ -15,6 +15,12 @@ declare module "@real-router/core" {
15
15
  onStay?: LifecycleHookFactory<Dependencies>;
16
16
  /** Factory that returns a hook called when this route segment is deactivated (left). */
17
17
  onLeave?: LifecycleHookFactory<Dependencies>;
18
+ /**
19
+ * Factory that returns a hook called on every successful navigation to this route
20
+ * (both entry and param-change). Acts as a fallback when `onEnter` or `onStay`
21
+ * is not defined for the corresponding case.
22
+ */
23
+ onNavigate?: LifecycleHookFactory<Dependencies>;
18
24
  }
19
25
  }
20
26