Skip to content

Commit bac6360

Browse files
authored
feat(commands): add stats command (#712)
1 parent 11e6420 commit bac6360

15 files changed

Lines changed: 2349 additions & 65 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ For complete details on each command, refer to the following documents:
112112
- [`config`](./docs/cli/config.md)
113113
- [`cache`](./docs/cli/cache.md)
114114
- [`extract integrity`](./docs/cli/extract-integrity.md)
115+
- [`stats`](./docs/cli/stats.md)
115116

116117
Each link provides access to the full documentation for the command, including additional details, options, and usage examples.
117118

bin/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ prog
135135
.example("nsecure extract integrity lodash@^4.1.2")
136136
.action(commands.extractIntegrity.main);
137137

138+
prog
139+
.command("stats")
140+
.describe(i18n.getTokenSync("cli.commands.stats.desc"))
141+
.example("nsecure stats")
142+
.action(commands.stats.main);
143+
138144
prog.parse(process.argv);
139145

140146
function defaultScannerCommand(name, options = {}) {

docs/cli/images/stats.PNG

281 KB
Loading

docs/cli/stats.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## 📝 Command `stats`
2+
3+
The `stats` displays the statistics of the last performed scan such as :
4+
5+
- The total execution time of the scan
6+
- The total number of API calls made
7+
- every stats about the API calls made
8+
- The total number of errors encountered
9+
- every error encountered
10+
11+
<p align="center">
12+
<img src="./images/stats.PNG">
13+
</p>
14+
15+
## 📜 Syntax
16+
17+
```bash
18+
$ nsecure stats
19+
```

i18n/arabic.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const cli = {
88
successfully_written_json: tS`تم كتابة ملف النتائج بنجاح في: ${0}`,
99
http_server_started: "تم تشغيل خادم HTTP على:",
1010
missingEnv: tS`متغير البيئة ${0} مفقود!`,
11-
stat: tS`${0} ${1} في ${2}`,
11+
stat: tS`${0}${1} في ${2}`,
1212
error: {
1313
name: tS`اسم ${0}: ${1}`,
1414
message: tS`الرسالة: ${0}`,
@@ -88,6 +88,13 @@ const cli = {
8888
missingSpecVersion: tS`يجب تحديد إصدار لحزمة '${0}'.`,
8989
invalidSpec: tS`مواصفات الحزمة '${0}' غير صالحة.`,
9090
specNotFound: tS`لم يتم العثور على مواصفات الحزمة '${0}' في سجل npm.`
91+
},
92+
stats: {
93+
desc: "عرض إحصائيات المسح.",
94+
elapsed: tS`مدة المسح: ${0}`,
95+
stats: tS`عدد استدعاءات API: ${0}`,
96+
error: "يجب إجراء مسح قبل عرض الإحصائيات.",
97+
errors: tS`عدد الأخطاء: ${0}`
9198
}
9299
},
93100
startHttp: {

i18n/english.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const cli = {
1010
successfully_written_json: tS`Successfully written results file at: ${0}`,
1111
http_server_started: "HTTP Server started on:",
1212
missingEnv: tS`Environment variable ${0} is missing!`,
13-
stat: tS`${0} ${1} in ${2}`,
13+
stat: tS`${0}${1} in ${2}`,
1414
error: {
1515
name: tS`${0} name: ${1}`,
1616
message: tS`Message: ${0}`,
@@ -90,6 +90,13 @@ const cli = {
9090
missingSpecVersion: tS`You must specify a version for '${0}' package.`,
9191
invalidSpec: tS`The package spec '${0}' is invalid.`,
9292
specNotFound: tS`The package spec '${0}' could not be found from the npm registry.`
93+
},
94+
stats: {
95+
desc: "Display the stats of a scan.",
96+
elapsed: tS`Scan duration: ${0}`,
97+
stats: tS`API calls count: ${0}`,
98+
error: "A scan must be performed before displaying stats.",
99+
errors: tS`Error count: ${0}`
93100
}
94101
},
95102
startHttp: {

i18n/french.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const cli = {
1010
successfully_written_json: tS`Ecriture du fichier de résultats réalisée avec succès ici : ${0}`,
1111
http_server_started: "Serveur HTTP démarré sur :",
1212
missingEnv: tS`La variable d'environnement ${0} est manquante!`,
13-
stat: tS`${0} ${1} en ${2}`,
13+
stat: tS`${0}${1} en ${2}`,
1414
error: {
1515
name: tS`Nom ${0}: ${1}`,
1616
message: tS`Message: ${0}`,
@@ -90,6 +90,13 @@ const cli = {
9090
missingSpecVersion: tS`Vous devez spécifier une version pour le package '${0}'.`,
9191
invalidSpec: tS`La spécification '${0}' est invalide.`,
9292
specNotFound: tS`La spécification '${0}' n'a pas pu être trouvée dans le registre npm.`
93+
},
94+
stats: {
95+
desc: "Afficher les statistiques d'un scan.",
96+
elapsed: tS`Durée du scan: ${0}`,
97+
stats: tS`Nombre d'appels API: ${0}`,
98+
error: "Un scan doit être effectué avant d'afficher les statistiques.",
99+
errors: tS`Nombre d'erreurs: ${0}`
93100
}
94101
},
95102
startHttp: {

i18n/turkish.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const cli = {
1010
successfully_written_json: tS`Sonuç dosyası başarıyla yazıldı: ${0}`,
1111
http_server_started: "HTTP Sunucusu başlatıldı:",
1212
missingEnv: tS`${0} ortam değişkeni eksik!`,
13-
stat: tS`${0} ${1} içinde ${2}`,
13+
stat: tS`${0}${1} içinde ${2}`,
1414
error: {
1515
name: tS`${0} adı: ${1}`,
1616
message: tS`Mesaj: ${0}`,
@@ -90,6 +90,13 @@ const cli = {
9090
missingSpecVersion: tS`'${0}' paketi için bir sürüm belirtmelisiniz.`,
9191
invalidSpec: tS`'${0}' paket özelliği geçersiz.`,
9292
specNotFound: tS`'${0}' paket özelliği npm kayıt defterinde bulunamadı.`
93+
},
94+
stats: {
95+
desc: "Bir taramanın istatistiklerini görüntüle.",
96+
elapsed: tS`Tarama süresi: ${0}`,
97+
stats: tS`API çağrı sayısı: ${0}`,
98+
error: "İstatistikleri görüntülemeden önce bir tarama yapılmalıdır.",
99+
errors: tS`Hata sayısı: ${0}`
93100
}
94101
},
95102
startHttp: {

src/commands/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ export * as scorecard from "./scorecard.js";
88
export * as report from "./report.js";
99
export * as cache from "./cache.js";
1010
export * as extractIntegrity from "./extract-integrity.js";
11+
export * as stats from "./stats.js";

src/commands/loggers/logger.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Import Third-party Dependencies
2+
import * as i18n from "@nodesecure/i18n";
3+
import ms from "ms";
4+
5+
// Import Internal Dependencies
6+
import kleur from "../../utils/styleText.js";
7+
8+
export function log(token, ...args) {
9+
console.log(kleur.white().bold(i18n.getTokenSync(token, ...args)));
10+
}
11+
12+
export function logError(token, ...args) {
13+
console.log(kleur.red().bold(i18n.getTokenSync(token, ...args)));
14+
}
15+
16+
export function logScannerStat(stat, isVerbose = true) {
17+
console.log(kleur.bold.white(
18+
i18n.getTokenSync("cli.stat",
19+
isVerbose ? kleur.blue().bold("verbose ") : "",
20+
stat.name,
21+
colorExecutionTime(stat.executionTime)
22+
)));
23+
}
24+
25+
export function logScannerError(error, phase) {
26+
console.log(kleur.bold.white(
27+
i18n.getTokenSync("cli.error.name",
28+
kleur.red().bold("error"),
29+
error.name
30+
)));
31+
32+
if (error.message) {
33+
console.log(i18n.getTokenSync("cli.error.message",
34+
error.message
35+
));
36+
}
37+
38+
if (phase) {
39+
console.log(i18n.getTokenSync("cli.error.phase",
40+
phase
41+
));
42+
}
43+
44+
if (error.statusCode) {
45+
console.log(i18n.getTokenSync("cli.error.statusCode",
46+
error.statusCode
47+
));
48+
}
49+
50+
console.log(i18n.getTokenSync("cli.error.executionTime",
51+
colorExecutionTime(error.executionTime)
52+
));
53+
54+
if (error.stack) {
55+
console.log(i18n.getTokenSync("cli.error.stack",
56+
error.stack
57+
));
58+
}
59+
}
60+
61+
export function formatMs(time) {
62+
return ms(Number(time.toFixed(2)));
63+
}
64+
65+
function colorExecutionTime(timeMs) {
66+
const formatted = formatMs(timeMs);
67+
if (timeMs <= 1_000) {
68+
return kleur.green().bold(formatted);
69+
}
70+
else if (timeMs <= 5_000) {
71+
return kleur.cyan().bold(formatted);
72+
}
73+
else if (timeMs <= 30_000) {
74+
return kleur.yellow().bold(formatted);
75+
}
76+
77+
return kleur.red().bold(formatted);
78+
}

0 commit comments

Comments
 (0)