@herodevs/cli 2.0.0-beta.4 → 2.0.0-beta.5

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 (55) hide show
  1. package/README.md +142 -108
  2. package/dist/api/gql-operations.d.ts +2 -0
  3. package/dist/api/gql-operations.js +36 -0
  4. package/dist/api/nes.client.d.ts +12 -0
  5. package/dist/api/nes.client.js +71 -0
  6. package/dist/commands/scan/eol.d.ts +12 -12
  7. package/dist/commands/scan/eol.js +163 -104
  8. package/dist/config/constants.d.ts +8 -3
  9. package/dist/config/constants.js +18 -3
  10. package/dist/hooks/finally.d.ts +3 -0
  11. package/dist/hooks/finally.js +14 -0
  12. package/dist/hooks/prerun.js +12 -0
  13. package/dist/service/analytics.svc.d.ts +28 -0
  14. package/dist/service/analytics.svc.js +112 -0
  15. package/dist/service/{eol/cdx.svc.d.ts → cdx.svc.d.ts} +8 -16
  16. package/dist/service/{eol/cdx.svc.js → cdx.svc.js} +17 -7
  17. package/dist/service/display.svc.d.ts +22 -0
  18. package/dist/service/display.svc.js +72 -0
  19. package/dist/service/file.svc.d.ts +20 -0
  20. package/dist/service/file.svc.js +71 -0
  21. package/dist/service/log.svc.d.ts +1 -0
  22. package/dist/service/log.svc.js +9 -0
  23. package/dist/service/{eol/sbom.worker.js → sbom.worker.js} +1 -1
  24. package/package.json +22 -15
  25. package/dist/api/client.d.ts +0 -12
  26. package/dist/api/client.js +0 -43
  27. package/dist/api/nes/nes.client.d.ts +0 -24
  28. package/dist/api/nes/nes.client.js +0 -127
  29. package/dist/api/queries/nes/sbom.d.ts +0 -3
  30. package/dist/api/queries/nes/sbom.js +0 -39
  31. package/dist/api/queries/nes/telemetry.d.ts +0 -2
  32. package/dist/api/queries/nes/telemetry.js +0 -24
  33. package/dist/api/types/hd-cli.types.d.ts +0 -30
  34. package/dist/api/types/hd-cli.types.js +0 -10
  35. package/dist/api/types/nes.types.d.ts +0 -58
  36. package/dist/api/types/nes.types.js +0 -1
  37. package/dist/commands/report/committers.d.ts +0 -23
  38. package/dist/commands/report/committers.js +0 -147
  39. package/dist/commands/report/purls.d.ts +0 -15
  40. package/dist/commands/report/purls.js +0 -85
  41. package/dist/commands/scan/sbom.d.ts +0 -21
  42. package/dist/commands/scan/sbom.js +0 -164
  43. package/dist/service/committers.svc.d.ts +0 -70
  44. package/dist/service/committers.svc.js +0 -196
  45. package/dist/service/eol/eol.svc.d.ts +0 -12
  46. package/dist/service/eol/eol.svc.js +0 -25
  47. package/dist/service/error.svc.d.ts +0 -8
  48. package/dist/service/error.svc.js +0 -28
  49. package/dist/service/nes/nes.svc.d.ts +0 -5
  50. package/dist/service/nes/nes.svc.js +0 -36
  51. package/dist/service/purls.svc.d.ts +0 -23
  52. package/dist/service/purls.svc.js +0 -99
  53. package/dist/ui/shared.ui.d.ts +0 -4
  54. package/dist/ui/shared.ui.js +0 -14
  55. /package/dist/service/{eol/sbom.worker.d.ts → sbom.worker.d.ts} +0 -0
@@ -0,0 +1,22 @@
1
+ import type { ComponentStatus, EolReport } from '@herodevs/eol-shared';
2
+ /**
3
+ * Formats status row text with appropriate color and icon
4
+ */
5
+ export declare const getStatusRowText: Record<ComponentStatus, (text: string) => string>;
6
+ export type ComponentCounts = Record<ComponentStatus, number> & {
7
+ NES_AVAILABLE: number;
8
+ TOTAL: number;
9
+ ECOSYSTEMS: string[];
10
+ };
11
+ /**
12
+ * Counts components by their status, including NES remediation availability
13
+ */
14
+ export declare function countComponentsByStatus(report: EolReport): ComponentCounts;
15
+ /**
16
+ * Formats scan results for console display
17
+ */
18
+ export declare function formatScanResults(report: EolReport): string[];
19
+ /**
20
+ * Formats web report URL for console display
21
+ */
22
+ export declare function formatWebReportUrl(id: string, reportCardUrl: string): string[];
@@ -0,0 +1,72 @@
1
+ import { deriveComponentStatus } from '@herodevs/eol-shared';
2
+ import { ux } from '@oclif/core';
3
+ import terminalLink from 'terminal-link';
4
+ const STATUS_COLORS = {
5
+ EOL: 'red',
6
+ UNKNOWN: 'default',
7
+ OK: 'green',
8
+ EOL_UPCOMING: 'yellow',
9
+ };
10
+ /**
11
+ * Formats status row text with appropriate color and icon
12
+ */
13
+ export const getStatusRowText = {
14
+ EOL: (text) => ux.colorize(STATUS_COLORS.EOL, `✗ ${text}`),
15
+ UNKNOWN: (text) => ux.colorize(STATUS_COLORS.UNKNOWN, `• ${text}`),
16
+ OK: (text) => ux.colorize(STATUS_COLORS.OK, `✔ ${text}`),
17
+ EOL_UPCOMING: (text) => ux.colorize(STATUS_COLORS.EOL_UPCOMING, `! ${text}`),
18
+ };
19
+ /**
20
+ * Counts components by their status, including NES remediation availability
21
+ */
22
+ export function countComponentsByStatus(report) {
23
+ const grouped = {
24
+ UNKNOWN: 0,
25
+ OK: 0,
26
+ EOL_UPCOMING: 0,
27
+ EOL: 0,
28
+ NES_AVAILABLE: 0,
29
+ ECOSYSTEMS: [],
30
+ TOTAL: report.components.length,
31
+ };
32
+ const ecosystems = new Set();
33
+ for (const component of report.components) {
34
+ const status = deriveComponentStatus(component.metadata);
35
+ grouped[status]++;
36
+ if (component.nesRemediation?.remediations?.length) {
37
+ grouped.NES_AVAILABLE++;
38
+ }
39
+ const ecosystem = component.purl.match(/^pkg:([^/]+)\//)?.[1];
40
+ if (ecosystem) {
41
+ ecosystems.add(ecosystem);
42
+ }
43
+ }
44
+ grouped.ECOSYSTEMS = Array.from(ecosystems);
45
+ return grouped;
46
+ }
47
+ /**
48
+ * Formats scan results for console display
49
+ */
50
+ export function formatScanResults(report) {
51
+ const { UNKNOWN, OK, EOL_UPCOMING, EOL, NES_AVAILABLE } = countComponentsByStatus(report);
52
+ if (!UNKNOWN && !OK && !EOL_UPCOMING && !EOL) {
53
+ return [ux.colorize('yellow', 'No components found in scan.')];
54
+ }
55
+ return [
56
+ ux.colorize('bold', 'Scan results:'),
57
+ ux.colorize('bold', '-'.repeat(40)),
58
+ ux.colorize('bold', `${report.components.length.toLocaleString()} total packages scanned`),
59
+ getStatusRowText.EOL(`${EOL.toLocaleString().padEnd(5)} End-of-Life (EOL)`),
60
+ getStatusRowText.EOL_UPCOMING(`${EOL_UPCOMING.toLocaleString().padEnd(5)} EOL Upcoming`),
61
+ getStatusRowText.OK(`${OK.toLocaleString().padEnd(5)} Not End-of-Life`),
62
+ getStatusRowText.UNKNOWN(`${UNKNOWN.toLocaleString().padEnd(5)} Unknown Status`),
63
+ getStatusRowText.UNKNOWN(`${NES_AVAILABLE.toLocaleString().padEnd(5)} HeroDevs NES Remediation${NES_AVAILABLE !== 1 ? 's' : ''} Available`),
64
+ ];
65
+ }
66
+ /**
67
+ * Formats web report URL for console display
68
+ */
69
+ export function formatWebReportUrl(id, reportCardUrl) {
70
+ const url = ux.colorize('blue', terminalLink(new URL(reportCardUrl).hostname, `${reportCardUrl}/${id}`, { fallback: (_, url) => url }));
71
+ return [ux.colorize('bold', '-'.repeat(40)), `🌐 View your full EOL report at: ${url}\n`];
72
+ }
@@ -0,0 +1,20 @@
1
+ import type { CdxBom, EolReport } from '@herodevs/eol-shared';
2
+ export interface FileError extends Error {
3
+ code?: string;
4
+ }
5
+ /**
6
+ * Reads an SBOM from a file path
7
+ */
8
+ export declare function readSbomFromFile(filePath: string): CdxBom;
9
+ /**
10
+ * Validates that a directory path exists and is actually a directory
11
+ */
12
+ export declare function validateDirectory(dirPath: string): void;
13
+ /**
14
+ * Saves an SBOM to a file in the specified directory
15
+ */
16
+ export declare function saveSbomToFile(dir: string, sbom: CdxBom): string;
17
+ /**
18
+ * Saves an EOL report to a file in the specified directory
19
+ */
20
+ export declare function saveReportToFile(dir: string, report: EolReport): string;
@@ -0,0 +1,71 @@
1
+ import fs from 'node:fs';
2
+ import path, { join, resolve } from 'node:path';
3
+ import { isCdxBom } from '@herodevs/eol-shared';
4
+ import { filenamePrefix } from "../config/constants.js";
5
+ import { getErrorMessage } from "./log.svc.js";
6
+ /**
7
+ * Reads an SBOM from a file path
8
+ */
9
+ export function readSbomFromFile(filePath) {
10
+ const file = resolve(filePath);
11
+ if (!fs.existsSync(file)) {
12
+ throw new Error(`SBOM file not found: ${file}`);
13
+ }
14
+ try {
15
+ const fileContent = fs.readFileSync(file, 'utf8');
16
+ const sbom = JSON.parse(fileContent);
17
+ if (!isCdxBom(sbom)) {
18
+ throw new Error(`Invalid SBOM file: ${file}`);
19
+ }
20
+ return sbom;
21
+ }
22
+ catch (error) {
23
+ throw new Error(`Failed to read SBOM file: ${getErrorMessage(error)}`);
24
+ }
25
+ }
26
+ /**
27
+ * Validates that a directory path exists and is actually a directory
28
+ */
29
+ export function validateDirectory(dirPath) {
30
+ const dir = resolve(dirPath);
31
+ if (!fs.existsSync(dir)) {
32
+ throw new Error(`Directory not found: ${dir}`);
33
+ }
34
+ const stats = fs.statSync(dir);
35
+ if (!stats.isDirectory()) {
36
+ throw new Error(`Path is not a directory: ${dir}`);
37
+ }
38
+ }
39
+ /**
40
+ * Saves an SBOM to a file in the specified directory
41
+ */
42
+ export function saveSbomToFile(dir, sbom) {
43
+ const outputPath = join(dir, `${filenamePrefix}.sbom.json`);
44
+ try {
45
+ fs.writeFileSync(outputPath, JSON.stringify(sbom, null, 2));
46
+ return outputPath;
47
+ }
48
+ catch (error) {
49
+ throw new Error(`Failed to save SBOM: ${getErrorMessage(error)}`);
50
+ }
51
+ }
52
+ /**
53
+ * Saves an EOL report to a file in the specified directory
54
+ */
55
+ export function saveReportToFile(dir, report) {
56
+ const reportPath = path.join(dir, `${filenamePrefix}.report.json`);
57
+ try {
58
+ fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
59
+ return reportPath;
60
+ }
61
+ catch (error) {
62
+ const fileError = error;
63
+ if (fileError.code === 'EACCES') {
64
+ throw new Error(`Permission denied. Unable to save report to ${filenamePrefix}.report.json`);
65
+ }
66
+ if (fileError.code === 'ENOSPC') {
67
+ throw new Error(`No space left on device. Unable to save report to ${filenamePrefix}.report.json`);
68
+ }
69
+ throw new Error(`Failed to save report: ${getErrorMessage(error)}`);
70
+ }
71
+ }
@@ -5,3 +5,4 @@ import debug from 'debug';
5
5
  * All user-facing output should be handled by commands.
6
6
  */
7
7
  export declare const debugLogger: debug.Debugger;
8
+ export declare function getErrorMessage(error: unknown): string;
@@ -5,3 +5,12 @@ import debug from 'debug';
5
5
  * All user-facing output should be handled by commands.
6
6
  */
7
7
  export const debugLogger = debug('oclif:herodevs-debug');
8
+ export function getErrorMessage(error) {
9
+ if (error instanceof Error) {
10
+ return error.message;
11
+ }
12
+ if (error && typeof error === 'object') {
13
+ return JSON.stringify(error);
14
+ }
15
+ return String(error) || 'Unknown error';
16
+ }
@@ -1,7 +1,7 @@
1
1
  import { writeFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { createBom } from '@cyclonedx/cdxgen';
4
- import { filenamePrefix } from "../../config/constants.js";
4
+ import { filenamePrefix } from "../config/constants.js";
5
5
  import { SBOM_DEFAULT__OPTIONS } from "./cdx.svc.js";
6
6
  process.on('uncaughtException', (err) => {
7
7
  console.error('Uncaught exception:', err.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herodevs/cli",
3
- "version": "2.0.0-beta.4",
3
+ "version": "2.0.0-beta.5",
4
4
  "author": "HeroDevs, Inc",
5
5
  "bin": {
6
6
  "hd": "./bin/run.js"
@@ -23,12 +23,14 @@
23
23
  "format": "biome format --write",
24
24
  "lint": "biome lint --write",
25
25
  "postpack": "shx rm -f oclif.manifest.json",
26
+ "prepare": "npm run build",
26
27
  "prepack": "oclif manifest && oclif readme",
27
28
  "pretest": "npm run lint && npm run typecheck",
28
29
  "readme": "npm run ci:fix && npm run build && npm exec oclif readme",
29
- "test": "globstar -- node --import tsx --test \"test/**/*.test.ts\"",
30
+ "test": "globstar -- node --import tsx --test --experimental-test-module-mocks \"test/**/*.test.ts\"",
30
31
  "test:e2e": "globstar -- node --import tsx --test \"e2e/**/*.test.ts\"",
31
- "typecheck": "tsc --noEmit"
32
+ "typecheck": "tsc --noEmit",
33
+ "version": "oclif manifest && npm run readme"
32
34
  },
33
35
  "keywords": [
34
36
  "herodevs",
@@ -36,30 +38,34 @@
36
38
  "herodevs cli"
37
39
  ],
38
40
  "dependencies": {
41
+ "@amplitude/analytics-node": "^1.5.5",
39
42
  "@apollo/client": "^3.13.8",
40
- "@cyclonedx/cdxgen": "^11.4.1",
41
- "@oclif/core": "^4.4.0",
42
- "@oclif/plugin-help": "^6.2.29",
43
- "@oclif/plugin-update": "^4.6.45",
43
+ "@cyclonedx/cdxgen": "~11.4.4",
44
+ "@herodevs/eol-shared": "github:herodevs/eol-shared#v0.1.10",
45
+ "@oclif/core": "^4.5.2",
46
+ "@oclif/plugin-help": "^6.2.32",
47
+ "@oclif/plugin-update": "^4.7.4",
44
48
  "graphql": "^16.11.0",
49
+ "node-machine-id": "^1.1.12",
50
+ "ora": "^8.2.0",
45
51
  "packageurl-js": "^2.0.1",
46
52
  "terminal-link": "^4.0.0",
47
53
  "update-notifier": "^7.3.1"
48
54
  },
49
55
  "devDependencies": {
50
- "@biomejs/biome": "^1.9.4",
56
+ "@biomejs/biome": "^2.2.2",
51
57
  "@oclif/test": "^4.1.13",
52
- "@types/inquirer": "^9.0.8",
53
- "@types/node": "^22.15.32",
58
+ "@types/inquirer": "^9.0.9",
59
+ "@types/node": "^24.3.0",
54
60
  "@types/sinon": "^17.0.4",
55
61
  "@types/update-notifier": "^6.0.8",
56
62
  "globstar": "^1.0.0",
57
- "oclif": "^4.20.1",
63
+ "oclif": "^4.22.14",
58
64
  "shx": "^0.4.0",
59
- "sinon": "^20.0.0",
65
+ "sinon": "^21.0.0",
60
66
  "ts-node": "^10.9.2",
61
- "tsx": "^4.20.3",
62
- "typescript": "^5.8.3"
67
+ "tsx": "^4.20.5",
68
+ "typescript": "^5.9.2"
63
69
  },
64
70
  "engines": {
65
71
  "node": ">=20.0.0"
@@ -82,7 +88,8 @@
82
88
  ],
83
89
  "hooks": {
84
90
  "init": "./dist/hooks/npm-update-notifier.js",
85
- "prerun": "./dist/hooks/prerun.js"
91
+ "prerun": "./dist/hooks/prerun.js",
92
+ "finally": "./dist/hooks/finally.js"
86
93
  },
87
94
  "topicSeparator": " ",
88
95
  "macos": {
@@ -1,12 +0,0 @@
1
- import * as apollo from '@apollo/client/core/index.js';
2
- export interface ApolloHelper {
3
- mutate<T, V extends apollo.OperationVariables>(mutation: apollo.DocumentNode, variables?: V): Promise<apollo.FetchResult<T>>;
4
- query<T, V extends apollo.OperationVariables | undefined = undefined>(query: apollo.DocumentNode, variables?: V): Promise<apollo.ApolloQueryResult<T>>;
5
- }
6
- export declare const createApollo: (url: string) => apollo.ApolloClient<apollo.NormalizedCacheObject>;
7
- export declare class ApolloClient implements ApolloHelper {
8
- #private;
9
- constructor(url: string);
10
- mutate<T, V extends apollo.OperationVariables>(mutation: apollo.DocumentNode, variables?: V): Promise<apollo.FetchResult<T>>;
11
- query<T, V extends apollo.OperationVariables | undefined>(query: apollo.DocumentNode, variables?: V): Promise<apollo.ApolloQueryResult<T>>;
12
- }
@@ -1,43 +0,0 @@
1
- import * as apollo from '@apollo/client/core/index.js';
2
- import { ApolloError } from "../service/error.svc.js";
3
- export const createApollo = (url) => new apollo.ApolloClient({
4
- cache: new apollo.InMemoryCache({
5
- addTypename: false,
6
- }),
7
- headers: {
8
- 'User-Agent': `hdcli/${process.env.npm_package_version ?? 'unknown'}`,
9
- },
10
- link: apollo.ApolloLink.from([
11
- new apollo.HttpLink({
12
- uri: url,
13
- }),
14
- ]),
15
- });
16
- export class ApolloClient {
17
- #apollo;
18
- constructor(url) {
19
- this.#apollo = createApollo(url);
20
- }
21
- async mutate(mutation, variables) {
22
- try {
23
- return await this.#apollo.mutate({
24
- mutation,
25
- variables,
26
- });
27
- }
28
- catch (error) {
29
- throw new ApolloError('GraphQL mutation failed', error);
30
- }
31
- }
32
- async query(query, variables) {
33
- try {
34
- return await this.#apollo.query({
35
- query,
36
- variables,
37
- });
38
- }
39
- catch (error) {
40
- throw new ApolloError('GraphQL query failed', error);
41
- }
42
- }
43
- }
@@ -1,24 +0,0 @@
1
- import type * as apollo from '@apollo/client/core/index.js';
2
- import type { InsightsEolScanInput, InsightsEolScanResult } from '../../api/types/nes.types.ts';
3
- import { type ProcessBatchOptions, type ScanInputOptions, type ScanResult } from '../types/hd-cli.types.ts';
4
- export interface NesClient {
5
- scan: {
6
- purls: (purls: string[], options: ScanInputOptions) => Promise<InsightsEolScanResult>;
7
- };
8
- }
9
- export declare class NesApolloClient implements NesClient {
10
- #private;
11
- scan: {
12
- purls: (purls: string[], options: ScanInputOptions) => Promise<InsightsEolScanResult>;
13
- };
14
- constructor(url: string);
15
- mutate<T, V extends Record<string, unknown>>(mutation: apollo.DocumentNode, variables?: V): Promise<apollo.FetchResult<T>>;
16
- query<T, V extends Record<string, unknown> | undefined>(query: apollo.DocumentNode, variables?: V): Promise<apollo.ApolloQueryResult<T>>;
17
- }
18
- export declare const batchSubmitPurls: (purls: string[], options?: ScanInputOptions, batchSize?: number) => Promise<ScanResult>;
19
- export declare const dedupeAndEncodePurls: (purls: string[]) => string[];
20
- export declare const createBatches: (items: string[], batchSize: number) => string[][];
21
- export declare const processBatch: ({ batch, index, totalPages, scanOptions, previousScanId, }: ProcessBatchOptions) => Promise<InsightsEolScanResult>;
22
- export declare const processBatches: (batches: string[][], scanOptions: ScanInputOptions) => Promise<InsightsEolScanResult[]>;
23
- export declare const handleBatchResults: (results: InsightsEolScanResult[]) => ScanResult;
24
- export declare const buildInsightsEolScanInput: (purls: string[], options: ScanInputOptions) => InsightsEolScanInput;
@@ -1,127 +0,0 @@
1
- import { PackageURL } from 'packageurl-js';
2
- import { ApolloClient } from "../../api/client.js";
3
- import { config } from "../../config/constants.js";
4
- import { debugLogger } from "../../service/log.svc.js";
5
- import { SbomScanner, buildScanResult } from "../../service/nes/nes.svc.js";
6
- import { DEFAULT_SCAN_BATCH_SIZE, DEFAULT_SCAN_INPUT_OPTIONS, } from "../types/hd-cli.types.js";
7
- export class NesApolloClient {
8
- scan = {
9
- purls: SbomScanner(this),
10
- };
11
- #apollo;
12
- constructor(url) {
13
- this.#apollo = new ApolloClient(url);
14
- }
15
- mutate(mutation, variables) {
16
- return this.#apollo.mutate(mutation, variables);
17
- }
18
- query(query, variables) {
19
- return this.#apollo.query(query, variables);
20
- }
21
- }
22
- /**
23
- * Submit a scan for a list of purls after they've been batched by batchSubmitPurls
24
- */
25
- function submitScan(purls, options) {
26
- const host = config.graphqlHost;
27
- const path = config.graphqlPath;
28
- const url = host + path;
29
- debugLogger('Submitting scan to %s', url);
30
- const client = new NesApolloClient(url);
31
- return client.scan.purls(purls, options);
32
- }
33
- export const batchSubmitPurls = async (purls, options = DEFAULT_SCAN_INPUT_OPTIONS, batchSize = DEFAULT_SCAN_BATCH_SIZE) => {
34
- try {
35
- const dedupedAndEncodedPurls = dedupeAndEncodePurls(purls);
36
- const batches = createBatches(dedupedAndEncodedPurls, batchSize);
37
- debugLogger('Processing %d batches', batches.length);
38
- if (batches.length === 0) {
39
- return {
40
- components: new Map(),
41
- message: 'No batches to process',
42
- success: true,
43
- warnings: [],
44
- scanId: undefined,
45
- createdOn: undefined,
46
- };
47
- }
48
- const results = await processBatches(batches, options);
49
- return handleBatchResults(results);
50
- }
51
- catch (error) {
52
- debugLogger('Fatal error in batchSubmitPurls: %s', error);
53
- throw new Error(`Failed to process purls: ${error instanceof Error ? error.message : String(error)}`);
54
- }
55
- };
56
- export const dedupeAndEncodePurls = (purls) => {
57
- const dedupedAndEncodedPurls = new Set();
58
- for (const purl of purls) {
59
- try {
60
- // The PackageURL.fromString method encodes each part of the purl
61
- const encodedPurl = PackageURL.fromString(purl).toString();
62
- if (!dedupedAndEncodedPurls.has(encodedPurl)) {
63
- dedupedAndEncodedPurls.add(encodedPurl);
64
- }
65
- }
66
- catch (error) {
67
- debugLogger('Error encoding purl: %s', error);
68
- }
69
- }
70
- return Array.from(dedupedAndEncodedPurls);
71
- };
72
- export const createBatches = (items, batchSize) => {
73
- const numberOfBatches = Math.ceil(items.length / batchSize);
74
- return Array.from({ length: numberOfBatches }, (_, index) => {
75
- const startIndex = index * batchSize;
76
- const endIndex = startIndex + batchSize;
77
- return items.slice(startIndex, endIndex);
78
- });
79
- };
80
- export const processBatch = async ({ batch, index, totalPages, scanOptions, previousScanId, }) => {
81
- const page = index + 1;
82
- if (page > totalPages) {
83
- throw new Error('Total pages exceeded');
84
- }
85
- debugLogger('Processing batch %d of %d', page, totalPages);
86
- debugLogger('ScanID: %s', previousScanId);
87
- const result = await submitScan(batch, {
88
- ...scanOptions,
89
- page,
90
- totalPages,
91
- scanId: previousScanId,
92
- });
93
- return result;
94
- };
95
- export const processBatches = async (batches, scanOptions) => {
96
- const totalPages = batches.length;
97
- const results = [];
98
- for (const [index, batch] of batches.entries()) {
99
- const previousScanId = results[index - 1]?.scanId;
100
- const result = await processBatch({
101
- batch,
102
- index,
103
- totalPages,
104
- scanOptions,
105
- previousScanId,
106
- });
107
- results.push(result);
108
- }
109
- return results;
110
- };
111
- export const handleBatchResults = (results) => {
112
- if (results.length === 0) {
113
- throw new Error('No results to process');
114
- }
115
- // The API returns placeholders for each batch except the last one.
116
- const finalResult = results[results.length - 1];
117
- return buildScanResult(finalResult);
118
- };
119
- export const buildInsightsEolScanInput = (purls, options) => {
120
- const { type, page, totalPages } = options;
121
- return {
122
- components: purls,
123
- type,
124
- page,
125
- totalPages,
126
- };
127
- };
@@ -1,3 +0,0 @@
1
- export declare const M_SCAN: {
2
- gql: import("graphql/language/ast.js").DocumentNode;
3
- };
@@ -1,39 +0,0 @@
1
- import { gql } from '@apollo/client/core/core.cjs';
2
- export const M_SCAN = {
3
- gql: gql `
4
- mutation EolScan($input: InsightsEolScanInput!) {
5
- insights {
6
- scan {
7
- eol(input: $input) {
8
- components {
9
- purl
10
- info {
11
- isEol
12
- isUnsafe
13
- eolAt
14
- daysEol
15
- status
16
- vulnCount
17
- }
18
- remediation {
19
- id
20
- }
21
- }
22
- diagnostics
23
- message
24
- scanId
25
- createdOn
26
- success
27
- warnings {
28
- purl
29
- type
30
- message
31
- error
32
- diagnostics
33
- }
34
- }
35
- }
36
- }
37
- }
38
- `,
39
- };
@@ -1,2 +0,0 @@
1
- export declare const TELEMETRY_INITIALIZE_MUTATION: import("graphql/language/ast.js").DocumentNode;
2
- export declare const TELEMETRY_REPORT_MUTATION: import("graphql/language/ast.js").DocumentNode;
@@ -1,24 +0,0 @@
1
- import { gql } from '@apollo/client/core/core.cjs';
2
- export const TELEMETRY_INITIALIZE_MUTATION = gql `
3
- mutation Telemetry($clientName: String!) {
4
- telemetry {
5
- initialize(input: { context: { client: { id: $clientName } } }) {
6
- success
7
- oid
8
- message
9
- }
10
- }
11
- }
12
- `;
13
- export const TELEMETRY_REPORT_MUTATION = gql `
14
- mutation Report($key: String!, $report: JSON!, $metadata: JSON) {
15
- telemetry {
16
- report(input: { key: $key, report: $report, metadata: $metadata }) {
17
- txId
18
- success
19
- message
20
- diagnostics
21
- }
22
- }
23
- }
24
- `;
@@ -1,30 +0,0 @@
1
- import { type ComponentStatus, type InsightsEolScanComponent, type ScanWarning } from './nes.types.ts';
2
- export declare const isValidComponentStatus: (status: string) => status is ComponentStatus;
3
- export interface ScanInputOptions {
4
- type: 'SBOM' | 'OTHER';
5
- page: number;
6
- totalPages: number;
7
- scanId?: string;
8
- }
9
- export declare const DEFAULT_SCAN_BATCH_SIZE = 1000;
10
- export declare const DEFAULT_SCAN_INPUT_OPTIONS: ScanInputOptions;
11
- export type ScanInput = {
12
- components: string[];
13
- options: ScanInputOptions;
14
- };
15
- export interface ScanResult {
16
- components: Map<string, InsightsEolScanComponent>;
17
- createdOn?: string;
18
- diagnostics?: Record<string, unknown>;
19
- message: string;
20
- success: boolean;
21
- warnings: ScanWarning[];
22
- scanId: string | undefined;
23
- }
24
- export interface ProcessBatchOptions {
25
- batch: string[];
26
- index: number;
27
- totalPages: number;
28
- scanOptions: ScanInputOptions;
29
- previousScanId?: string;
30
- }
@@ -1,10 +0,0 @@
1
- import { VALID_STATUSES } from "./nes.types.js";
2
- export const isValidComponentStatus = (status) => {
3
- return VALID_STATUSES.includes(status);
4
- };
5
- export const DEFAULT_SCAN_BATCH_SIZE = 1000;
6
- export const DEFAULT_SCAN_INPUT_OPTIONS = {
7
- type: 'SBOM',
8
- page: 1,
9
- totalPages: 1,
10
- };