stack-analyze 1.1.6 → 1.1.9
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/CHANGELOG.md +37 -3
- package/about.js +110 -0
- package/cli.js +318 -114
- package/demo.js +20 -0
- package/functions/animeInfo.js +9 -42
- package/functions/bitly.js +5 -7
- package/functions/cryptoList.js +23 -49
- package/functions/gitUser.js +6 -9
- package/functions/hardware.js +191 -231
- package/functions/moviesInfo.js +8 -41
- package/functions/multipleStack.js +19 -34
- package/functions/pageSpeed.js +13 -13
- package/functions/scraping.js +153 -0
- package/functions/singleStack.js +19 -37
- package/functions/twitch.js +44 -0
- package/{index.js → index.cjs} +202 -60
- package/index.mjs +496 -0
- package/package.json +24 -17
- package/readme.md +13 -5
- package/utils.js +15 -0
- package/about/index.js +0 -56
- package/hash/aboutOpts.js +0 -55
- package/hash/hardwareTools.js +0 -47
- package/hash/infoTools.js +0 -90
- package/hash/mainTools.js +0 -67
- package/models/aboutTables.js +0 -40
package/functions/moviesInfo.js
CHANGED
|
@@ -1,54 +1,23 @@
|
|
|
1
1
|
// modules
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { default as axios } from "axios";
|
|
3
|
+
import colors from "colors";
|
|
4
|
+
import { printTable } from "console-table-printer";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @description movie info tool
|
|
8
|
-
* @param { string } api_key - key required for api tool
|
|
9
8
|
* @param { string } query - search any movie
|
|
10
9
|
* @returns { Promise<void> } - return movie lisy
|
|
11
10
|
*/
|
|
12
|
-
const movieDB = async (
|
|
11
|
+
const movieDB = async (query, token) => {
|
|
13
12
|
try {
|
|
14
13
|
const { data } = await axios.get("https://api.themoviedb.org/3/search/movie", {
|
|
15
14
|
params: {
|
|
16
|
-
api_key,
|
|
15
|
+
api_key: token,
|
|
17
16
|
query,
|
|
18
17
|
page: 1
|
|
19
18
|
}
|
|
20
19
|
});
|
|
21
20
|
|
|
22
|
-
const movieList = new Table({
|
|
23
|
-
columns: [
|
|
24
|
-
{
|
|
25
|
-
name: "title",
|
|
26
|
-
alignment: "left",
|
|
27
|
-
color: "green"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: "original_language",
|
|
31
|
-
alignment: "left",
|
|
32
|
-
color: "green"
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: "popularity",
|
|
36
|
-
alignment: "left",
|
|
37
|
-
color: "yellow"
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
name: "vote_average",
|
|
41
|
-
alignment: "left",
|
|
42
|
-
color: "yellow"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
name: "release_date",
|
|
46
|
-
alignment: "left",
|
|
47
|
-
color: "yellow"
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
});
|
|
51
|
-
|
|
52
21
|
const movieData = data.results
|
|
53
22
|
.map(({
|
|
54
23
|
title,
|
|
@@ -72,13 +41,11 @@ const movieDB = async (api_key, query) => {
|
|
|
72
41
|
})
|
|
73
42
|
.filter(({ release_date }) => release_date !== undefined && release_date !== "");
|
|
74
43
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
movieList.printTable();
|
|
44
|
+
printTable(movieData);
|
|
78
45
|
} catch (err) {
|
|
79
|
-
console.error(red(err.message));
|
|
46
|
+
console.error(colors.red(err.message));
|
|
80
47
|
}
|
|
81
48
|
};
|
|
82
49
|
|
|
83
50
|
// export
|
|
84
|
-
|
|
51
|
+
export default movieDB;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// modules
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import figlet from "figlet";
|
|
3
|
+
import Wappalyzer from "wappalyzer";
|
|
4
|
+
import colors from "colors";
|
|
5
|
+
import { printTable } from "console-table-printer";
|
|
6
|
+
|
|
7
|
+
// list format
|
|
8
|
+
import { listFormat } from "../utils.js";
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
*
|
|
@@ -14,26 +17,6 @@ const { Table } = require("console-table-printer");
|
|
|
14
17
|
const multipleStack = async (urls) => {
|
|
15
18
|
const wappalyzer = await new Wappalyzer();
|
|
16
19
|
|
|
17
|
-
const p = new Table({
|
|
18
|
-
columns: [
|
|
19
|
-
{
|
|
20
|
-
name: "techName",
|
|
21
|
-
alignment: "left",
|
|
22
|
-
color: "cyan"
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
name: "techWebsite",
|
|
26
|
-
alignment: "left",
|
|
27
|
-
color: "green"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: "techCategories",
|
|
31
|
-
alignment: "left",
|
|
32
|
-
color: "cyan"
|
|
33
|
-
}
|
|
34
|
-
]
|
|
35
|
-
});
|
|
36
|
-
|
|
37
20
|
try {
|
|
38
21
|
await wappalyzer.init();
|
|
39
22
|
|
|
@@ -55,23 +38,25 @@ const multipleStack = async (urls) => {
|
|
|
55
38
|
name,
|
|
56
39
|
website,
|
|
57
40
|
categories
|
|
58
|
-
}) =>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
41
|
+
}) => {
|
|
42
|
+
const stackCategories = categories.map(({ name }) => name);
|
|
43
|
+
return {
|
|
44
|
+
techName: name,
|
|
45
|
+
techWebsite: website,
|
|
46
|
+
techCategories: listFormat.format(stackCategories)
|
|
47
|
+
};
|
|
48
|
+
});
|
|
63
49
|
|
|
64
|
-
console.info(green(textSync(url, "Small")));
|
|
50
|
+
console.info(colors.green(figlet.textSync(url, "Small")));
|
|
65
51
|
console.group();
|
|
66
|
-
|
|
67
|
-
p.printTable();
|
|
52
|
+
printTable(stackResult);
|
|
68
53
|
console.groupEnd();
|
|
69
54
|
});
|
|
70
55
|
} catch (err) {
|
|
71
|
-
console.error(red(err.message));
|
|
56
|
+
console.error(colors.red(err.message));
|
|
72
57
|
}
|
|
73
58
|
|
|
74
59
|
await wappalyzer.destroy();
|
|
75
60
|
};
|
|
76
61
|
|
|
77
|
-
|
|
62
|
+
export default multipleStack;
|
package/functions/pageSpeed.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// modules
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { default as axios } from "axios";
|
|
3
|
+
import { SingleBar } from "cli-progress";
|
|
4
|
+
import colors from "colors";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @description async function mobile website pagespeed
|
|
@@ -39,13 +39,13 @@ const pageSpeed = async (url) => {
|
|
|
39
39
|
switch (true) {
|
|
40
40
|
case (mobile === 1 || mobile <= 49):
|
|
41
41
|
case (desktop === 1 || desktop <= 49):
|
|
42
|
-
b1 = new
|
|
42
|
+
b1 = new SingleBar({
|
|
43
43
|
format: "Mobile Result | {bar} || {value}/{total} || bad".red,
|
|
44
44
|
barCompleteChar: "\u2588",
|
|
45
45
|
barIncompleteChar: "\u2591",
|
|
46
46
|
hideCursor: true
|
|
47
47
|
});
|
|
48
|
-
b2 = new
|
|
48
|
+
b2 = new SingleBar({
|
|
49
49
|
format: "Desktop Result | {bar} || {value}/{total} || bad".red,
|
|
50
50
|
barCompleteChar: "\u2588",
|
|
51
51
|
barIncompleteChar: "\u2591",
|
|
@@ -54,13 +54,13 @@ const pageSpeed = async (url) => {
|
|
|
54
54
|
break;
|
|
55
55
|
case (mobile === 50 || mobile <= 89):
|
|
56
56
|
case (desktop === 50 || desktop <= 89):
|
|
57
|
-
b1 = new
|
|
57
|
+
b1 = new SingleBar({
|
|
58
58
|
format: "Mobile Result | {bar} || {value}/{total} || decent".yellow,
|
|
59
59
|
barCompleteChar: "\u2588",
|
|
60
60
|
barIncompleteChar: "\u2591",
|
|
61
61
|
hideCursor: true
|
|
62
62
|
});
|
|
63
|
-
b2 = new
|
|
63
|
+
b2 = new SingleBar({
|
|
64
64
|
format: "Desktop Result | {bar} || {value}/{total} || decent".yellow,
|
|
65
65
|
barCompleteChar: "\u2588",
|
|
66
66
|
barIncompleteChar: "\u2591",
|
|
@@ -69,13 +69,13 @@ const pageSpeed = async (url) => {
|
|
|
69
69
|
break;
|
|
70
70
|
case (mobile >= 90 || mobile === 100):
|
|
71
71
|
case (desktop >= 90 || desktop === 100):
|
|
72
|
-
b1 = new
|
|
72
|
+
b1 = new SingleBar({
|
|
73
73
|
format: "Mobile Result | {bar} || {value}/{total} || excelent".green,
|
|
74
74
|
barCompleteChar: "\u2588",
|
|
75
75
|
barIncompleteChar: "\u2591",
|
|
76
76
|
hideCursor: true
|
|
77
77
|
});
|
|
78
|
-
b2 = new
|
|
78
|
+
b2 = new SingleBar({
|
|
79
79
|
format: "Desktop Result | {bar} || {value}/{total} || excelent".green,
|
|
80
80
|
barCompleteChar: "\u2588",
|
|
81
81
|
barIncompleteChar: "\u2591",
|
|
@@ -83,13 +83,13 @@ const pageSpeed = async (url) => {
|
|
|
83
83
|
});
|
|
84
84
|
break;
|
|
85
85
|
default:
|
|
86
|
-
b1 = new
|
|
86
|
+
b1 = new SingleBar({
|
|
87
87
|
format: "Mobile Result | {bar} || {value}/{total} || undifined",
|
|
88
88
|
barCompleteChar: "\u2588",
|
|
89
89
|
barIncompleteChar: "\u2591",
|
|
90
90
|
hideCursor: true
|
|
91
91
|
});
|
|
92
|
-
b2 = new
|
|
92
|
+
b2 = new SingleBar({
|
|
93
93
|
format: "Desktop Result | {bar} || {value}/{total} || undifined",
|
|
94
94
|
barCompleteChar: "\u2588",
|
|
95
95
|
barIncompleteChar: "\u2591",
|
|
@@ -109,8 +109,8 @@ const pageSpeed = async (url) => {
|
|
|
109
109
|
b1.stop();
|
|
110
110
|
b2.stop();
|
|
111
111
|
} catch (err) {
|
|
112
|
-
console.error(red(err.message));
|
|
112
|
+
console.error(colors.red(err.message));
|
|
113
113
|
}
|
|
114
114
|
};
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
export default pageSpeed;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { default as axios } from "axios";
|
|
2
|
+
import { load } from "cheerio";
|
|
3
|
+
import colors from "colors";
|
|
4
|
+
import { printTable } from "console-table-printer";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} options
|
|
8
|
+
* @property {function(): Promise<void>} options.title
|
|
9
|
+
* @property {function(): Promise<void>} options.images
|
|
10
|
+
* @property {function(): Promise<void>} options.metadata
|
|
11
|
+
* @property {function(): Promise<void>} options.headings
|
|
12
|
+
* @property {function(): Promise<void>} options.table_heading
|
|
13
|
+
* @property {function(): Promise<void>} options.table_data
|
|
14
|
+
* @property {function(): Promise<void>} options.links
|
|
15
|
+
* @property {function(): Promise<void>} options.cites
|
|
16
|
+
*
|
|
17
|
+
* @param {string} url
|
|
18
|
+
* @returns {options}
|
|
19
|
+
*/
|
|
20
|
+
export default function scrape(url) {
|
|
21
|
+
let $;
|
|
22
|
+
|
|
23
|
+
const scraping = axios.create({
|
|
24
|
+
baseURL: url
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const title = async () => {
|
|
28
|
+
try {
|
|
29
|
+
const { data } = await scraping.get("");
|
|
30
|
+
$ = load(data);
|
|
31
|
+
|
|
32
|
+
console.info("title page:", $("title").text());
|
|
33
|
+
} catch (err) { console.error(colors.red(err.message)); }
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const images = async () => {
|
|
37
|
+
try {
|
|
38
|
+
const { data } = await scraping.get("");
|
|
39
|
+
$ = load(data);
|
|
40
|
+
|
|
41
|
+
const imgs = $("img").map((i, el) => ({
|
|
42
|
+
imagePath: $(el).attr("src"),
|
|
43
|
+
imageTitle: $(el).attr("alt")
|
|
44
|
+
})).toArray();
|
|
45
|
+
|
|
46
|
+
imgs.length === 0
|
|
47
|
+
? console.info("no found images")
|
|
48
|
+
: printTable(imgs);
|
|
49
|
+
} catch (err) { console.error(colors.red(err.message)); }
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const metadata = async () => {
|
|
53
|
+
try {
|
|
54
|
+
const { data } = await scraping.get("");
|
|
55
|
+
$ = load(data);
|
|
56
|
+
|
|
57
|
+
const metadataList = $("meta").map((i, el) => ({
|
|
58
|
+
metaInfo: $(el).attr("name"),
|
|
59
|
+
metaContent: $(el).attr("content")
|
|
60
|
+
})).toArray()
|
|
61
|
+
.filter(({ metaInfo }) => metaInfo !== undefined);
|
|
62
|
+
|
|
63
|
+
printTable(metadataList);
|
|
64
|
+
} catch (err) { console.error(colors.red(err.message)); }
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const headings = async () => {
|
|
68
|
+
try {
|
|
69
|
+
const { data } = await scraping.get("");
|
|
70
|
+
$ = load(data);
|
|
71
|
+
|
|
72
|
+
const headingList = $("h1, h2, h3, h4, h5, h6").map((i, el) => ({
|
|
73
|
+
headingTag: $(el).prop("tagName"),
|
|
74
|
+
headingText: $(el).text()
|
|
75
|
+
})).toArray();
|
|
76
|
+
|
|
77
|
+
printTable(headingList);
|
|
78
|
+
} catch (err) { console.error(colors.red(err.message)); }
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const table_heading = async () => {
|
|
82
|
+
try {
|
|
83
|
+
const { data } = await scraping.get("");
|
|
84
|
+
$ = load(data);
|
|
85
|
+
|
|
86
|
+
const tableHeadList = $("th").map((i, el) => ({
|
|
87
|
+
headingRow: i,
|
|
88
|
+
text: $(el).text()
|
|
89
|
+
})).toArray();
|
|
90
|
+
|
|
91
|
+
tableHeadList.length === 0
|
|
92
|
+
? console.info("no found th tags")
|
|
93
|
+
: printTable(tableHeadList);
|
|
94
|
+
} catch (err) { console.error(colors.red(err.message)); }
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const table_data = async () => {
|
|
98
|
+
try {
|
|
99
|
+
const { data } = await scraping.get("");
|
|
100
|
+
$ = load(data);
|
|
101
|
+
|
|
102
|
+
const tableColumnList = $("td").map((i, el) => $(el).text()).toArray();
|
|
103
|
+
|
|
104
|
+
tableColumnList.length === 0
|
|
105
|
+
? console.info("no found td tags")
|
|
106
|
+
: printTable(tableColumnList);
|
|
107
|
+
} catch (err) { console.error(colors.red(err.message)); }
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
const links = async () => {
|
|
112
|
+
try {
|
|
113
|
+
const { data } = await scraping.get("");
|
|
114
|
+
$ = load(data);
|
|
115
|
+
|
|
116
|
+
const linkList = $("a").map((i, el) => ({
|
|
117
|
+
url: $(el).attr("href"),
|
|
118
|
+
text: $(el).text()
|
|
119
|
+
})).toArray()
|
|
120
|
+
.filter(({ url }) => url.indexOf("#") !== 0);
|
|
121
|
+
|
|
122
|
+
printTable(linkList);
|
|
123
|
+
} catch (err) { console.error(colors.red(err.message)); }
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const cites = async () => {
|
|
127
|
+
try {
|
|
128
|
+
const { data } = await scraping.get("");
|
|
129
|
+
$ = load(data);
|
|
130
|
+
|
|
131
|
+
const citeList = $("q, blockquote").map((i, el) => ({
|
|
132
|
+
citeTag: $(el).prop("tagName"),
|
|
133
|
+
citeLink: $(el).attr("cite"),
|
|
134
|
+
citeText: $(el).text()
|
|
135
|
+
})).toArray();
|
|
136
|
+
|
|
137
|
+
citeList.length === 0
|
|
138
|
+
? console.info("no found q and/or blockquote tags")
|
|
139
|
+
: printTable(citeList);
|
|
140
|
+
} catch (err) { console.error(colors.red(err.message)); }
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
title,
|
|
145
|
+
images,
|
|
146
|
+
metadata,
|
|
147
|
+
headings,
|
|
148
|
+
table_heading,
|
|
149
|
+
table_data,
|
|
150
|
+
links,
|
|
151
|
+
cites
|
|
152
|
+
};
|
|
153
|
+
}
|
package/functions/singleStack.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// module
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import Wappalyzer from "wappalyzer";
|
|
3
|
+
import figlet from "figlet";
|
|
4
|
+
import colors from "colors";
|
|
5
|
+
import { printTable } from "console-table-printer";
|
|
6
|
+
|
|
7
|
+
// list format
|
|
8
|
+
import { listFormat } from "../utils.js";
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
*
|
|
@@ -11,30 +14,9 @@ const { Table } = require("console-table-printer");
|
|
|
11
14
|
* @returns { Promise<void> } - return async results single web
|
|
12
15
|
*
|
|
13
16
|
*/
|
|
14
|
-
async function singleStack(url) {
|
|
17
|
+
export default async function singleStack(url) {
|
|
15
18
|
const wappalyzer = await new Wappalyzer;
|
|
16
19
|
|
|
17
|
-
const p = new Table({
|
|
18
|
-
columns: [
|
|
19
|
-
{
|
|
20
|
-
name: "techName",
|
|
21
|
-
alignment: "left",
|
|
22
|
-
color: "cyan"
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
name: "techWebsite",
|
|
26
|
-
alignment: "left",
|
|
27
|
-
color: "green"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: "techCategories",
|
|
31
|
-
alignment: "left",
|
|
32
|
-
color: "cyan"
|
|
33
|
-
}
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
});
|
|
37
|
-
|
|
38
20
|
try {
|
|
39
21
|
await wappalyzer.init();
|
|
40
22
|
|
|
@@ -44,22 +26,22 @@ async function singleStack(url) {
|
|
|
44
26
|
name,
|
|
45
27
|
website,
|
|
46
28
|
categories
|
|
47
|
-
}) =>
|
|
48
|
-
|
|
49
|
-
techWebsite: website,
|
|
50
|
-
techCategories: categories.map(({ name }) => name).join(", ")
|
|
51
|
-
}));
|
|
29
|
+
}) => {
|
|
30
|
+
const stackCategories = categories.map(({ name }) => name);
|
|
52
31
|
|
|
53
|
-
|
|
32
|
+
return {
|
|
33
|
+
techName: name,
|
|
34
|
+
techWebsite: website,
|
|
35
|
+
techCategories: listFormat.format(stackCategories)
|
|
36
|
+
};
|
|
37
|
+
});
|
|
54
38
|
|
|
55
|
-
|
|
39
|
+
console.info(colors.green(figlet.textSync(url)));
|
|
56
40
|
|
|
57
|
-
|
|
41
|
+
printTable(stackResult);
|
|
58
42
|
} catch (err) {
|
|
59
|
-
console.error(red(err.message));
|
|
43
|
+
console.error(colors.red(err.message));
|
|
60
44
|
}
|
|
61
45
|
|
|
62
46
|
await wappalyzer.destroy();
|
|
63
47
|
}
|
|
64
|
-
|
|
65
|
-
module.exports = singleStack;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// modules
|
|
2
|
+
import { default as axios } from "axios";
|
|
3
|
+
import { format } from "timeago.js";
|
|
4
|
+
import colors from "colors";
|
|
5
|
+
|
|
6
|
+
// table
|
|
7
|
+
import { printTable } from "console-table-printer";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @description twitch user info
|
|
12
|
+
* @param {string} twitchUser - twitch user for search
|
|
13
|
+
* @param {string} apiToken - twitch api token
|
|
14
|
+
* @returns { Promise<void> } - return twitch results
|
|
15
|
+
*/
|
|
16
|
+
const twitchInfo = async (twitchUser, twitchClient, apiToken) => {
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const { data: twitchData } = await axios.get(`https://api.twitch.tv/helix/users?login=${twitchUser}`, {
|
|
20
|
+
headers: {
|
|
21
|
+
Authorization: `Bearer ${apiToken}`,
|
|
22
|
+
"Client-Id": twitchClient
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const result = twitchData.data.map(({
|
|
27
|
+
display_name,
|
|
28
|
+
broadcaster_type,
|
|
29
|
+
view_count,
|
|
30
|
+
created_at
|
|
31
|
+
}) => ({
|
|
32
|
+
display_name,
|
|
33
|
+
broadcaster_type,
|
|
34
|
+
view_count,
|
|
35
|
+
createdTime: format(created_at)
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
printTable(result);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
console.error(colors.red(err));
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default twitchInfo;
|