@real-router/lifecycle-plugin 0.0.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 +130 -0
- package/dist/cjs/index.d.ts +30 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.d.mts +30 -0
- package/dist/esm/index.mjs +2 -0
- package/dist/esm/index.mjs.map +1 -0
- package/package.json +59 -0
- package/src/factory.ts +47 -0
- package/src/index.ts +20 -0
- package/src/types.ts +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# @real-router/lifecycle-plugin
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@real-router/lifecycle-plugin)
|
|
4
|
+
[](https://www.npmjs.com/package/@real-router/lifecycle-plugin)
|
|
5
|
+
[](https://bundlejs.com/?q=@real-router/lifecycle-plugin&treeshake=[*])
|
|
6
|
+
[](../../LICENSE)
|
|
7
|
+
|
|
8
|
+
> Route-level lifecycle hooks for [Real-Router](https://github.com/greydragon888/real-router). Add `onEnter`, `onStay`, `onLeave` callbacks directly to route definitions.
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
// Without plugin — scattered subscribe() calls with route checks:
|
|
12
|
+
router.subscribe(({ route, previousRoute }) => {
|
|
13
|
+
if (route.name === "dashboard") trackPageView("dashboard");
|
|
14
|
+
if (previousRoute?.name === "editor") saveEditorState();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// With plugin — declarative, per-route:
|
|
18
|
+
{ name: "dashboard", path: "/dashboard", onEnter: () => trackPageView("dashboard") }
|
|
19
|
+
{ name: "editor", path: "/editor", onLeave: () => saveEditorState() }
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @real-router/lifecycle-plugin
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Peer dependency:** `@real-router/core`
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { createRouter } from "@real-router/core";
|
|
34
|
+
import { lifecyclePluginFactory } from "@real-router/lifecycle-plugin";
|
|
35
|
+
|
|
36
|
+
const routes = [
|
|
37
|
+
{
|
|
38
|
+
name: "home",
|
|
39
|
+
path: "/",
|
|
40
|
+
onLeave: (toState, fromState) => {
|
|
41
|
+
console.log("Leaving home for", toState.name);
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "users.view",
|
|
46
|
+
path: "/users/:id",
|
|
47
|
+
onEnter: (toState) => {
|
|
48
|
+
analytics.track("user_profile_viewed", { userId: toState.params.id });
|
|
49
|
+
},
|
|
50
|
+
onStay: (toState, fromState) => {
|
|
51
|
+
console.log("User changed:", fromState.params.id, "→", toState.params.id);
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const router = createRouter(routes);
|
|
57
|
+
router.usePlugin(lifecyclePluginFactory());
|
|
58
|
+
|
|
59
|
+
await router.start("/");
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Hook Reference
|
|
63
|
+
|
|
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 |
|
|
69
|
+
|
|
70
|
+
All hooks receive `(toState: State, fromState: State | undefined) => void`.
|
|
71
|
+
|
|
72
|
+
### Execution order
|
|
73
|
+
|
|
74
|
+
`onLeave` fires first (at leave-approve phase), then `onEnter` or `onStay` (at transition success).
|
|
75
|
+
|
|
76
|
+
## Use Cases
|
|
77
|
+
|
|
78
|
+
### Analytics tracking
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
{
|
|
82
|
+
name: "product",
|
|
83
|
+
path: "/products/:id",
|
|
84
|
+
onEnter: (toState) => {
|
|
85
|
+
analytics.track("product_viewed", { productId: toState.params.id });
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Cleanup on leave
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
{
|
|
94
|
+
name: "editor",
|
|
95
|
+
path: "/editor/:docId",
|
|
96
|
+
onLeave: () => {
|
|
97
|
+
autosaveTimer.clear();
|
|
98
|
+
webSocket.disconnect();
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### React to param changes
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
{
|
|
107
|
+
name: "search",
|
|
108
|
+
path: "/search?q",
|
|
109
|
+
onStay: (toState) => {
|
|
110
|
+
searchStore.setQuery(toState.params.q);
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Documentation
|
|
116
|
+
|
|
117
|
+
- [ARCHITECTURE.md](ARCHITECTURE.md) — Design decisions and data flow
|
|
118
|
+
- [Plugin Architecture](https://github.com/greydragon888/real-router/wiki/plugin-architecture) — How plugins integrate with the router
|
|
119
|
+
|
|
120
|
+
## Related Packages
|
|
121
|
+
|
|
122
|
+
| Package | Description |
|
|
123
|
+
| ---------------------------------------------------------------------------------------- | -------------------------------------- |
|
|
124
|
+
| [@real-router/core](https://www.npmjs.com/package/@real-router/core) | Core router (required peer dependency) |
|
|
125
|
+
| [@real-router/browser-plugin](https://www.npmjs.com/package/@real-router/browser-plugin) | Browser History API integration |
|
|
126
|
+
| [@real-router/logger-plugin](https://www.npmjs.com/package/@real-router/logger-plugin) | Development logging |
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
[MIT](../../LICENSE) © [Oleg Ivanov](https://github.com/greydragon888)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { PluginFactory, State } from "@real-router/core";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Lifecycle hook callback for route transitions.
|
|
6
|
+
* Fire-and-forget: return values are ignored, errors are caught and warned.
|
|
7
|
+
*/
|
|
8
|
+
type LifecycleHook = (toState: State, fromState: State | undefined) => void;
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/factory.d.ts
|
|
11
|
+
declare function lifecyclePluginFactory(): PluginFactory;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/index.d.ts
|
|
14
|
+
/**
|
|
15
|
+
* Module augmentation for real-router.
|
|
16
|
+
* Extends Route interface with lifecycle hooks.
|
|
17
|
+
*/
|
|
18
|
+
declare module "@real-router/core" {
|
|
19
|
+
interface Route {
|
|
20
|
+
/** Called when this route segment is newly activated (entered). */
|
|
21
|
+
onEnter?: LifecycleHook;
|
|
22
|
+
/** Called when this route segment stays active but params changed. */
|
|
23
|
+
onStay?: LifecycleHook;
|
|
24
|
+
/** Called when this route segment is deactivated (left). */
|
|
25
|
+
onLeave?: LifecycleHook;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { type LifecycleHook, lifecyclePluginFactory };
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@real-router/core/api`);function t(e){return(t,n,r,i)=>{let a=e.getRouteConfig(n)?.[t];typeof a==`function`&&a(r,i)}}function n(n){let r=t((0,e.getPluginApi)(n));return{onTransitionLeaveApprove:(e,t)=>{t&&e.name!==t.name&&r(`onLeave`,t.name,e,t)},onTransitionSuccess:(e,t)=>{e.name===t?.name?r(`onStay`,e.name,e,t):r(`onEnter`,e.name,e,t)}}}function r(){return n}exports.lifecyclePluginFactory=r;
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/factory.ts"],"sourcesContent":["import { getPluginApi } from \"@real-router/core/api\";\n\nimport type { LifecycleHook } from \"./types\";\nimport type { PluginFactory, State } from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\nfunction createInvokeHook(api: PluginApi) {\n return (\n hookName: \"onEnter\" | \"onStay\" | \"onLeave\",\n routeName: string,\n toState: State,\n fromState: State | undefined,\n ): void => {\n const hook = api.getRouteConfig(routeName)?.[hookName];\n\n if (typeof hook === \"function\") {\n (hook as LifecycleHook)(toState, fromState);\n }\n };\n}\n\nfunction createPlugin(router: Parameters<PluginFactory>[0]) {\n const invokeHook = createInvokeHook(getPluginApi(router));\n\n return {\n onTransitionLeaveApprove: (\n toState: State,\n fromState: State | undefined,\n ) => {\n if (fromState && toState.name !== fromState.name) {\n invokeHook(\"onLeave\", fromState.name, toState, fromState);\n }\n },\n\n onTransitionSuccess: (toState: State, fromState: State | undefined) => {\n if (toState.name === fromState?.name) {\n invokeHook(\"onStay\", toState.name, toState, fromState);\n } else {\n invokeHook(\"onEnter\", toState.name, toState, fromState);\n }\n },\n };\n}\n\nexport function lifecyclePluginFactory(): PluginFactory {\n return createPlugin;\n}\n"],"mappings":"0GAMA,SAAS,EAAiB,EAAgB,CACxC,OACE,EACA,EACA,EACA,IACS,CACT,IAAM,EAAO,EAAI,eAAe,EAAU,GAAG,GAEzC,OAAO,GAAS,YACjB,EAAuB,EAAS,EAAU,EAKjD,SAAS,EAAa,EAAsC,CAC1D,IAAM,EAAa,GAAA,EAAA,EAAA,cAA8B,EAAO,CAAC,CAEzD,MAAO,CACL,0BACE,EACA,IACG,CACC,GAAa,EAAQ,OAAS,EAAU,MAC1C,EAAW,UAAW,EAAU,KAAM,EAAS,EAAU,EAI7D,qBAAsB,EAAgB,IAAiC,CACjE,EAAQ,OAAS,GAAW,KAC9B,EAAW,SAAU,EAAQ,KAAM,EAAS,EAAU,CAEtD,EAAW,UAAW,EAAQ,KAAM,EAAS,EAAU,EAG5D,CAGH,SAAgB,GAAwC,CACtD,OAAO"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { PluginFactory, State } from "@real-router/core";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Lifecycle hook callback for route transitions.
|
|
6
|
+
* Fire-and-forget: return values are ignored, errors are caught and warned.
|
|
7
|
+
*/
|
|
8
|
+
type LifecycleHook = (toState: State, fromState: State | undefined) => void;
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/factory.d.ts
|
|
11
|
+
declare function lifecyclePluginFactory(): PluginFactory;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/index.d.ts
|
|
14
|
+
/**
|
|
15
|
+
* Module augmentation for real-router.
|
|
16
|
+
* Extends Route interface with lifecycle hooks.
|
|
17
|
+
*/
|
|
18
|
+
declare module "@real-router/core" {
|
|
19
|
+
interface Route {
|
|
20
|
+
/** Called when this route segment is newly activated (entered). */
|
|
21
|
+
onEnter?: LifecycleHook;
|
|
22
|
+
/** Called when this route segment stays active but params changed. */
|
|
23
|
+
onStay?: LifecycleHook;
|
|
24
|
+
/** Called when this route segment is deactivated (left). */
|
|
25
|
+
onLeave?: LifecycleHook;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { type LifecycleHook, lifecyclePluginFactory };
|
|
30
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{getPluginApi as e}from"@real-router/core/api";function t(e){return(t,n,r,i)=>{let a=e.getRouteConfig(n)?.[t];typeof a==`function`&&a(r,i)}}function n(n){let r=t(e(n));return{onTransitionLeaveApprove:(e,t)=>{t&&e.name!==t.name&&r(`onLeave`,t.name,e,t)},onTransitionSuccess:(e,t)=>{e.name===t?.name?r(`onStay`,e.name,e,t):r(`onEnter`,e.name,e,t)}}}function r(){return n}export{r as lifecyclePluginFactory};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/factory.ts"],"sourcesContent":["import { getPluginApi } from \"@real-router/core/api\";\n\nimport type { LifecycleHook } from \"./types\";\nimport type { PluginFactory, State } from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\n\nfunction createInvokeHook(api: PluginApi) {\n return (\n hookName: \"onEnter\" | \"onStay\" | \"onLeave\",\n routeName: string,\n toState: State,\n fromState: State | undefined,\n ): void => {\n const hook = api.getRouteConfig(routeName)?.[hookName];\n\n if (typeof hook === \"function\") {\n (hook as LifecycleHook)(toState, fromState);\n }\n };\n}\n\nfunction createPlugin(router: Parameters<PluginFactory>[0]) {\n const invokeHook = createInvokeHook(getPluginApi(router));\n\n return {\n onTransitionLeaveApprove: (\n toState: State,\n fromState: State | undefined,\n ) => {\n if (fromState && toState.name !== fromState.name) {\n invokeHook(\"onLeave\", fromState.name, toState, fromState);\n }\n },\n\n onTransitionSuccess: (toState: State, fromState: State | undefined) => {\n if (toState.name === fromState?.name) {\n invokeHook(\"onStay\", toState.name, toState, fromState);\n } else {\n invokeHook(\"onEnter\", toState.name, toState, fromState);\n }\n },\n };\n}\n\nexport function lifecyclePluginFactory(): PluginFactory {\n return createPlugin;\n}\n"],"mappings":"qDAMA,SAAS,EAAiB,EAAgB,CACxC,OACE,EACA,EACA,EACA,IACS,CACT,IAAM,EAAO,EAAI,eAAe,EAAU,GAAG,GAEzC,OAAO,GAAS,YACjB,EAAuB,EAAS,EAAU,EAKjD,SAAS,EAAa,EAAsC,CAC1D,IAAM,EAAa,EAAiB,EAAa,EAAO,CAAC,CAEzD,MAAO,CACL,0BACE,EACA,IACG,CACC,GAAa,EAAQ,OAAS,EAAU,MAC1C,EAAW,UAAW,EAAU,KAAM,EAAS,EAAU,EAI7D,qBAAsB,EAAgB,IAAiC,CACjE,EAAQ,OAAS,GAAW,KAC9B,EAAW,SAAU,EAAQ,KAAM,EAAS,EAAU,CAEtD,EAAW,UAAW,EAAQ,KAAM,EAAS,EAAU,EAG5D,CAGH,SAAgB,GAAwC,CACtD,OAAO"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@real-router/lifecycle-plugin",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "commonjs",
|
|
5
|
+
"description": "Route-level lifecycle hooks: onEnter, onStay, onLeave",
|
|
6
|
+
"main": "./dist/cjs/index.js",
|
|
7
|
+
"module": "./dist/esm/index.mjs",
|
|
8
|
+
"types": "./dist/esm/index.d.mts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"development": "./src/index.ts",
|
|
12
|
+
"types": {
|
|
13
|
+
"import": "./dist/esm/index.d.mts",
|
|
14
|
+
"require": "./dist/cjs/index.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"import": "./dist/esm/index.mjs",
|
|
17
|
+
"require": "./dist/cjs/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/greydragon888/real-router.git"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"real-router",
|
|
30
|
+
"lifecycle",
|
|
31
|
+
"hooks",
|
|
32
|
+
"onEnter",
|
|
33
|
+
"onStay",
|
|
34
|
+
"onLeave"
|
|
35
|
+
],
|
|
36
|
+
"author": {
|
|
37
|
+
"name": "Oleg Ivanov",
|
|
38
|
+
"email": "greydragon888@gmail.com",
|
|
39
|
+
"url": "https://github.com/greydragon888"
|
|
40
|
+
},
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/greydragon888/real-router/issues"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/greydragon888/real-router",
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsdown --config-loader unrun",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"type-check": "tsc --noEmit",
|
|
50
|
+
"lint": "eslint --cache --ext .ts src/ tests/ --fix --max-warnings 0",
|
|
51
|
+
"lint:package": "publint",
|
|
52
|
+
"lint:types": "attw --pack .",
|
|
53
|
+
"build:dist-only": "tsdown --config-loader unrun"
|
|
54
|
+
},
|
|
55
|
+
"sideEffects": false,
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@real-router/core": "workspace:^"
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/factory.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { getPluginApi } from "@real-router/core/api";
|
|
2
|
+
|
|
3
|
+
import type { LifecycleHook } from "./types";
|
|
4
|
+
import type { PluginFactory, State } from "@real-router/core";
|
|
5
|
+
import type { PluginApi } from "@real-router/core/api";
|
|
6
|
+
|
|
7
|
+
function createInvokeHook(api: PluginApi) {
|
|
8
|
+
return (
|
|
9
|
+
hookName: "onEnter" | "onStay" | "onLeave",
|
|
10
|
+
routeName: string,
|
|
11
|
+
toState: State,
|
|
12
|
+
fromState: State | undefined,
|
|
13
|
+
): void => {
|
|
14
|
+
const hook = api.getRouteConfig(routeName)?.[hookName];
|
|
15
|
+
|
|
16
|
+
if (typeof hook === "function") {
|
|
17
|
+
(hook as LifecycleHook)(toState, fromState);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function createPlugin(router: Parameters<PluginFactory>[0]) {
|
|
23
|
+
const invokeHook = createInvokeHook(getPluginApi(router));
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
onTransitionLeaveApprove: (
|
|
27
|
+
toState: State,
|
|
28
|
+
fromState: State | undefined,
|
|
29
|
+
) => {
|
|
30
|
+
if (fromState && toState.name !== fromState.name) {
|
|
31
|
+
invokeHook("onLeave", fromState.name, toState, fromState);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
onTransitionSuccess: (toState: State, fromState: State | undefined) => {
|
|
36
|
+
if (toState.name === fromState?.name) {
|
|
37
|
+
invokeHook("onStay", toState.name, toState, fromState);
|
|
38
|
+
} else {
|
|
39
|
+
invokeHook("onEnter", toState.name, toState, fromState);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function lifecyclePluginFactory(): PluginFactory {
|
|
46
|
+
return createPlugin;
|
|
47
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { LifecycleHook } from "./types";
|
|
2
|
+
|
|
3
|
+
export { lifecyclePluginFactory } from "./factory";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Module augmentation for real-router.
|
|
7
|
+
* Extends Route interface with lifecycle hooks.
|
|
8
|
+
*/
|
|
9
|
+
declare module "@real-router/core" {
|
|
10
|
+
interface Route {
|
|
11
|
+
/** Called when this route segment is newly activated (entered). */
|
|
12
|
+
onEnter?: LifecycleHook;
|
|
13
|
+
/** Called when this route segment stays active but params changed. */
|
|
14
|
+
onStay?: LifecycleHook;
|
|
15
|
+
/** Called when this route segment is deactivated (left). */
|
|
16
|
+
onLeave?: LifecycleHook;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type { LifecycleHook } from "./types";
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { State } from "@real-router/core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lifecycle hook callback for route transitions.
|
|
5
|
+
* Fire-and-forget: return values are ignored, errors are caught and warned.
|
|
6
|
+
*/
|
|
7
|
+
export type LifecycleHook = (
|
|
8
|
+
toState: State,
|
|
9
|
+
fromState: State | undefined,
|
|
10
|
+
) => void;
|