@herodevs/cli 0.3.1 → 1.0.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/LICENSE +7 -0
- package/README.md +146 -6
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +11 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +4 -0
- package/dist/api/client.d.ts +14 -0
- package/dist/api/client.js +42 -0
- package/dist/api/nes/nes.client.d.ts +16 -0
- package/dist/api/nes/nes.client.js +17 -0
- package/dist/api/queries/nes/sbom.d.ts +3 -0
- package/dist/api/queries/nes/sbom.js +29 -0
- package/{report-ingestion/src/lib/queries.d.ts → dist/api/queries/nes/telemetry.d.ts} +2 -2
- package/{report-ingestion/src/lib/queries.js → dist/api/queries/nes/telemetry.js} +3 -7
- package/dist/api/types/nes.types.d.ts +35 -0
- package/dist/api/types/nes.types.js +1 -0
- package/dist/commands/report/committers.d.ts +22 -0
- package/dist/commands/report/committers.js +104 -0
- package/dist/commands/report/purls.d.ts +13 -0
- package/dist/commands/report/purls.js +66 -0
- package/dist/commands/scan/eol.d.ts +16 -0
- package/dist/commands/scan/eol.js +71 -0
- package/dist/commands/scan/sbom.d.ts +18 -0
- package/dist/commands/scan/sbom.js +106 -0
- package/dist/config/update.d.ts +3 -0
- package/dist/config/update.js +7 -0
- package/dist/hooks/init/update.d.ts +2 -0
- package/dist/hooks/init/update.js +5 -0
- package/dist/hooks/prerun/CommandContextHook.d.ts +3 -0
- package/dist/hooks/prerun/CommandContextHook.js +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/service/committers.svc.d.ts +70 -0
- package/dist/service/committers.svc.js +175 -0
- package/dist/service/eol/cdx.svc.d.ts +22 -0
- package/dist/service/eol/cdx.svc.js +86 -0
- package/dist/service/eol/eol.svc.d.ts +34 -0
- package/dist/service/eol/eol.svc.js +86 -0
- package/dist/service/line.svc.d.ts +24 -0
- package/dist/service/line.svc.js +61 -0
- package/dist/service/log.svc.d.ts +10 -0
- package/dist/service/log.svc.js +20 -0
- package/dist/service/nes/nes.svc.d.ts +4 -0
- package/dist/service/nes/nes.svc.js +25 -0
- package/dist/service/purls.svc.d.ts +17 -0
- package/dist/service/purls.svc.js +29 -0
- package/dist/ui/eol.ui.d.ts +3 -0
- package/dist/ui/eol.ui.js +17 -0
- package/package.json +73 -16
- package/core-types/README.md +0 -11
- package/core-types/package.json +0 -12
- package/core-types/src/index.d.ts +0 -1
- package/core-types/src/index.js +0 -5
- package/core-types/src/index.js.map +0 -1
- package/core-types/src/lib/project-type.d.ts +0 -1
- package/core-types/src/lib/project-type.js +0 -3
- package/core-types/src/lib/project-type.js.map +0 -1
- package/init/README.md +0 -11
- package/init/package.json +0 -18
- package/init/src/index.d.ts +0 -1
- package/init/src/index.js +0 -5
- package/init/src/index.js.map +0 -1
- package/init/src/lib/configure-project.d.ts +0 -3
- package/init/src/lib/configure-project.js +0 -10
- package/init/src/lib/configure-project.js.map +0 -1
- package/init/src/lib/get-package-choices.d.ts +0 -2
- package/init/src/lib/get-package-choices.js +0 -16
- package/init/src/lib/get-package-choices.js.map +0 -1
- package/init/src/lib/get-product-choices.d.ts +0 -2
- package/init/src/lib/get-product-choices.js +0 -29
- package/init/src/lib/get-product-choices.js.map +0 -1
- package/init/src/lib/get-release-trains.d.ts +0 -2
- package/init/src/lib/get-release-trains.js +0 -57
- package/init/src/lib/get-release-trains.js.map +0 -1
- package/init/src/lib/init.d.ts +0 -5
- package/init/src/lib/init.js +0 -72
- package/init/src/lib/init.js.map +0 -1
- package/init/src/lib/mock-trains.d.ts +0 -2
- package/init/src/lib/mock-trains.js +0 -190
- package/init/src/lib/mock-trains.js.map +0 -1
- package/init/src/lib/models/choice.d.ts +0 -4
- package/init/src/lib/models/choice.js +0 -3
- package/init/src/lib/models/choice.js.map +0 -1
- package/init/src/lib/models/entry.d.ts +0 -6
- package/init/src/lib/models/entry.js +0 -3
- package/init/src/lib/models/entry.js.map +0 -1
- package/init/src/lib/models/index.d.ts +0 -5
- package/init/src/lib/models/index.js +0 -9
- package/init/src/lib/models/index.js.map +0 -1
- package/init/src/lib/models/package-version.d.ts +0 -14
- package/init/src/lib/models/package-version.js +0 -3
- package/init/src/lib/models/package-version.js.map +0 -1
- package/init/src/lib/models/product.d.ts +0 -5
- package/init/src/lib/models/product.js +0 -3
- package/init/src/lib/models/product.js.map +0 -1
- package/init/src/lib/models/release-train.d.ts +0 -9
- package/init/src/lib/models/release-train.js +0 -3
- package/init/src/lib/models/release-train.js.map +0 -1
- package/init/src/lib/npm/configure-npm-project.d.ts +0 -2
- package/init/src/lib/npm/configure-npm-project.js +0 -59
- package/init/src/lib/npm/configure-npm-project.js.map +0 -1
- package/init/src/lib/verify-project-type.d.ts +0 -6
- package/init/src/lib/verify-project-type.js +0 -19
- package/init/src/lib/verify-project-type.js.map +0 -1
- package/report-committers/README.md +0 -11
- package/report-committers/package.json +0 -15
- package/report-committers/src/index.d.ts +0 -1
- package/report-committers/src/index.js +0 -5
- package/report-committers/src/index.js.map +0 -1
- package/report-committers/src/lib/committers.d.ts +0 -11
- package/report-committers/src/lib/committers.js +0 -106
- package/report-committers/src/lib/committers.js.map +0 -1
- package/report-committers/src/lib/constants.d.ts +0 -4
- package/report-committers/src/lib/constants.js +0 -8
- package/report-committers/src/lib/constants.js.map +0 -1
- package/report-committers/src/lib/get-committer-counts.d.ts +0 -3
- package/report-committers/src/lib/get-committer-counts.js +0 -33
- package/report-committers/src/lib/get-committer-counts.js.map +0 -1
- package/report-committers/src/lib/parse-date-flags.d.ts +0 -4
- package/report-committers/src/lib/parse-date-flags.js +0 -11
- package/report-committers/src/lib/parse-date-flags.js.map +0 -1
- package/report-committers/src/lib/parse-git-log-entries.d.ts +0 -2
- package/report-committers/src/lib/parse-git-log-entries.js +0 -11
- package/report-committers/src/lib/parse-git-log-entries.js.map +0 -1
- package/report-committers/src/lib/parse-monthly.d.ts +0 -4
- package/report-committers/src/lib/parse-monthly.js +0 -56
- package/report-committers/src/lib/parse-monthly.js.map +0 -1
- package/report-committers/src/lib/types.d.ts +0 -27
- package/report-committers/src/lib/types.js +0 -3
- package/report-committers/src/lib/types.js.map +0 -1
- package/report-diagnostics/README.md +0 -11
- package/report-diagnostics/package.json +0 -15
- package/report-diagnostics/src/index.d.ts +0 -1
- package/report-diagnostics/src/index.js +0 -5
- package/report-diagnostics/src/index.js.map +0 -1
- package/report-diagnostics/src/lib/diagnostics.d.ts +0 -7
- package/report-diagnostics/src/lib/diagnostics.js +0 -94
- package/report-diagnostics/src/lib/diagnostics.js.map +0 -1
- package/report-diagnostics/src/lib/get-diagnostic-types.d.ts +0 -4
- package/report-diagnostics/src/lib/get-diagnostic-types.js +0 -15
- package/report-diagnostics/src/lib/get-diagnostic-types.js.map +0 -1
- package/report-diagnostics/src/lib/get-file-contents.d.ts +0 -1
- package/report-diagnostics/src/lib/get-file-contents.js +0 -12
- package/report-diagnostics/src/lib/get-file-contents.js.map +0 -1
- package/report-diagnostics/src/lib/get-package-json-section.d.ts +0 -1
- package/report-diagnostics/src/lib/get-package-json-section.js +0 -15
- package/report-diagnostics/src/lib/get-package-json-section.js.map +0 -1
- package/report-ingestion/README.md +0 -11
- package/report-ingestion/package.json +0 -13
- package/report-ingestion/src/index.d.ts +0 -1
- package/report-ingestion/src/index.js +0 -5
- package/report-ingestion/src/index.js.map +0 -1
- package/report-ingestion/src/lib/ingestion.d.ts +0 -3
- package/report-ingestion/src/lib/ingestion.js +0 -40
- package/report-ingestion/src/lib/ingestion.js.map +0 -1
- package/report-ingestion/src/lib/prompts.d.ts +0 -5
- package/report-ingestion/src/lib/prompts.js +0 -46
- package/report-ingestion/src/lib/prompts.js.map +0 -1
- package/report-ingestion/src/lib/queries.js.map +0 -1
- package/report-ingestion/src/lib/send-manifest.d.ts +0 -10
- package/report-ingestion/src/lib/send-manifest.js +0 -129
- package/report-ingestion/src/lib/send-manifest.js.map +0 -1
- package/report-ingestion/src/lib/types.d.ts +0 -6
- package/report-ingestion/src/lib/types.js +0 -3
- package/report-ingestion/src/lib/types.js.map +0 -1
- package/sea-config.json +0 -5
- package/src/lib/cli.d.ts +0 -1
- package/src/lib/cli.js +0 -22
- package/src/lib/cli.js.map +0 -1
- package/src/lib/create-group-command.d.ts +0 -2
- package/src/lib/create-group-command.js +0 -28
- package/src/lib/create-group-command.js.map +0 -1
- package/src/lib/ensure-version.d.ts +0 -2
- package/src/lib/ensure-version.js +0 -71
- package/src/lib/ensure-version.js.map +0 -1
- package/src/lib/get-commands.d.ts +0 -2
- package/src/lib/get-commands.js +0 -27
- package/src/lib/get-commands.js.map +0 -1
- package/src/lib/log-colors.d.ts +0 -28
- package/src/lib/log-colors.js +0 -9
- package/src/lib/log-colors.js.map +0 -1
- package/src/main.d.ts +0 -2
- package/src/main.js +0 -6
- package/src/main.js.map +0 -1
- package/tracker-init/README.md +0 -11
- package/tracker-init/package.json +0 -15
- package/tracker-init/src/index.d.ts +0 -1
- package/tracker-init/src/index.js +0 -5
- package/tracker-init/src/index.js.map +0 -1
- package/tracker-init/src/lib/default-config.d.ts +0 -2
- package/tracker-init/src/lib/default-config.js +0 -20
- package/tracker-init/src/lib/default-config.js.map +0 -1
- package/tracker-init/src/lib/init.d.ts +0 -5
- package/tracker-init/src/lib/init.js +0 -25
- package/tracker-init/src/lib/init.js.map +0 -1
- package/tracker-run/README.md +0 -11
- package/tracker-run/package.json +0 -18
- package/tracker-run/src/index.d.ts +0 -1
- package/tracker-run/src/index.js +0 -5
- package/tracker-run/src/index.js.map +0 -1
- package/tracker-run/src/lib/get-data-filepath.d.ts +0 -1
- package/tracker-run/src/lib/get-data-filepath.js +0 -8
- package/tracker-run/src/lib/get-data-filepath.js.map +0 -1
- package/tracker-run/src/lib/get-data.d.ts +0 -1
- package/tracker-run/src/lib/get-data.js +0 -14
- package/tracker-run/src/lib/get-data.js.map +0 -1
- package/tracker-run/src/lib/get-git-commit.d.ts +0 -4
- package/tracker-run/src/lib/get-git-commit.js +0 -32
- package/tracker-run/src/lib/get-git-commit.js.map +0 -1
- package/tracker-run/src/lib/process-category.d.ts +0 -2
- package/tracker-run/src/lib/process-category.js +0 -151
- package/tracker-run/src/lib/process-category.js.map +0 -1
- package/tracker-run/src/lib/process-config.d.ts +0 -2
- package/tracker-run/src/lib/process-config.js +0 -19
- package/tracker-run/src/lib/process-config.js.map +0 -1
- package/tracker-run/src/lib/run.d.ts +0 -7
- package/tracker-run/src/lib/run.js +0 -29
- package/tracker-run/src/lib/run.js.map +0 -1
- package/tracker-run/src/lib/save-results.d.ts +0 -2
- package/tracker-run/src/lib/save-results.js +0 -19
- package/tracker-run/src/lib/save-results.js.map +0 -1
- package/tracker-shared/README.md +0 -11
- package/tracker-shared/package.json +0 -12
- package/tracker-shared/src/index.d.ts +0 -2
- package/tracker-shared/src/index.js +0 -14
- package/tracker-shared/src/index.js.map +0 -1
- package/tracker-shared/src/lib/models/aggregate-result.d.ts +0 -4
- package/tracker-shared/src/lib/models/aggregate-result.js +0 -3
- package/tracker-shared/src/lib/models/aggregate-result.js.map +0 -1
- package/tracker-shared/src/lib/models/category-result.d.ts +0 -7
- package/tracker-shared/src/lib/models/category-result.js +0 -3
- package/tracker-shared/src/lib/models/category-result.js.map +0 -1
- package/tracker-shared/src/lib/models/category.d.ts +0 -9
- package/tracker-shared/src/lib/models/category.js +0 -3
- package/tracker-shared/src/lib/models/category.js.map +0 -1
- package/tracker-shared/src/lib/models/config.d.ts +0 -8
- package/tracker-shared/src/lib/models/config.js +0 -3
- package/tracker-shared/src/lib/models/config.js.map +0 -1
- package/tracker-shared/src/lib/models/file-result.d.ts +0 -5
- package/tracker-shared/src/lib/models/file-result.js +0 -3
- package/tracker-shared/src/lib/models/file-result.js.map +0 -1
- package/tracker-shared/src/lib/models/index.d.ts +0 -8
- package/tracker-shared/src/lib/models/index.js +0 -12
- package/tracker-shared/src/lib/models/index.js.map +0 -1
- package/tracker-shared/src/lib/models/process-result.d.ts +0 -6
- package/tracker-shared/src/lib/models/process-result.js +0 -3
- package/tracker-shared/src/lib/models/process-result.js.map +0 -1
- package/tracker-shared/src/lib/models/result.d.ts +0 -11
- package/tracker-shared/src/lib/models/result.js +0 -3
- package/tracker-shared/src/lib/models/result.js.map +0 -1
- package/tracker-shared/src/lib/models/total-result.d.ts +0 -4
- package/tracker-shared/src/lib/models/total-result.js +0 -3
- package/tracker-shared/src/lib/models/total-result.js.map +0 -1
- package/tracker-shared/src/lib/read-config.d.ts +0 -2
- package/tracker-shared/src/lib/read-config.js +0 -13
- package/tracker-shared/src/lib/read-config.js.map +0 -1
- package/utility/README.md +0 -11
- package/utility/package.json +0 -13
- package/utility/src/index.d.ts +0 -4
- package/utility/src/index.js +0 -8
- package/utility/src/index.js.map +0 -1
- package/utility/src/lib/get-project-types.d.ts +0 -2
- package/utility/src/lib/get-project-types.js +0 -13
- package/utility/src/lib/get-project-types.js.map +0 -1
- package/utility/src/lib/get-root-dir.d.ts +0 -1
- package/utility/src/lib/get-root-dir.js +0 -12
- package/utility/src/lib/get-root-dir.js.map +0 -1
- package/utility/src/lib/run-command.d.ts +0 -1
- package/utility/src/lib/run-command.js +0 -31
- package/utility/src/lib/run-command.js.map +0 -1
- package/utility/src/lib/sort-by-name.d.ts +0 -3
- package/utility/src/lib/sort-by-name.js +0 -7
- package/utility/src/lib/sort-by-name.js.map +0 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Command, Flags, ux } from '@oclif/core';
|
|
2
|
+
import { prepareRows, scanForEol } from "../../service/eol/eol.svc.js";
|
|
3
|
+
import { promptComponentDetails } from "../../ui/eol.ui.js";
|
|
4
|
+
import SbomScan from "./sbom.js";
|
|
5
|
+
export default class ScanEol extends Command {
|
|
6
|
+
static description = 'Scan a given sbom for EOL data';
|
|
7
|
+
static enableJsonFlag = true;
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> <%= command.id %> --dir=./my-project',
|
|
10
|
+
'<%= config.bin %> <%= command.id %> --file=path/to/sbom.json',
|
|
11
|
+
];
|
|
12
|
+
static flags = {
|
|
13
|
+
file: Flags.string({
|
|
14
|
+
char: 'f',
|
|
15
|
+
description: 'The file path of an existing cyclonedx sbom to scan for EOL',
|
|
16
|
+
}),
|
|
17
|
+
dir: Flags.string({
|
|
18
|
+
char: 'd',
|
|
19
|
+
description: 'The directory to scan in order to create a cyclonedx sbom',
|
|
20
|
+
}),
|
|
21
|
+
save: Flags.boolean({
|
|
22
|
+
char: 's',
|
|
23
|
+
default: false,
|
|
24
|
+
description: 'Save the generated SBOM as nes.sbom.json in the scanned directory',
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
async run() {
|
|
28
|
+
this.checkEolScanDisabled();
|
|
29
|
+
const { flags } = await this.parse(ScanEol);
|
|
30
|
+
const { dir: _dirFlag, file: _fileFlag } = flags;
|
|
31
|
+
// Load the SBOM: Only pass the file, dir, and save flags to SbomScan
|
|
32
|
+
const sbomArgs = SbomScan.getSbomArgs(flags);
|
|
33
|
+
const sbomCommand = new SbomScan(sbomArgs, this.config);
|
|
34
|
+
const sbom = await sbomCommand.run();
|
|
35
|
+
// Scan the SBOM for EOL information
|
|
36
|
+
const { purls, scan } = await scanForEol(sbom);
|
|
37
|
+
ux.action.stop('Scan completed');
|
|
38
|
+
if (!scan?.components) {
|
|
39
|
+
if (_fileFlag) {
|
|
40
|
+
throw new Error(`Scan failed to generate for file path: ${_fileFlag}`);
|
|
41
|
+
}
|
|
42
|
+
if (_dirFlag) {
|
|
43
|
+
throw new Error(`Scan failed to generate for dir: ${_dirFlag}`);
|
|
44
|
+
}
|
|
45
|
+
throw new Error('Scan failed to generate components.');
|
|
46
|
+
}
|
|
47
|
+
const lines = await prepareRows(purls, scan);
|
|
48
|
+
if (lines?.length === 0) {
|
|
49
|
+
this.log('No dependencies found');
|
|
50
|
+
return { components: [] };
|
|
51
|
+
}
|
|
52
|
+
const r = await promptComponentDetails(lines);
|
|
53
|
+
this.log('What now %o', r);
|
|
54
|
+
return scan;
|
|
55
|
+
}
|
|
56
|
+
checkEolScanDisabled(override = true) {
|
|
57
|
+
// Check if running in beta version or pre v1.0.0
|
|
58
|
+
const version = this.config.version;
|
|
59
|
+
const [major] = version.split('.').map(Number);
|
|
60
|
+
if (version.includes('beta') || major < 1) {
|
|
61
|
+
this.log(`VERSION=${version}`);
|
|
62
|
+
throw new Error('The EOL scan feature is not available in beta releases. Please wait for the stable release.');
|
|
63
|
+
}
|
|
64
|
+
// Just in case the beta check fails
|
|
65
|
+
if (override) {
|
|
66
|
+
this.log(`VERSION=${version}`);
|
|
67
|
+
this.log('EOL scan is disabled');
|
|
68
|
+
return { components: [] };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import type { Sbom } from '../../service/eol/cdx.svc.ts';
|
|
3
|
+
export default class ScanSbom extends Command {
|
|
4
|
+
static description: string;
|
|
5
|
+
static enableJsonFlag: boolean;
|
|
6
|
+
static examples: string[];
|
|
7
|
+
static flags: {
|
|
8
|
+
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
dir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
save: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
static getSbomArgs(flags: Record<string, string>): string[];
|
|
13
|
+
getScanOptions(): {};
|
|
14
|
+
run(): Promise<Sbom>;
|
|
15
|
+
private _getSbomFromScan;
|
|
16
|
+
private _getSbomFromFile;
|
|
17
|
+
private _saveSbom;
|
|
18
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { Command, Flags, ux } from '@oclif/core';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { createSbom, validateIsCycloneDxSbom } from "../../service/eol/eol.svc.js";
|
|
5
|
+
export default class ScanSbom extends Command {
|
|
6
|
+
static description = 'Scan a SBOM for purls';
|
|
7
|
+
static enableJsonFlag = true;
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> <%= command.id %> --dir=./my-project',
|
|
10
|
+
'<%= config.bin %> <%= command.id %> --file=path/to/sbom.json',
|
|
11
|
+
];
|
|
12
|
+
static flags = {
|
|
13
|
+
file: Flags.string({
|
|
14
|
+
char: 'f',
|
|
15
|
+
description: 'The file path of an existing cyclonedx sbom to scan for EOL',
|
|
16
|
+
}),
|
|
17
|
+
dir: Flags.string({
|
|
18
|
+
char: 'd',
|
|
19
|
+
description: 'The directory to scan in order to create a cyclonedx sbom',
|
|
20
|
+
}),
|
|
21
|
+
save: Flags.boolean({
|
|
22
|
+
char: 's',
|
|
23
|
+
default: false,
|
|
24
|
+
description: 'Save the generated SBOM as nes.sbom.json in the scanned directory',
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
static getSbomArgs(flags) {
|
|
28
|
+
const { dir, file, save } = flags ?? {};
|
|
29
|
+
const sbomArgs = [];
|
|
30
|
+
if (file)
|
|
31
|
+
sbomArgs.push('--file', file);
|
|
32
|
+
if (dir)
|
|
33
|
+
sbomArgs.push('--dir', dir);
|
|
34
|
+
if (save)
|
|
35
|
+
sbomArgs.push('--save');
|
|
36
|
+
return sbomArgs;
|
|
37
|
+
}
|
|
38
|
+
getScanOptions() {
|
|
39
|
+
// intentionally provided for mocking
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
async run() {
|
|
43
|
+
const { flags } = await this.parse(ScanSbom);
|
|
44
|
+
const { dir: _dirFlag, save, file: _fileFlag } = flags;
|
|
45
|
+
// Validate that exactly one of --file or --dir is provided
|
|
46
|
+
if (_fileFlag && _dirFlag) {
|
|
47
|
+
throw new Error('Cannot specify both --file and --dir flags. Please use one or the other.');
|
|
48
|
+
}
|
|
49
|
+
let sbom;
|
|
50
|
+
if (_fileFlag) {
|
|
51
|
+
sbom = this._getSbomFromFile(_fileFlag);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const _dir = _dirFlag || process.cwd();
|
|
55
|
+
sbom = await this._getSbomFromScan(_dir);
|
|
56
|
+
if (save) {
|
|
57
|
+
this._saveSbom(_dir, sbom);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return sbom;
|
|
61
|
+
}
|
|
62
|
+
async _getSbomFromScan(_dirFlag) {
|
|
63
|
+
const dir = path.resolve(_dirFlag);
|
|
64
|
+
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
|
65
|
+
throw new Error(`Directory not found or not a directory: ${dir}`);
|
|
66
|
+
}
|
|
67
|
+
ux.action.start(`Scanning ${dir}`);
|
|
68
|
+
const options = this.getScanOptions();
|
|
69
|
+
const sbom = await createSbom(dir, options);
|
|
70
|
+
if (!sbom) {
|
|
71
|
+
throw new Error(`SBOM failed to generate for dir: ${dir}`);
|
|
72
|
+
}
|
|
73
|
+
return sbom;
|
|
74
|
+
}
|
|
75
|
+
_getSbomFromFile(_fileFlag) {
|
|
76
|
+
const file = path.resolve(_fileFlag);
|
|
77
|
+
if (!fs.existsSync(file)) {
|
|
78
|
+
throw new Error(`SBOM file not found: ${file}`);
|
|
79
|
+
}
|
|
80
|
+
ux.action.start(`Loading sbom from ${file}`);
|
|
81
|
+
try {
|
|
82
|
+
const fileContent = fs.readFileSync(file, {
|
|
83
|
+
encoding: 'utf8',
|
|
84
|
+
flag: 'r',
|
|
85
|
+
});
|
|
86
|
+
const sbom = JSON.parse(fileContent);
|
|
87
|
+
validateIsCycloneDxSbom(sbom);
|
|
88
|
+
return sbom;
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
92
|
+
throw new Error(`Failed to read or parse SBOM file: ${errorMessage}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
_saveSbom(dir, sbom) {
|
|
96
|
+
try {
|
|
97
|
+
const outputPath = path.join(dir, 'nes.sbom.json');
|
|
98
|
+
fs.writeFileSync(outputPath, JSON.stringify(sbom, null, 2));
|
|
99
|
+
this.log(`SBOM saved to ${outputPath}`);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
103
|
+
this.warn(`Failed to save SBOM: ${errorMessage}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
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;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { run } from '@oclif/core';
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export interface CommitEntry {
|
|
2
|
+
month: string;
|
|
3
|
+
author: string;
|
|
4
|
+
}
|
|
5
|
+
export interface AuthorCommitCounts {
|
|
6
|
+
[author: string]: number;
|
|
7
|
+
}
|
|
8
|
+
export interface MonthlyData {
|
|
9
|
+
[month: string]: AuthorCommitCounts;
|
|
10
|
+
}
|
|
11
|
+
export interface ReportData {
|
|
12
|
+
monthly: {
|
|
13
|
+
[month: string]: {
|
|
14
|
+
[author: string]: number;
|
|
15
|
+
total: number;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
overall: {
|
|
19
|
+
[author: string]: number;
|
|
20
|
+
total: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Parses git log output into structured data
|
|
25
|
+
* @param output - Git log command output
|
|
26
|
+
* @returns Parsed commit entries
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseGitLogOutput(output: string): CommitEntry[];
|
|
29
|
+
/**
|
|
30
|
+
* Groups commit data by month
|
|
31
|
+
* @param entries - Commit entries
|
|
32
|
+
* @returns Object with months as keys and author commit counts as values
|
|
33
|
+
*/
|
|
34
|
+
export declare function groupCommitsByMonth(entries: CommitEntry[]): MonthlyData;
|
|
35
|
+
/**
|
|
36
|
+
* Calculates overall commit statistics by author
|
|
37
|
+
* @param entries - Commit entries
|
|
38
|
+
* @returns Object with authors as keys and total commit counts as values
|
|
39
|
+
*/
|
|
40
|
+
export declare function calculateOverallStats(entries: CommitEntry[]): AuthorCommitCounts;
|
|
41
|
+
/**
|
|
42
|
+
* Formats monthly report sections
|
|
43
|
+
* @param monthlyData - Grouped commit data by month
|
|
44
|
+
* @returns Formatted monthly report sections
|
|
45
|
+
*/
|
|
46
|
+
export declare function formatMonthlyReport(monthlyData: MonthlyData): string;
|
|
47
|
+
/**
|
|
48
|
+
* Formats overall statistics section
|
|
49
|
+
* @param overallStats - Overall commit counts by author
|
|
50
|
+
* @param grandTotal - Total number of commits
|
|
51
|
+
* @returns Formatted overall statistics section
|
|
52
|
+
*/
|
|
53
|
+
export declare function formatOverallStats(overallStats: AuthorCommitCounts, grandTotal: number): string;
|
|
54
|
+
/**
|
|
55
|
+
* Formats the report data as CSV
|
|
56
|
+
* @param data - The structured report data
|
|
57
|
+
*/
|
|
58
|
+
export declare function formatAsCsv(data: ReportData): string;
|
|
59
|
+
/**
|
|
60
|
+
* Formats the report data as text
|
|
61
|
+
* @param data - The structured report data
|
|
62
|
+
*/
|
|
63
|
+
export declare function formatAsText(data: ReportData): string;
|
|
64
|
+
/**
|
|
65
|
+
* Format output based on user preference
|
|
66
|
+
* @param output
|
|
67
|
+
* @param reportData
|
|
68
|
+
* @returns
|
|
69
|
+
*/
|
|
70
|
+
export declare function formatOutputBasedOnFlag(output: string, reportData: ReportData): string;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses git log output into structured data
|
|
3
|
+
* @param output - Git log command output
|
|
4
|
+
* @returns Parsed commit entries
|
|
5
|
+
*/
|
|
6
|
+
export function parseGitLogOutput(output) {
|
|
7
|
+
return output
|
|
8
|
+
.split('\n')
|
|
9
|
+
.filter(Boolean)
|
|
10
|
+
.map((line) => {
|
|
11
|
+
// Remove surrounding double quotes if present (e.g. "March|John Doe" → March|John Doe)
|
|
12
|
+
const [month, author] = line.replace(/^"(.*)"$/, '$1').split('|');
|
|
13
|
+
return { month, author };
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Groups commit data by month
|
|
18
|
+
* @param entries - Commit entries
|
|
19
|
+
* @returns Object with months as keys and author commit counts as values
|
|
20
|
+
*/
|
|
21
|
+
export function groupCommitsByMonth(entries) {
|
|
22
|
+
const result = {};
|
|
23
|
+
// Group commits by month
|
|
24
|
+
const commitsByMonth = Object.groupBy(entries, (entry) => entry.month);
|
|
25
|
+
// Process each month
|
|
26
|
+
for (const [month, commits] of Object.entries(commitsByMonth)) {
|
|
27
|
+
if (!commits) {
|
|
28
|
+
result[month] = {};
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
// Count commits per author for this month
|
|
32
|
+
const commitsByAuthor = Object.groupBy(commits, (entry) => entry.author);
|
|
33
|
+
const authorCounts = {};
|
|
34
|
+
for (const [author, authorCommits] of Object.entries(commitsByAuthor)) {
|
|
35
|
+
authorCounts[author] = authorCommits?.length ?? 0;
|
|
36
|
+
}
|
|
37
|
+
result[month] = authorCounts;
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Calculates overall commit statistics by author
|
|
43
|
+
* @param entries - Commit entries
|
|
44
|
+
* @returns Object with authors as keys and total commit counts as values
|
|
45
|
+
*/
|
|
46
|
+
export function calculateOverallStats(entries) {
|
|
47
|
+
const commitsByAuthor = Object.groupBy(entries, (entry) => entry.author);
|
|
48
|
+
const result = {};
|
|
49
|
+
// Count commits for each author
|
|
50
|
+
for (const author in commitsByAuthor) {
|
|
51
|
+
result[author] = commitsByAuthor[author]?.length ?? 0;
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Formats monthly report sections
|
|
57
|
+
* @param monthlyData - Grouped commit data by month
|
|
58
|
+
* @returns Formatted monthly report sections
|
|
59
|
+
*/
|
|
60
|
+
export function formatMonthlyReport(monthlyData) {
|
|
61
|
+
const sortedMonths = Object.keys(monthlyData).sort();
|
|
62
|
+
let report = '';
|
|
63
|
+
for (const month of sortedMonths) {
|
|
64
|
+
report += `\n## ${month}\n`;
|
|
65
|
+
const authors = Object.entries(monthlyData[month]).sort((a, b) => b[1] - a[1]);
|
|
66
|
+
for (const [author, count] of authors) {
|
|
67
|
+
report += `${count.toString().padStart(6)} ${author}\n`;
|
|
68
|
+
}
|
|
69
|
+
const monthTotal = authors.reduce((sum, [_, count]) => sum + count, 0);
|
|
70
|
+
report += `${monthTotal.toString().padStart(6)} TOTAL\n`;
|
|
71
|
+
}
|
|
72
|
+
return report;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Formats overall statistics section
|
|
76
|
+
* @param overallStats - Overall commit counts by author
|
|
77
|
+
* @param grandTotal - Total number of commits
|
|
78
|
+
* @returns Formatted overall statistics section
|
|
79
|
+
*/
|
|
80
|
+
export function formatOverallStats(overallStats, grandTotal) {
|
|
81
|
+
let report = '\n## Overall Statistics\n';
|
|
82
|
+
const sortedStats = Object.entries(overallStats).sort((a, b) => b[1] - a[1]);
|
|
83
|
+
for (const [author, count] of sortedStats) {
|
|
84
|
+
report += `${count.toString().padStart(6)} ${author}\n`;
|
|
85
|
+
}
|
|
86
|
+
report += `${grandTotal.toString().padStart(6)} GRAND TOTAL\n`;
|
|
87
|
+
return report;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Formats the report data as CSV
|
|
91
|
+
* @param data - The structured report data
|
|
92
|
+
*/
|
|
93
|
+
export function formatAsCsv(data) {
|
|
94
|
+
// First prepare all author names (for columns)
|
|
95
|
+
const allAuthors = new Set();
|
|
96
|
+
// Collect all unique author names
|
|
97
|
+
for (const monthData of Object.values(data.monthly)) {
|
|
98
|
+
for (const author of Object.keys(monthData)) {
|
|
99
|
+
if (author !== 'total')
|
|
100
|
+
allAuthors.add(author);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const authors = Array.from(allAuthors).sort();
|
|
104
|
+
// Create CSV header
|
|
105
|
+
let csv = `Month,${authors.join(',')},Total\n`;
|
|
106
|
+
// Add monthly data rows
|
|
107
|
+
const sortedMonths = Object.keys(data.monthly).sort();
|
|
108
|
+
for (const month of sortedMonths) {
|
|
109
|
+
csv += month;
|
|
110
|
+
// Add data for each author
|
|
111
|
+
for (const author of authors) {
|
|
112
|
+
const count = data.monthly[month][author] || 0;
|
|
113
|
+
csv += `,${count}`;
|
|
114
|
+
}
|
|
115
|
+
// Add monthly total
|
|
116
|
+
csv += `,${`${data.monthly[month].total}\n`}`;
|
|
117
|
+
}
|
|
118
|
+
// Add overall totals row
|
|
119
|
+
csv += 'Overall';
|
|
120
|
+
for (const author of authors) {
|
|
121
|
+
const count = data.overall[author] || 0;
|
|
122
|
+
csv += `,${count}`;
|
|
123
|
+
}
|
|
124
|
+
csv += `,${data.overall.total}\n`;
|
|
125
|
+
return csv;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Formats the report data as text
|
|
129
|
+
* @param data - The structured report data
|
|
130
|
+
*/
|
|
131
|
+
export function formatAsText(data) {
|
|
132
|
+
let report = 'Monthly Commit Report\n';
|
|
133
|
+
// Monthly sections
|
|
134
|
+
const sortedMonths = Object.keys(data.monthly).sort();
|
|
135
|
+
for (const month of sortedMonths) {
|
|
136
|
+
report += `\n## ${month}\n`;
|
|
137
|
+
const authors = Object.entries(data.monthly[month])
|
|
138
|
+
.filter(([author]) => author !== 'total')
|
|
139
|
+
.sort((a, b) => b[1] - a[1]);
|
|
140
|
+
for (const [author, count] of authors) {
|
|
141
|
+
report += `${count.toString().padStart(6)} ${author}\n`;
|
|
142
|
+
}
|
|
143
|
+
report += `${data.monthly[month].total.toString().padStart(6)} TOTAL\n`;
|
|
144
|
+
}
|
|
145
|
+
// Overall statistics
|
|
146
|
+
report += '\n## Overall Statistics\n';
|
|
147
|
+
const sortedEntries = Object.entries(data.overall)
|
|
148
|
+
.filter(([author]) => author !== 'total')
|
|
149
|
+
.sort((a, b) => b[1] - a[1]);
|
|
150
|
+
for (const [author, count] of sortedEntries) {
|
|
151
|
+
report += `${count.toString().padStart(6)} ${author}\n`;
|
|
152
|
+
}
|
|
153
|
+
report += `${data.overall.total.toString().padStart(6)} GRAND TOTAL\n`;
|
|
154
|
+
return report;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Format output based on user preference
|
|
158
|
+
* @param output
|
|
159
|
+
* @param reportData
|
|
160
|
+
* @returns
|
|
161
|
+
*/
|
|
162
|
+
export function formatOutputBasedOnFlag(output, reportData) {
|
|
163
|
+
let formattedOutput;
|
|
164
|
+
switch (output) {
|
|
165
|
+
case 'json':
|
|
166
|
+
formattedOutput = JSON.stringify(reportData, null, 2);
|
|
167
|
+
break;
|
|
168
|
+
case 'csv':
|
|
169
|
+
formattedOutput = formatAsCsv(reportData);
|
|
170
|
+
break;
|
|
171
|
+
default:
|
|
172
|
+
formattedOutput = formatAsText(reportData);
|
|
173
|
+
}
|
|
174
|
+
return formattedOutput;
|
|
175
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CdxCreator, CdxGenOptions } from './eol.svc.ts';
|
|
2
|
+
export interface SbomEntry {
|
|
3
|
+
group: string;
|
|
4
|
+
name: string;
|
|
5
|
+
purl: string;
|
|
6
|
+
version: string;
|
|
7
|
+
}
|
|
8
|
+
export interface Sbom {
|
|
9
|
+
components: SbomEntry[];
|
|
10
|
+
dependencies: SbomEntry[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Lazy loads cdxgen (for ESM purposes), scans
|
|
14
|
+
* `directory`, and returns the `bomJson` property.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createBomFromDir(directory: string, opts?: CdxGenOptions): Promise<Sbom | undefined>;
|
|
17
|
+
export declare const cdxgen: {
|
|
18
|
+
createBom: CdxCreator | undefined;
|
|
19
|
+
};
|
|
20
|
+
export declare function getCdxGen(): Promise<{
|
|
21
|
+
createBom: CdxCreator | undefined;
|
|
22
|
+
}>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { log } from "../../service/log.svc.js";
|
|
2
|
+
/**
|
|
3
|
+
* Lazy loads cdxgen (for ESM purposes), scans
|
|
4
|
+
* `directory`, and returns the `bomJson` property.
|
|
5
|
+
*/
|
|
6
|
+
export async function createBomFromDir(directory, opts = {}) {
|
|
7
|
+
const options = {
|
|
8
|
+
$0: 'cdxgen',
|
|
9
|
+
_: [],
|
|
10
|
+
'auto-compositions': true,
|
|
11
|
+
autoCompositions: true,
|
|
12
|
+
'data-flow-slices-file': 'data-flow.slices.json',
|
|
13
|
+
dataFlowSlicesFile: 'data-flow.slices.json',
|
|
14
|
+
deep: false, // TODO: you def want to check this out
|
|
15
|
+
'deps-slices-file': 'deps.slices.json',
|
|
16
|
+
depsSlicesFile: 'deps.slices.json',
|
|
17
|
+
evidence: false,
|
|
18
|
+
'export-proto': false,
|
|
19
|
+
exportProto: false,
|
|
20
|
+
// DON'T FAIL ON ERROR; you won't get hlepful logs
|
|
21
|
+
'fail-on-error': false,
|
|
22
|
+
failOnError: false,
|
|
23
|
+
false: true,
|
|
24
|
+
'include-crypto': false,
|
|
25
|
+
'include-formulation': false,
|
|
26
|
+
includeCrypto: false,
|
|
27
|
+
includeFormulation: false,
|
|
28
|
+
// 'server-host': '127.0.0.1',
|
|
29
|
+
// serverHost: '127.0.0.1',
|
|
30
|
+
// 'server-port': '9090',
|
|
31
|
+
// serverPort: '9090',
|
|
32
|
+
'install-deps': true,
|
|
33
|
+
installDeps: true,
|
|
34
|
+
'min-confidence': 0,
|
|
35
|
+
minConfidence: 0,
|
|
36
|
+
multiProject: true,
|
|
37
|
+
'no-banner': false,
|
|
38
|
+
noBabel: false,
|
|
39
|
+
noBanner: false,
|
|
40
|
+
o: 'bom.json',
|
|
41
|
+
output: 'bom.json',
|
|
42
|
+
outputFormat: 'json', // or "xml"
|
|
43
|
+
// author: ['OWASP Foundation'],
|
|
44
|
+
profile: 'generic',
|
|
45
|
+
project: undefined,
|
|
46
|
+
'project-version': '',
|
|
47
|
+
projectVersion: '',
|
|
48
|
+
'proto-bin-file': 'bom.cdx',
|
|
49
|
+
protoBinFile: 'bom.cdx',
|
|
50
|
+
r: false,
|
|
51
|
+
'reachables-slices-file': 'reachables.slices.json',
|
|
52
|
+
reachablesSlicesFile: 'reachables.slices.json',
|
|
53
|
+
recurse: false,
|
|
54
|
+
requiredOnly: false,
|
|
55
|
+
'semantics-slices-file': 'semantics.slices.json',
|
|
56
|
+
semanticsSlicesFile: 'semantics.slices.json',
|
|
57
|
+
'skip-dt-tls-check': true,
|
|
58
|
+
skipDtTlsCheck: true,
|
|
59
|
+
'spec-version': 1.6,
|
|
60
|
+
specVersion: 1.6,
|
|
61
|
+
'usages-slices-file': 'usages.slices.json',
|
|
62
|
+
usagesSlicesFile: 'usages.slices.json',
|
|
63
|
+
validate: true,
|
|
64
|
+
...opts,
|
|
65
|
+
};
|
|
66
|
+
const { createBom } = await getCdxGen();
|
|
67
|
+
const sbom = await createBom?.(directory, options);
|
|
68
|
+
log.info('Successfully generated SBOM');
|
|
69
|
+
return sbom?.bomJson;
|
|
70
|
+
}
|
|
71
|
+
// use a value holder, for easier mocking
|
|
72
|
+
export const cdxgen = { createBom: undefined };
|
|
73
|
+
export async function getCdxGen() {
|
|
74
|
+
if (cdxgen.createBom) {
|
|
75
|
+
return cdxgen;
|
|
76
|
+
}
|
|
77
|
+
const ogEnv = process.env.NODE_ENV;
|
|
78
|
+
process.env.NODE_ENV = undefined;
|
|
79
|
+
try {
|
|
80
|
+
cdxgen.createBom = (await import('@cyclonedx/cdxgen')).createBom;
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
process.env.NODE_ENV = ogEnv;
|
|
84
|
+
}
|
|
85
|
+
return cdxgen;
|
|
86
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ScanResult } from '../../api/types/nes.types.ts';
|
|
2
|
+
import type { Line } from '../line.svc.ts';
|
|
3
|
+
import { type Sbom } from './cdx.svc.ts';
|
|
4
|
+
export interface CdxGenOptions {
|
|
5
|
+
projectType?: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface ScanOptions {
|
|
8
|
+
cdxgen?: CdxGenOptions;
|
|
9
|
+
}
|
|
10
|
+
export type CdxCreator = (dir: string, opts: CdxGenOptions) => Promise<{
|
|
11
|
+
bomJson: Sbom;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function createSbom(directory: string, opts?: ScanOptions): Promise<Sbom>;
|
|
14
|
+
export declare function validateIsCycloneDxSbom(sbom: unknown): asserts sbom is Sbom;
|
|
15
|
+
/**
|
|
16
|
+
* Main function to scan directory and collect SBOM data
|
|
17
|
+
*/
|
|
18
|
+
export declare function scanForEol(sbom: Sbom): Promise<{
|
|
19
|
+
purls: string[];
|
|
20
|
+
scan: ScanResult;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* Uses the purls from the sbom to run the scan.
|
|
24
|
+
*/
|
|
25
|
+
export declare function submitScan(purls: string[]): Promise<ScanResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Work in progress; creates "rows" for each component
|
|
28
|
+
* based on the model + the scan result from NES.
|
|
29
|
+
*
|
|
30
|
+
* The idea being that each row can easily be used for
|
|
31
|
+
* processing and/or rendering.
|
|
32
|
+
*/
|
|
33
|
+
export declare function prepareRows(purls: string[], scan: ScanResult): Promise<Line[]>;
|
|
34
|
+
export { cdxgen } from './cdx.svc.ts';
|