@herodevs/cli 1.0.0-beta.2 → 1.2.0-beta.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 +30 -325
- package/bin/dev.js +6 -6
- package/bin/run.js +3 -2
- package/dist/api/client.d.ts +0 -2
- package/dist/api/client.js +19 -18
- package/dist/api/nes/nes.client.d.ts +4 -0
- package/dist/api/nes/nes.client.js +11 -0
- package/dist/api/queries/nes/sbom.js +5 -0
- package/dist/api/types/nes.types.d.ts +17 -3
- package/dist/api/types/nes.types.js +11 -1
- package/dist/commands/report/committers.d.ts +3 -2
- package/dist/commands/report/committers.js +75 -33
- package/dist/commands/report/purls.d.ts +4 -2
- package/dist/commands/report/purls.js +51 -31
- package/dist/commands/scan/eol.d.ts +13 -4
- package/dist/commands/scan/eol.js +112 -37
- package/dist/commands/scan/sbom.d.ts +4 -1
- package/dist/commands/scan/sbom.js +86 -33
- package/dist/hooks/prerun.js +8 -0
- package/dist/service/committers.svc.js +24 -3
- package/dist/service/eol/cdx.svc.d.ts +52 -0
- package/dist/service/eol/cdx.svc.js +58 -62
- package/dist/service/eol/eol.svc.d.ts +0 -21
- package/dist/service/eol/eol.svc.js +2 -62
- package/dist/service/eol/sbom.worker.d.ts +1 -0
- package/dist/service/eol/sbom.worker.js +26 -0
- package/dist/service/error.svc.d.ts +8 -0
- package/dist/service/error.svc.js +28 -0
- package/dist/service/log.svc.d.ts +5 -8
- package/dist/service/log.svc.js +5 -18
- package/dist/service/nes/nes.svc.js +4 -3
- package/dist/service/purls.svc.js +1 -1
- package/dist/ui/date.ui.d.ts +1 -0
- package/dist/ui/date.ui.js +15 -0
- package/dist/ui/eol.ui.d.ts +4 -3
- package/dist/ui/eol.ui.js +56 -15
- package/dist/ui/shared.us.d.ts +3 -0
- package/dist/ui/shared.us.js +13 -0
- package/package.json +13 -14
- package/dist/hooks/init/update.d.ts +0 -2
- package/dist/hooks/init/update.js +0 -5
- package/dist/hooks/prerun/CommandContextHook.js +0 -8
- package/dist/service/line.svc.d.ts +0 -24
- package/dist/service/line.svc.js +0 -61
- /package/dist/hooks/{prerun/CommandContextHook.d.ts → prerun.d.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseMomentToSimpleDate(momentDate: string | Date | number | null): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function parseMomentToSimpleDate(momentDate) {
|
|
2
|
+
// Only return empty string for null
|
|
3
|
+
if (momentDate === null)
|
|
4
|
+
return '';
|
|
5
|
+
try {
|
|
6
|
+
const dateObj = new Date(momentDate);
|
|
7
|
+
if (Number.isNaN(dateObj.getTime())) {
|
|
8
|
+
throw new Error('Invalid date');
|
|
9
|
+
}
|
|
10
|
+
return dateObj.toISOString().split('T')[0];
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new Error('Invalid date');
|
|
14
|
+
}
|
|
15
|
+
}
|
package/dist/ui/eol.ui.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
export declare function
|
|
1
|
+
import type { ComponentStatus, ScanResultComponentsMap } from '../api/types/nes.types.ts';
|
|
2
|
+
export declare function truncatePurl(purl: string): string;
|
|
3
|
+
export declare function colorizeStatus(status: ComponentStatus): string;
|
|
4
|
+
export declare function createStatusDisplay(components: ScanResultComponentsMap, all: boolean): Record<ComponentStatus, string[]>;
|
package/dist/ui/eol.ui.js
CHANGED
|
@@ -1,17 +1,58 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { ux } from '@oclif/core';
|
|
2
|
+
import { parseMomentToSimpleDate } from "./date.ui.js";
|
|
3
|
+
import { INDICATORS, STATUS_COLORS } from "./shared.us.js";
|
|
4
|
+
export function truncatePurl(purl) {
|
|
5
|
+
return purl.length > 60 ? `${purl.slice(0, 57)}...` : purl;
|
|
6
|
+
}
|
|
7
|
+
export function colorizeStatus(status) {
|
|
8
|
+
return ux.colorize(STATUS_COLORS[status], status);
|
|
9
|
+
}
|
|
10
|
+
function formatSimpleComponent(purl, status) {
|
|
11
|
+
const color = STATUS_COLORS[status];
|
|
12
|
+
return ` ${INDICATORS[status]} ${ux.colorize(color, truncatePurl(purl))}`;
|
|
13
|
+
}
|
|
14
|
+
function getDaysEolString(daysEol) {
|
|
15
|
+
// UNKNOWN || OK
|
|
16
|
+
if (daysEol === null) {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
// LTS
|
|
20
|
+
if (daysEol < 0) {
|
|
21
|
+
return `${Math.abs(daysEol)} days from now`;
|
|
22
|
+
}
|
|
23
|
+
// EOL
|
|
24
|
+
if (daysEol === 0) {
|
|
25
|
+
return 'today';
|
|
26
|
+
}
|
|
27
|
+
return `${daysEol} days ago`;
|
|
28
|
+
}
|
|
29
|
+
function formatDetailedComponent(purl, eolAt, daysEol, status) {
|
|
30
|
+
const simpleComponent = formatSimpleComponent(purl, status);
|
|
31
|
+
const eolAtString = parseMomentToSimpleDate(eolAt);
|
|
32
|
+
const daysEolString = getDaysEolString(daysEol);
|
|
33
|
+
const output = [`${simpleComponent}`, ` ⮑ EOL Date: ${eolAtString} (${daysEolString})`]
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.join('\n');
|
|
36
|
+
return output;
|
|
37
|
+
}
|
|
38
|
+
export function createStatusDisplay(components, all) {
|
|
39
|
+
const statusOutput = {
|
|
40
|
+
UNKNOWN: [],
|
|
41
|
+
OK: [],
|
|
42
|
+
LTS: [],
|
|
43
|
+
EOL: [],
|
|
7
44
|
};
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
45
|
+
// Single loop to separate and format components
|
|
46
|
+
for (const [purl, component] of components.entries()) {
|
|
47
|
+
const { status, eolAt, daysEol } = component.info;
|
|
48
|
+
if (all) {
|
|
49
|
+
if (status === 'UNKNOWN' || status === 'OK') {
|
|
50
|
+
statusOutput[status].push(formatSimpleComponent(purl, status));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (status === 'LTS' || status === 'EOL') {
|
|
54
|
+
statusOutput[status].push(formatDetailedComponent(purl, eolAt, daysEol, status));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return statusOutput;
|
|
17
58
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ux } from '@oclif/core';
|
|
2
|
+
export const STATUS_COLORS = {
|
|
3
|
+
EOL: 'red',
|
|
4
|
+
UNKNOWN: 'default',
|
|
5
|
+
OK: 'green',
|
|
6
|
+
LTS: 'yellow',
|
|
7
|
+
};
|
|
8
|
+
export const INDICATORS = {
|
|
9
|
+
EOL: ux.colorize(STATUS_COLORS.EOL, '✗'),
|
|
10
|
+
UNKNOWN: ux.colorize(STATUS_COLORS.UNKNOWN, '•'),
|
|
11
|
+
OK: ux.colorize(STATUS_COLORS.OK, '✔'),
|
|
12
|
+
LTS: ux.colorize(STATUS_COLORS.LTS, '⚡'),
|
|
13
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herodevs/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0-beta.1",
|
|
4
4
|
"author": "HeroDevs, Inc",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hd": "./bin/run.js"
|
|
@@ -19,13 +19,14 @@
|
|
|
19
19
|
"clean": "shx rm -rf dist && npm run clean:files && shx rm -rf node_modules",
|
|
20
20
|
"clean:files": "shx rm -f nes.**.csv nes.**.json nes.**.text",
|
|
21
21
|
"dev": "npm run build && ./bin/dev.js",
|
|
22
|
+
"dev:debug": "npm run build && DEBUG=* ./bin/dev.js",
|
|
22
23
|
"format": "biome format --write",
|
|
23
24
|
"lint": "biome lint --write",
|
|
24
25
|
"postpack": "shx rm -f oclif.manifest.json",
|
|
25
26
|
"prepack": "oclif manifest && oclif readme",
|
|
26
27
|
"pretest": "npm run lint && npm run typecheck",
|
|
27
28
|
"readme": "npm run ci:fix && npm run build && npm exec oclif readme",
|
|
28
|
-
"test": "
|
|
29
|
+
"test": "globstar -- node --import tsx --test \"test/**/*.test.ts\"",
|
|
29
30
|
"typecheck": "tsc --noEmit",
|
|
30
31
|
"version": "oclif readme && git add README.md"
|
|
31
32
|
},
|
|
@@ -39,9 +40,7 @@
|
|
|
39
40
|
"@cyclonedx/cdxgen": "^11.2.2",
|
|
40
41
|
"@oclif/core": "^4",
|
|
41
42
|
"@oclif/plugin-help": "^6",
|
|
42
|
-
"
|
|
43
|
-
"graphql": "^16.8.1",
|
|
44
|
-
"inquirer": "^12.5.0"
|
|
43
|
+
"graphql": "^16.8.1"
|
|
45
44
|
},
|
|
46
45
|
"devDependencies": {
|
|
47
46
|
"@biomejs/biome": "^1.8.3",
|
|
@@ -49,13 +48,16 @@
|
|
|
49
48
|
"@types/inquirer": "^9.0.7",
|
|
50
49
|
"@types/node": "^22",
|
|
51
50
|
"@types/sinon": "^17.0.4",
|
|
51
|
+
"globstar": "^1.0.0",
|
|
52
52
|
"oclif": "^4",
|
|
53
53
|
"shx": "^0.3.3",
|
|
54
54
|
"sinon": "^19.0.2",
|
|
55
|
+
"ts-node": "^10",
|
|
56
|
+
"tsx": "^4.19.3",
|
|
55
57
|
"typescript": "^5.8.0"
|
|
56
58
|
},
|
|
57
59
|
"engines": {
|
|
58
|
-
"node": ">=
|
|
60
|
+
"node": ">=18.0.0"
|
|
59
61
|
},
|
|
60
62
|
"files": [
|
|
61
63
|
"./bin",
|
|
@@ -70,15 +72,12 @@
|
|
|
70
72
|
"commands": "./dist/commands",
|
|
71
73
|
"plugins": [
|
|
72
74
|
"@oclif/plugin-help",
|
|
73
|
-
"@oclif/plugin-plugins"
|
|
74
|
-
"@oclif/plugin-update"
|
|
75
|
+
"@oclif/plugin-plugins"
|
|
75
76
|
],
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
}
|
|
77
|
+
"hooks": {
|
|
78
|
+
"prerun": "./dist/hooks/prerun.js"
|
|
79
|
+
},
|
|
80
|
+
"topicSeparator": " "
|
|
82
81
|
},
|
|
83
82
|
"types": "dist/index.d.ts"
|
|
84
83
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { initOclifLog, log } from "../../service/log.svc.js";
|
|
2
|
-
const hook = async (opts) => {
|
|
3
|
-
initOclifLog(opts.context.log, opts.context.log, opts.context.debug);
|
|
4
|
-
log.info = opts.context.log || log.info;
|
|
5
|
-
log.warn = opts.context.log || log.warn;
|
|
6
|
-
log.debug = opts.context.debug || log.debug;
|
|
7
|
-
};
|
|
8
|
-
export default hook;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { ComponentStatus, ScanResultComponent } from '../api/types/nes.types.ts';
|
|
2
|
-
export interface Line {
|
|
3
|
-
daysEol: number | null;
|
|
4
|
-
purl: ScanResultComponent['purl'];
|
|
5
|
-
info: {
|
|
6
|
-
eolAt: Date | null;
|
|
7
|
-
isEol: boolean;
|
|
8
|
-
};
|
|
9
|
-
status: ComponentStatus;
|
|
10
|
-
}
|
|
11
|
-
export declare function getStatusFromComponent(component: ScanResultComponent, daysEol: number | null): ComponentStatus;
|
|
12
|
-
export declare function daysBetween(date1: Date, date2: Date): number;
|
|
13
|
-
export declare function getDaysEolFromEolAt(eolAt: Date | null): number | null;
|
|
14
|
-
export declare function getMessageAndStatus(status: string, daysEol: number | null): {
|
|
15
|
-
stat: string;
|
|
16
|
-
msg: string;
|
|
17
|
-
};
|
|
18
|
-
export declare function formatLine(l: Line, idx: number, ctx: {
|
|
19
|
-
longest: number;
|
|
20
|
-
total: number;
|
|
21
|
-
}): {
|
|
22
|
-
name: string;
|
|
23
|
-
value: Line;
|
|
24
|
-
};
|
package/dist/service/line.svc.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
export function getStatusFromComponent(component, daysEol) {
|
|
2
|
-
const { info } = component;
|
|
3
|
-
if (component.status) {
|
|
4
|
-
if (info.isEol && component.status && component.status !== 'EOL') {
|
|
5
|
-
throw new Error(`isEol is true but status is not EOL: ${component.purl}`);
|
|
6
|
-
}
|
|
7
|
-
return component.status;
|
|
8
|
-
}
|
|
9
|
-
// If API fails to set status, we derive it based on other properties
|
|
10
|
-
if (daysEol === null) {
|
|
11
|
-
return info.isEol ? 'EOL' : 'OK';
|
|
12
|
-
}
|
|
13
|
-
if (daysEol > 0) {
|
|
14
|
-
// daysEol is positive means we're past the EOL date
|
|
15
|
-
return 'EOL';
|
|
16
|
-
}
|
|
17
|
-
// daysEol is zero or negative means we haven't reached EOL yet
|
|
18
|
-
return 'LTS';
|
|
19
|
-
}
|
|
20
|
-
export function daysBetween(date1, date2) {
|
|
21
|
-
const msPerDay = 1000 * 60 * 60 * 24 + 15; // milliseconds in a day plus 15 ms
|
|
22
|
-
return Math.round((date2.getTime() - date1.getTime()) / msPerDay);
|
|
23
|
-
}
|
|
24
|
-
export function getDaysEolFromEolAt(eolAt) {
|
|
25
|
-
return eolAt ? Math.abs(daysBetween(new Date(), eolAt)) : null;
|
|
26
|
-
}
|
|
27
|
-
export function getMessageAndStatus(status, daysEol) {
|
|
28
|
-
let msg = '';
|
|
29
|
-
let stat = '';
|
|
30
|
-
const stringifiedDaysEol = daysEol ? daysEol.toString() : 'unknown';
|
|
31
|
-
switch (status) {
|
|
32
|
-
case 'EOL': {
|
|
33
|
-
stat = 'EOL';
|
|
34
|
-
msg = `EOL'd ${stringifiedDaysEol} days ago.`;
|
|
35
|
-
break;
|
|
36
|
-
}
|
|
37
|
-
case 'LTS': {
|
|
38
|
-
stat = 'LTS';
|
|
39
|
-
msg = `Will go EOL in ${stringifiedDaysEol} days.`;
|
|
40
|
-
break;
|
|
41
|
-
}
|
|
42
|
-
case 'OK': {
|
|
43
|
-
stat = 'OK';
|
|
44
|
-
break;
|
|
45
|
-
}
|
|
46
|
-
default:
|
|
47
|
-
throw new Error(`Unknown status: ${status}`);
|
|
48
|
-
}
|
|
49
|
-
return { stat, msg };
|
|
50
|
-
}
|
|
51
|
-
export function formatLine(l, idx, ctx) {
|
|
52
|
-
const { daysEol, purl, status } = l;
|
|
53
|
-
const { stat, msg } = getMessageAndStatus(status, daysEol);
|
|
54
|
-
const padlen = ctx.total.toString().length;
|
|
55
|
-
const rownum = `${idx + 1}`.padStart(padlen, ' ');
|
|
56
|
-
const name = purl.padEnd(ctx.longest, ' ');
|
|
57
|
-
return {
|
|
58
|
-
name: `${rownum}. [${stat}] ${name} | ${msg}`,
|
|
59
|
-
value: l,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
File without changes
|