@herodevs/cli 2.0.0-beta.13 → 2.0.0-beta.15

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.
Files changed (63) hide show
  1. package/README.md +192 -20
  2. package/dist/api/apollo.client.d.ts +3 -0
  3. package/dist/api/apollo.client.js +53 -0
  4. package/dist/api/ci-token.client.d.ts +26 -0
  5. package/dist/api/ci-token.client.js +95 -0
  6. package/dist/api/errors.d.ts +8 -0
  7. package/dist/api/errors.js +13 -0
  8. package/dist/api/gql-operations.d.ts +3 -0
  9. package/dist/api/gql-operations.js +36 -1
  10. package/dist/api/graphql-errors.d.ts +6 -0
  11. package/dist/api/graphql-errors.js +22 -0
  12. package/dist/api/nes.client.d.ts +1 -2
  13. package/dist/api/nes.client.js +31 -20
  14. package/dist/api/user-setup.client.d.ts +15 -0
  15. package/dist/api/user-setup.client.js +92 -0
  16. package/dist/commands/auth/login.d.ts +14 -0
  17. package/dist/commands/auth/login.js +225 -0
  18. package/dist/commands/auth/logout.d.ts +5 -0
  19. package/dist/commands/auth/logout.js +27 -0
  20. package/dist/commands/auth/provision-ci-token.d.ts +5 -0
  21. package/dist/commands/auth/provision-ci-token.js +62 -0
  22. package/dist/commands/report/committers.d.ts +11 -7
  23. package/dist/commands/report/committers.js +144 -76
  24. package/dist/commands/scan/eol.d.ts +2 -0
  25. package/dist/commands/scan/eol.js +34 -4
  26. package/dist/commands/tracker/init.d.ts +14 -0
  27. package/dist/commands/tracker/init.js +84 -0
  28. package/dist/commands/tracker/run.d.ts +15 -0
  29. package/dist/commands/tracker/run.js +183 -0
  30. package/dist/config/constants.d.ts +14 -0
  31. package/dist/config/constants.js +15 -0
  32. package/dist/config/tracker.config.d.ts +16 -0
  33. package/dist/config/tracker.config.js +16 -0
  34. package/dist/hooks/finally/finally.js +10 -4
  35. package/dist/hooks/init/01_initialize_amplitude.js +20 -9
  36. package/dist/service/analytics.svc.d.ts +10 -3
  37. package/dist/service/analytics.svc.js +180 -18
  38. package/dist/service/auth-config.svc.d.ts +5 -0
  39. package/dist/service/auth-config.svc.js +20 -0
  40. package/dist/service/auth-refresh.svc.d.ts +8 -0
  41. package/dist/service/auth-refresh.svc.js +45 -0
  42. package/dist/service/auth-token.svc.d.ts +11 -0
  43. package/dist/service/auth-token.svc.js +48 -0
  44. package/dist/service/auth.svc.d.ts +27 -0
  45. package/dist/service/auth.svc.js +88 -0
  46. package/dist/service/ci-auth.svc.d.ts +6 -0
  47. package/dist/service/ci-auth.svc.js +32 -0
  48. package/dist/service/ci-token.svc.d.ts +6 -0
  49. package/dist/service/ci-token.svc.js +75 -0
  50. package/dist/service/committers.svc.d.ts +46 -58
  51. package/dist/service/committers.svc.js +55 -173
  52. package/dist/service/jwt.svc.d.ts +1 -0
  53. package/dist/service/jwt.svc.js +19 -0
  54. package/dist/service/tracker.svc.d.ts +58 -0
  55. package/dist/service/tracker.svc.js +101 -0
  56. package/dist/types/auth.d.ts +9 -0
  57. package/dist/types/auth.js +1 -0
  58. package/dist/utils/open-in-browser.d.ts +1 -0
  59. package/dist/utils/open-in-browser.js +21 -0
  60. package/dist/utils/retry.d.ts +11 -0
  61. package/dist/utils/retry.js +29 -0
  62. package/dist/utils/strip-typename.js +2 -1
  63. package/package.json +38 -19
@@ -0,0 +1,19 @@
1
+ export function decodeJwtPayload(token) {
2
+ if (!token) {
3
+ return;
4
+ }
5
+ try {
6
+ const parts = token.split('.');
7
+ if (parts.length < 2 || !parts[1]) {
8
+ return;
9
+ }
10
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
11
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
12
+ return;
13
+ }
14
+ return payload;
15
+ }
16
+ catch {
17
+ return;
18
+ }
19
+ }
@@ -0,0 +1,58 @@
1
+ import { type TrackerCategoryDefinition, type TrackerConfig } from '../config/tracker.config.ts';
2
+ export type GitLastCommit = {
3
+ hash: string;
4
+ timestamp: string;
5
+ author: string;
6
+ };
7
+ export type FilesStats = {
8
+ total: number;
9
+ source: number;
10
+ comment: number;
11
+ single: number;
12
+ block: number;
13
+ mixed: number;
14
+ empty: number;
15
+ todo: number;
16
+ blockEmpty: number;
17
+ [k: string]: number | string | boolean;
18
+ };
19
+ export type CategoryStatsResult = {
20
+ name: string;
21
+ totals: FilesStats;
22
+ errors: string[];
23
+ fileTypes: string[];
24
+ };
25
+ export type CategorySavedResult = {
26
+ timestamp: string;
27
+ hash: string;
28
+ categories: CategoryStatsResult[];
29
+ };
30
+ export declare const INITIAL_FILES_STATS: FilesStats;
31
+ export declare const getRootDir: (path: string) => string;
32
+ export declare const getConfiguration: (path: string, folderName: string, fileName: string) => TrackerConfig;
33
+ export declare const createTrackerConfig: (rootPath: string, config: TrackerConfig, overwrite?: boolean) => Promise<void>;
34
+ export declare const getFilesFromCategory: (category: TrackerCategoryDefinition, options: {
35
+ rootDir: string;
36
+ ignorePatterns?: string[];
37
+ }) => string[];
38
+ export declare const getFileStats: (path: string, options: {
39
+ rootDir: string;
40
+ }) => {
41
+ source: number;
42
+ total: number;
43
+ comment: number;
44
+ single: number;
45
+ block: number;
46
+ mixed: number;
47
+ empty: number;
48
+ todo: number;
49
+ blockEmpty: number;
50
+ path: string;
51
+ fileType: string;
52
+ error?: undefined;
53
+ } | {
54
+ path: string;
55
+ fileType: string;
56
+ error: boolean;
57
+ };
58
+ export declare const saveResults: (categoriesResult: CategoryStatsResult[], rootDir: string, outputDir: string, git: GitLastCommit) => string;
@@ -0,0 +1,101 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { writeFile } from 'node:fs/promises';
3
+ import { extname, join, resolve } from 'node:path';
4
+ import { globSync } from 'glob'; // replace with node:fs globSync as soon as v22 is minimum supported version
5
+ import sloc from 'sloc';
6
+ import { DEFAULT_TRACKER_RUN_DATA_FILE } from '../config/constants.js';
7
+ import { TRACKER_ROOT_FILE } from "../config/tracker.config.js";
8
+ export const INITIAL_FILES_STATS = {
9
+ total: 0,
10
+ block: 0,
11
+ blockEmpty: 0,
12
+ comment: 0,
13
+ empty: 0,
14
+ mixed: 0,
15
+ single: 0,
16
+ source: 0,
17
+ todo: 0,
18
+ };
19
+ export const getRootDir = (path) => {
20
+ if (existsSync(join(path, TRACKER_ROOT_FILE))) {
21
+ return path;
22
+ }
23
+ else if (path === join(path, '..')) {
24
+ throw new Error(`Couldn't find root directory for the project`);
25
+ }
26
+ return getRootDir(resolve(join(path, '..')));
27
+ };
28
+ export const getConfiguration = (path, folderName, fileName) => {
29
+ const filePath = join(path, folderName, fileName);
30
+ if (!existsSync(filePath)) {
31
+ throw new Error(`Couldn't find configuration ${fileName} file in ${path}. If you haven't, run tracker init command to create the configuration file. If you have a custom folder and configuration file, use the flags -d (directory) and -f (filename) to specify it`);
32
+ }
33
+ const stringConfiguration = readFileSync(filePath, {
34
+ encoding: 'utf-8',
35
+ });
36
+ try {
37
+ return JSON.parse(stringConfiguration);
38
+ }
39
+ catch (_err) {
40
+ throw new Error(`A configuration file was found, but it's contents are not valid. Review your configuration file and fix any errors, or run tracker init -o to overwrite the file`);
41
+ }
42
+ };
43
+ export const createTrackerConfig = async (rootPath, config, overwrite = false) => {
44
+ const { outputDir } = config;
45
+ const configDir = join(rootPath, outputDir);
46
+ const configFile = join(configDir, config.configFile);
47
+ const doesConfigFileExists = existsSync(configFile);
48
+ if (!existsSync(configDir)) {
49
+ mkdirSync(configDir);
50
+ }
51
+ if (doesConfigFileExists && !overwrite) {
52
+ throw new Error(`Configuration file already exists for this repo. If you want to overwrite it, run the command again with the --overwrite flag`);
53
+ }
54
+ await writeFile(join(configDir, config.configFile), JSON.stringify(config, null, 2));
55
+ };
56
+ export const getFilesFromCategory = (category, options) => {
57
+ const { fileTypes, includes } = category;
58
+ const { rootDir, ignorePatterns } = options;
59
+ // if no includes folder or no specific file type is set, we ignore the category
60
+ if (fileTypes.length === 0 || includes.length === 0) {
61
+ return [];
62
+ }
63
+ const patterns = includes.flatMap((include) => fileTypes.map((type) => `*${include.replace('./', '')}/**/*.${type}`));
64
+ return globSync(patterns, {
65
+ cwd: rootDir,
66
+ ignore: ignorePatterns?.map((ignore) => `${ignore}`) ?? [],
67
+ });
68
+ };
69
+ export const getFileStats = (path, options) => {
70
+ const fileType = extname(path).replace(/\./g, '');
71
+ try {
72
+ const stats = sloc(readFileSync(join(options.rootDir, path), 'utf8'), fileType);
73
+ return {
74
+ path,
75
+ fileType,
76
+ ...stats,
77
+ };
78
+ }
79
+ catch (_err) {
80
+ return {
81
+ path,
82
+ fileType,
83
+ error: true,
84
+ };
85
+ }
86
+ };
87
+ export const saveResults = (categoriesResult, rootDir, outputDir, git) => {
88
+ const dataResults = [];
89
+ const dataFile = join(rootDir, outputDir, DEFAULT_TRACKER_RUN_DATA_FILE);
90
+ try {
91
+ const savedOutput = readFileSync(dataFile).toString('utf8');
92
+ dataResults.push(...JSON.parse(savedOutput));
93
+ }
94
+ catch (_err) { }
95
+ dataResults.push({
96
+ ...git,
97
+ categories: categoriesResult,
98
+ });
99
+ writeFileSync(dataFile, JSON.stringify(dataResults, null, 2));
100
+ return `${dataFile}`;
101
+ };
@@ -0,0 +1,9 @@
1
+ export interface TokenResponse {
2
+ access_token: string;
3
+ refresh_token?: string;
4
+ expires_in?: number;
5
+ refresh_expires_in?: number;
6
+ token_type?: string;
7
+ scope?: string;
8
+ id_token?: string;
9
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare function openInBrowser(url: string): Promise<void>;
@@ -0,0 +1,21 @@
1
+ import { exec } from 'node:child_process';
2
+ import { platform } from 'node:os';
3
+ export function openInBrowser(url) {
4
+ return new Promise((resolve, reject) => {
5
+ const escapedUrl = `"${url.replace(/"/g, '\\"')}"`;
6
+ const command = (() => {
7
+ const plat = platform();
8
+ if (plat === 'darwin')
9
+ return `open ${escapedUrl}`; // macOS
10
+ if (plat === 'win32')
11
+ return `start "" ${escapedUrl}`; // Windows
12
+ return `xdg-open ${escapedUrl}`; // Linux
13
+ })();
14
+ exec(command, (err) => {
15
+ if (err)
16
+ reject(new Error(`Failed to open browser: ${err.message}`));
17
+ else
18
+ resolve();
19
+ });
20
+ });
21
+ }
@@ -0,0 +1,11 @@
1
+ export type RetryOptions = {
2
+ attempts: number;
3
+ baseDelayMs: number;
4
+ onRetry?: (info: {
5
+ attempt: number;
6
+ delayMs: number;
7
+ error: unknown;
8
+ }) => void;
9
+ finalErrorMessage?: string;
10
+ };
11
+ export declare function withRetries<T>(operation: string, fn: () => Promise<T>, options: RetryOptions): Promise<T>;
@@ -0,0 +1,29 @@
1
+ import { debugLogger } from "../service/log.svc.js";
2
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
3
+ export async function withRetries(operation, fn, options) {
4
+ const { attempts, baseDelayMs, onRetry, finalErrorMessage } = options;
5
+ let lastError;
6
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
7
+ try {
8
+ return await fn();
9
+ }
10
+ catch (error) {
11
+ lastError = error;
12
+ if (attempt === attempts) {
13
+ break;
14
+ }
15
+ const delayMs = baseDelayMs * attempt;
16
+ if (onRetry) {
17
+ onRetry({ attempt, delayMs, error });
18
+ }
19
+ else {
20
+ debugLogger('Retry (%s) attempt %d/%d after %dms: %o', operation, attempt, attempts, delayMs, error);
21
+ }
22
+ await sleep(delayMs);
23
+ }
24
+ }
25
+ const message = finalErrorMessage ??
26
+ (lastError instanceof Error ? lastError.message : null) ??
27
+ 'Please contact support@herodevs.com.';
28
+ throw new Error(message);
29
+ }
@@ -7,7 +7,8 @@ export function stripTypename(obj) {
7
7
  for (const key of Object.keys(obj)) {
8
8
  if (key === '__typename')
9
9
  continue;
10
- result[key] = stripTypename(obj[key]);
10
+ const value = obj[key];
11
+ result[key] = stripTypename(value);
11
12
  }
12
13
  return result;
13
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herodevs/cli",
3
- "version": "2.0.0-beta.13",
3
+ "version": "2.0.0-beta.15",
4
4
  "author": "HeroDevs, Inc",
5
5
  "bin": {
6
6
  "hd": "./bin/run.js"
@@ -11,7 +11,7 @@
11
11
  "url": "https://github.com/herodevs/cli"
12
12
  },
13
13
  "homepage": "https://github.com/herodevs/cli",
14
- "bugs": "https://github.com/@herodevs/cli/issues",
14
+ "bugs": "https://github.com/herodevs/cli/issues",
15
15
  "scripts": {
16
16
  "build": "shx rm -rf dist && tsc -b",
17
17
  "ci": "biome ci",
@@ -20,15 +20,18 @@
20
20
  "clean:files": "shx rm -f herodevs.**.csv herodevs.**.json herodevs.**.txt",
21
21
  "dev": "npm run build && ./bin/dev.js",
22
22
  "dev:debug": "npm run build && DEBUG=oclif:* ./bin/dev.js",
23
+ "dev:auth:local": "OAUTH_CONNECT_URL='http://localhost:6040/realms/herodevs_local/protocol/openid-connect' npm run dev auth login",
24
+ "dev:auth:logout": "npm run dev auth logout",
23
25
  "format": "biome format --write",
24
26
  "lint": "biome lint --write",
25
27
  "postpack": "shx rm -f oclif.manifest.json",
26
28
  "prepare": "shx test -d dist || npm run build",
27
29
  "prepack": "oclif manifest",
28
30
  "pretest": "npm run lint && npm run typecheck",
29
- "readme": "npm run ci:fix && npm run build && oclif readme",
30
- "test": "globstar -- node --import tsx --test --experimental-test-module-mocks \"test/**/*.test.ts\"",
31
- "test:e2e": "globstar -- node --import tsx --test \"e2e/**/*.test.ts\"",
31
+ "readme": "npm run ci:fix && npm run build && oclif readme && sed -i '' 's|/plugin-help/blob/v|/plugin-help/blob/|; s|/plugin-update/blob/v|/plugin-update/blob/|' README.md",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest watch",
34
+ "test:e2e": "globstar -- node --import tsx --import ./e2e/setup/register-mock-auth.mjs --test \"e2e/**/*.test.ts\"",
32
35
  "typecheck": "tsc --noEmit",
33
36
  "version": "oclif manifest",
34
37
  "postversion": "node scripts/update-install-script-version.js && git add README.md"
@@ -39,33 +42,50 @@
39
42
  "herodevs cli"
40
43
  ],
41
44
  "dependencies": {
42
- "@amplitude/analytics-node": "^1.5.21",
43
- "@apollo/client": "^3.13.8",
44
- "@cyclonedx/cdxgen": "^11.11.0",
45
- "@herodevs/eol-shared": "github:herodevs/eol-shared#v0.1.12",
45
+ "@amplitude/analytics-node": "^1.5.26",
46
+ "@apollo/client": "^4.0.9",
47
+ "@cyclonedx/cdxgen": "^12.1.1",
48
+ "@herodevs/eol-shared": "github:herodevs/eol-shared#v0.1.17",
49
+ "@inquirer/prompts": "^8.0.2",
50
+ "@napi-rs/keyring": "^1.2.0",
46
51
  "@oclif/core": "^4.8.0",
47
52
  "@oclif/plugin-help": "^6.2.32",
48
- "@oclif/plugin-update": "^4.7.13",
53
+ "@oclif/plugin-update": "^4.7.16",
54
+ "@oclif/table": "^0.5.1",
55
+ "cli-progress": "^3.12.0",
56
+ "conf": "^15.1.0",
57
+ "date-fns": "^4.1.0",
58
+ "glob": "^13.0.0",
59
+ "graphql": "^16.11.0",
49
60
  "node-machine-id": "^1.1.12",
50
61
  "ora": "^9.0.0",
51
62
  "packageurl-js": "^2.0.1",
63
+ "sloc": "^0.3.2",
52
64
  "terminal-link": "^5.0.0",
53
65
  "update-notifier": "^7.3.1"
54
66
  },
55
67
  "devDependencies": {
56
- "@biomejs/biome": "^2.3.3",
57
- "@oclif/test": "^4.1.13",
68
+ "@biomejs/biome": "^2.3.8",
69
+ "@oclif/test": "^4.1.15",
70
+ "@types/cli-progress": "^3.11.6",
71
+ "@types/debug": "^4.1.12",
58
72
  "@types/inquirer": "^9.0.9",
59
- "@types/node": "^24.10.0",
60
- "@types/sinon": "^17.0.4",
73
+ "@types/mock-fs": "^4.13.4",
74
+ "@types/node": "^24.10.1",
75
+ "@types/ora": "^3.1.0",
76
+ "@types/sinon": "^21.0.0",
77
+ "@types/sloc": "^0.2.3",
78
+ "@types/terminal-link": "^1.1.0",
61
79
  "@types/update-notifier": "^6.0.8",
62
80
  "globstar": "^1.0.0",
63
- "oclif": "^4.22.38",
81
+ "mock-fs": "^5.5.0",
82
+ "oclif": "^4.22.47",
64
83
  "shx": "^0.4.0",
65
84
  "sinon": "^21.0.0",
66
85
  "ts-node": "^10.9.2",
67
- "tsx": "^4.20.6",
68
- "typescript": "^5.9.3"
86
+ "tsx": "^4.21.0",
87
+ "typescript": "^5.9.3",
88
+ "vitest": "^4.0.16"
69
89
  },
70
90
  "engines": {
71
91
  "node": ">=20.0.0"
@@ -83,7 +103,6 @@
83
103
  "commands": "./dist/commands",
84
104
  "plugins": [
85
105
  "@oclif/plugin-help",
86
- "@oclif/plugin-plugins",
87
106
  "@oclif/plugin-update"
88
107
  ],
89
108
  "hooks": {
@@ -110,4 +129,4 @@
110
129
  }
111
130
  },
112
131
  "types": "dist/index.d.ts"
113
- }
132
+ }