@trustify-da/trustify-da-javascript-client 0.3.0-ea.243eaef → 0.3.0-ea.24dd325

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.
@@ -0,0 +1,260 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { load as yamlLoad } from 'js-yaml';
4
+ /**
5
+ * @typedef {{
6
+ * 'group-by'?: 'dependency' | 'bundle',
7
+ * exclude?: string[],
8
+ * labels?: string[],
9
+ * 'branch-prefix'?: string,
10
+ * [key: string]: unknown
11
+ * }} RemediationConfig
12
+ */
13
+ /** @typedef {{ critical?: number, high?: number, 'license-conflicts'?: number, [key: string]: unknown }} FailOnConfig */
14
+ /** @typedef {{ 'fail-on'?: FailOnConfig, [key: string]: unknown }} CheckConfig */
15
+ /** @typedef {{ format?: string, targets?: string[], [key: string]: unknown }} SbomConfig */
16
+ /**
17
+ * @typedef {{
18
+ * 'backend-url'?: string,
19
+ * providers?: string[] | string,
20
+ * sources?: string[] | string,
21
+ * remediation?: RemediationConfig,
22
+ * check?: CheckConfig,
23
+ * sbom?: SbomConfig,
24
+ * [key: string]: unknown
25
+ * }} FileConfig
26
+ */
27
+ /**
28
+ * @typedef {{
29
+ * backendUrl: string | null,
30
+ * backendUrlSource: 'cli' | 'environment' | 'file' | 'default',
31
+ * providers: string[],
32
+ * sources: string[],
33
+ * groupBy: string,
34
+ * remediation: object,
35
+ * check: object,
36
+ * sbom: object
37
+ * }} ResolvedConfig
38
+ */
39
+ /** Config file names discovered by walking up the directory tree, in precedence order. */
40
+ export const CONFIG_FILENAMES = ['.trustify-da.yml', '.trustify-da.yaml'];
41
+ function isMapping(value) {
42
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
43
+ return false;
44
+ }
45
+ const prototype = Object.getPrototypeOf(value);
46
+ return prototype === Object.prototype || prototype == null;
47
+ }
48
+ function assertMapping(value, field, source) {
49
+ if (!isMapping(value)) {
50
+ throw new Error(`Invalid config file ${source}: ${field} must be a mapping`);
51
+ }
52
+ }
53
+ function assertString(value, field, source) {
54
+ if (typeof value !== 'string') {
55
+ throw new Error(`Invalid config file ${source}: ${field} must be a string`);
56
+ }
57
+ }
58
+ function assertStringArray(value, field, source) {
59
+ if (!Array.isArray(value) || value.some(item => typeof item !== 'string')) {
60
+ throw new Error(`Invalid config file ${source}: ${field} must be an array of strings`);
61
+ }
62
+ }
63
+ function assertStringOrArray(value, field, source) {
64
+ if (typeof value !== 'string') {
65
+ assertStringArray(value, field, source);
66
+ }
67
+ }
68
+ /**
69
+ * Validates all fields in the initial configuration schema. Unknown fields are preserved.
70
+ * @param {unknown} config
71
+ * @param {string} [source]
72
+ * @returns {FileConfig}
73
+ */
74
+ function validateConfig(config, source = '<config>') {
75
+ assertMapping(config, 'root', source);
76
+ if (config['backend-url'] !== undefined) {
77
+ assertString(config['backend-url'], 'backend-url', source);
78
+ }
79
+ if (config.providers !== undefined) {
80
+ assertStringOrArray(config.providers, 'providers', source);
81
+ }
82
+ if (config.sources !== undefined) {
83
+ assertStringOrArray(config.sources, 'sources', source);
84
+ }
85
+ if (config.remediation !== undefined) {
86
+ assertMapping(config.remediation, 'remediation', source);
87
+ const remediation = config.remediation;
88
+ if (remediation['group-by'] !== undefined && !['dependency', 'bundle'].includes(remediation['group-by'])) {
89
+ throw new Error(`Invalid config file ${source}: remediation.group-by must be dependency or bundle`);
90
+ }
91
+ if (remediation.exclude !== undefined) {
92
+ assertStringArray(remediation.exclude, 'remediation.exclude', source);
93
+ }
94
+ if (remediation.labels !== undefined) {
95
+ assertStringArray(remediation.labels, 'remediation.labels', source);
96
+ }
97
+ if (remediation['branch-prefix'] !== undefined) {
98
+ assertString(remediation['branch-prefix'], 'remediation.branch-prefix', source);
99
+ }
100
+ }
101
+ if (config.check !== undefined) {
102
+ assertMapping(config.check, 'check', source);
103
+ if (config.check['fail-on'] !== undefined) {
104
+ assertMapping(config.check['fail-on'], 'check.fail-on', source);
105
+ for (const field of ['critical', 'high', 'license-conflicts']) {
106
+ const value = config.check['fail-on'][field];
107
+ if (value !== undefined && (typeof value !== 'number' || !Number.isFinite(value))) {
108
+ throw new Error(`Invalid config file ${source}: check.fail-on.${field} must be a finite number`);
109
+ }
110
+ }
111
+ }
112
+ }
113
+ if (config.sbom !== undefined) {
114
+ assertMapping(config.sbom, 'sbom', source);
115
+ if (config.sbom.format !== undefined) {
116
+ assertString(config.sbom.format, 'sbom.format', source);
117
+ }
118
+ if (config.sbom.targets !== undefined) {
119
+ assertStringArray(config.sbom.targets, 'sbom.targets', source);
120
+ }
121
+ }
122
+ return config;
123
+ }
124
+ /**
125
+ * Walks up from `startPath` looking for a `.trustify-da.yml` (or `.yaml`) file,
126
+ * similar to how `.eslintrc` discovery works. If `startPath` points at a file,
127
+ * discovery begins in its containing directory. Within a directory, `.yml` takes
128
+ * precedence over `.yaml`.
129
+ * @param {string} startPath - manifest file or directory to start the search from
130
+ * @returns {string | null} absolute path to the config file, or null if none found
131
+ */
132
+ function findConfigFile(startPath) {
133
+ let dir = path.resolve(startPath || '.');
134
+ try {
135
+ if (fs.statSync(dir).isFile()) {
136
+ dir = path.dirname(dir);
137
+ }
138
+ }
139
+ catch {
140
+ // startPath may not exist yet — walk up from its resolved location anyway
141
+ }
142
+ // Walk up until the filesystem root (where dirname(dir) === dir)
143
+ for (;;) {
144
+ for (const name of CONFIG_FILENAMES) {
145
+ const candidate = path.join(dir, name);
146
+ try {
147
+ if (fs.statSync(candidate).isFile()) {
148
+ return candidate;
149
+ }
150
+ }
151
+ catch {
152
+ // Ignore inaccessible or disappearing candidates and continue discovery.
153
+ }
154
+ }
155
+ const parent = path.dirname(dir);
156
+ if (parent === dir) {
157
+ return null;
158
+ }
159
+ dir = parent;
160
+ }
161
+ }
162
+ /**
163
+ * Discovers and parses `.trustify-da.yml` by walking up from `startPath`.
164
+ * A missing config file is not an error — an empty object is returned so callers
165
+ * can fall back to defaults.
166
+ * @param {string} [startPath] - manifest file or directory to start the search from
167
+ * @returns {FileConfig} the parsed config object, or `{}` when no file is found
168
+ * @throws {Error} when the config file exists but contains malformed YAML
169
+ */
170
+ export function loadConfig(startPath) {
171
+ const configPath = findConfigFile(startPath);
172
+ if (!configPath) {
173
+ return {};
174
+ }
175
+ const content = fs.readFileSync(configPath, 'utf-8');
176
+ let doc;
177
+ try {
178
+ doc = yamlLoad(content);
179
+ }
180
+ catch (err) {
181
+ throw new Error(`Failed to parse config file ${configPath}: ${err.message}`);
182
+ }
183
+ return doc == null ? {} : validateConfig(doc, configPath);
184
+ }
185
+ /**
186
+ * Normalizes a providers/sources value to an array of trimmed strings. Accepts
187
+ * either a YAML array (`[redhat, osv]`) or a comma-separated string (`redhat,osv`).
188
+ * @param {string[] | string | undefined | null} value
189
+ * @returns {string[]}
190
+ */
191
+ function toArray(value) {
192
+ if (Array.isArray(value)) {
193
+ return value.filter(v => typeof v === 'string' && v.trim()).map(v => v.trim());
194
+ }
195
+ if (typeof value === 'string') {
196
+ return value.split(',').map(v => v.trim()).filter(Boolean);
197
+ }
198
+ return [];
199
+ }
200
+ /**
201
+ * Returns the first argument that is neither undefined nor null.
202
+ * @param {...unknown} values
203
+ * @returns {unknown}
204
+ */
205
+ function first(...values) {
206
+ return values.find(v => v != null);
207
+ }
208
+ /**
209
+ * Merges config sources with precedence: CLI flag > environment variable
210
+ * (`TRUSTIFY_DA_*`) > config file > hardcoded default. Providers and sources are
211
+ * normalized to arrays regardless of whether the source used a YAML array or a
212
+ * comma-separated string.
213
+ * @param {FileConfig} [fileConfig={}] - values parsed from `.trustify-da.yml`
214
+ * @param {{ providers?: string, sources?: string, groupBy?: string, backendUrl?: string }} [cliFlags={}]
215
+ * @param {{ [key: string]: string | undefined }} [envVars={}] - typically `process.env`
216
+ * @returns {ResolvedConfig} the resolved, typed config object
217
+ */
218
+ export function mergeConfig(fileConfig = {}, cliFlags = {}, envVars = {}) {
219
+ const file = validateConfig(fileConfig || {});
220
+ const cli = cliFlags || {};
221
+ const env = envVars || {};
222
+ let backendUrl = null;
223
+ let backendUrlSource = 'default';
224
+ if (cli.backendUrl != null) {
225
+ backendUrl = cli.backendUrl;
226
+ backendUrlSource = 'cli';
227
+ }
228
+ else if (env.TRUSTIFY_DA_BACKEND_URL != null) {
229
+ backendUrl = env.TRUSTIFY_DA_BACKEND_URL;
230
+ backendUrlSource = 'environment';
231
+ }
232
+ else if (file['backend-url'] != null) {
233
+ backendUrl = file['backend-url'];
234
+ backendUrlSource = 'file';
235
+ }
236
+ const groupBy = first(cli.groupBy, env.TRUSTIFY_DA_GROUP_BY, file.remediation?.['group-by']) ?? 'dependency';
237
+ if (!['dependency', 'bundle'].includes(groupBy)) {
238
+ throw new Error(`Invalid group-by value "${groupBy}". Expected dependency or bundle.`);
239
+ }
240
+ return {
241
+ backendUrl,
242
+ backendUrlSource,
243
+ providers: toArray(first(cli.providers, env.TRUSTIFY_DA_PROVIDERS, file.providers)),
244
+ sources: toArray(first(cli.sources, env.TRUSTIFY_DA_SOURCES, file.sources)),
245
+ groupBy,
246
+ remediation: file.remediation ?? {},
247
+ check: file.check ?? {},
248
+ sbom: file.sbom ?? {},
249
+ };
250
+ }
251
+ /**
252
+ * Loads and merges the project configuration for a target path.
253
+ * @param {string} [startPath] - manifest file or directory to start discovery from
254
+ * @param {{ providers?: string, sources?: string, groupBy?: string, backendUrl?: string }} [cliFlags={}] - explicit options
255
+ * @param {{ [key: string]: string | undefined }} [envVars={}] - typically `process.env`
256
+ * @returns {ResolvedConfig} the resolved, typed config object
257
+ */
258
+ export function resolveConfig(startPath, cliFlags = {}, envVars = {}) {
259
+ return mergeConfig(loadConfig(startPath), cliFlags, envVars);
260
+ }
@@ -1,8 +1,6 @@
1
1
  /**
2
- * This function is used to determine the Trustify DA backend URL.
3
- * The TRUSTIFY_DA_BACKEND_URL is evaluated in the following order and selected when it finds it first:
4
- * 1. Environment Variable
5
- * 2. (key,value) from opts object
2
+ * Selects the Trustify DA backend URL from opts.TRUSTIFY_DA_BACKEND_URL, then environment
3
+ * variable TRUSTIFY_DA_BACKEND_URL. Options take precedence over environment variables.
6
4
  * If TRUSTIFY_DA_BACKEND_URL is not set, the function will throw an error.
7
5
  * @param {{TRUSTIFY_DA_DEBUG?: string | undefined; TRUSTIFY_DA_BACKEND_URL?: string | undefined}} [opts={}]
8
6
  * @return {string} - The selected Trustify DA backend URL
@@ -64,6 +62,7 @@ export type Options = {
64
62
  TRUSTIFY_DA_YARN_PATH?: string | undefined;
65
63
  TRUSTIFY_DA_WORKSPACE_DIR?: string | undefined;
66
64
  TRUSTIFY_DA_LICENSE_CHECK?: string | undefined;
65
+ licenseCheck?: boolean | undefined;
67
66
  MATCH_MANIFEST_VERSIONS?: string | undefined;
68
67
  TRUSTIFY_DA_SOURCE?: string | undefined;
69
68
  TRUSTIFY_DA_TOKEN?: string | undefined;
@@ -106,10 +105,10 @@ export type SbomResult = {
106
105
  * Get component analysis report for a manifest content.
107
106
  * @param {string} manifest - path to the manifest
108
107
  * @param {Options} [opts={}] - optional various options to pass along the application
109
- * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
108
+ * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
110
109
  * @throws {Error} if no matching provider, failed to get create content, or backend request failed
111
110
  */
112
- declare function componentAnalysis(manifest: string, opts?: Options): Promise<any>;
111
+ declare function componentAnalysis(manifest: string, opts?: Options): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
113
112
  /**
114
113
  * @overload
115
114
  * @param {string} manifest
@@ -124,21 +123,21 @@ declare function stackAnalysis(manifest: string, html: true, opts?: Options | un
124
123
  * @param {string} manifest
125
124
  * @param {false} html
126
125
  * @param {Options} [opts={}]
127
- * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
126
+ * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
128
127
  * @throws {Error}
129
128
  */
130
- declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<any>;
129
+ declare function stackAnalysis(manifest: string, html: false, opts?: Options | undefined): Promise<import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
131
130
  /**
132
131
  * Get stack analysis report for a manifest file.
133
132
  * @overload
134
133
  * @param {string} manifest - path for the manifest
135
134
  * @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport object.
136
135
  * @param {Options} [opts={}] - optional various options to pass along the application
137
- * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
136
+ * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
138
137
  * @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
139
138
  * or backend request failed
140
139
  */
141
- declare function stackAnalysis(manifest: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | any>;
140
+ declare function stackAnalysis(manifest: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport>;
142
141
  /**
143
142
  * @overload
144
143
  * @param {string} workspaceRoot
@@ -169,14 +168,14 @@ declare function stackAnalysisBatch(workspaceRoot: string, html: true, opts?: (O
169
168
  * @param {string} workspaceRoot
170
169
  * @param {false} html
171
170
  * @param {Options & { batchMetadata: true }} opts
172
- * @returns {Promise<{ analysis: Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
171
+ * @returns {Promise<{ analysis: Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
173
172
  * @throws {Error}
174
173
  */
175
174
  declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts: Options & {
176
175
  batchMetadata: true;
177
176
  }): Promise<{
178
177
  analysis: {
179
- [x: string]: any;
178
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
180
179
  };
181
180
  metadata: BatchAnalysisMetadata;
182
181
  }>;
@@ -185,13 +184,13 @@ declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts: Op
185
184
  * @param {string} workspaceRoot
186
185
  * @param {false} html
187
186
  * @param {Options & { batchMetadata?: false }} [opts={}]
188
- * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
187
+ * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
189
188
  * @throws {Error}
190
189
  */
191
190
  declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts?: (Options & {
192
191
  batchMetadata?: false;
193
192
  }) | undefined): Promise<{
194
- [x: string]: any;
193
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
195
194
  }>;
196
195
  /**
197
196
  * Get stack analysis for all workspace packages/crates (batch).
@@ -203,14 +202,14 @@ declare function stackAnalysisBatch(workspaceRoot: string, html: false, opts?: (
203
202
  * @param {string} workspaceRoot - Path to workspace root (containing lock file and workspace config)
204
203
  * @param {boolean} [html=false] - true returns HTML, false returns JSON report
205
204
  * @param {Options} [opts={}] - `batchConcurrency`, discovery ignores, `continueOnError` (default true), `batchMetadata` (default false)
206
- * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>|{ analysis: string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
205
+ * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>|{ analysis: string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
207
206
  * @throws {Error} if workspace root invalid, no manifests found, no packages pass validation, no SBOMs produced, or backend request failed. When `opts.batchMetadata` is set, `error.batchMetadata` may be set on thrown errors.
208
207
  */
209
208
  declare function stackAnalysisBatch(workspaceRoot: string, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
210
- [x: string]: any;
209
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
211
210
  } | {
212
211
  analysis: string | {
213
- [x: string]: any;
212
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
214
213
  };
215
214
  metadata: BatchAnalysisMetadata;
216
215
  }>;
@@ -228,11 +227,11 @@ declare function imageAnalysis(imageRefs: Array<string>, html: true, opts?: Opti
228
227
  * @param {Array<string>} imageRefs
229
228
  * @param {false} html
230
229
  * @param {Options} [opts={}]
231
- * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
230
+ * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
232
231
  * @throws {Error}
233
232
  */
234
233
  declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Options | undefined): Promise<{
235
- [x: string]: any;
234
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
236
235
  }>;
237
236
  /**
238
237
  * Get image analysis report for a set of OCI image references.
@@ -240,12 +239,12 @@ declare function imageAnalysis(imageRefs: Array<string>, html: false, opts?: Opt
240
239
  * @param {Array<string>} imageRefs - OCI image references
241
240
  * @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport
242
241
  * @param {Options} [opts={}] - optional various options to pass along the application
243
- * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
242
+ * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
244
243
  * @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
245
244
  * or backend request failed
246
245
  */
247
246
  declare function imageAnalysis(imageRefs: Array<string>, html?: boolean | undefined, opts?: Options | undefined): Promise<string | {
248
- [x: string]: any;
247
+ [x: string]: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport;
249
248
  }>;
250
249
  /**
251
250
  * Validates the Exhort token.
@@ -268,3 +267,5 @@ import { resolveBatchMetadata } from './batch_opts.js';
268
267
  export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceModules, discoverUvWorkspaceMembers, discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata };
269
268
  export { getProjectLicense, findLicenseFilePath, identifyLicense, getLicenseDetails, licensesFromReport, normalizeLicensesResponse, runLicenseCheck, getCompatibility } from "./license/index.js";
270
269
  export { generateReport, generateDeduplicationKey } from "./remediation_report.js";
270
+ export { loadConfig, mergeConfig, resolveConfig, CONFIG_FILENAMES } from "./config.js";
271
+ export { runRemediation, findManifests } from "./remediate.js";
package/dist/src/index.js CHANGED
@@ -17,6 +17,8 @@ export { ImageRef } from "./oci_image/images.js";
17
17
  export { getProjectLicense, findLicenseFilePath, identifyLicense, getLicenseDetails, licensesFromReport, normalizeLicensesResponse, runLicenseCheck, getCompatibility } from "./license/index.js";
18
18
  export { extractRemediations } from "./remediation.js";
19
19
  export { generateReport, generateDeduplicationKey } from './remediation_report.js';
20
+ export { loadConfig, mergeConfig, resolveConfig, CONFIG_FILENAMES } from './config.js';
21
+ export { runRemediation, findManifests } from './remediate.js';
20
22
  export default { componentAnalysis, stackAnalysis, stackAnalysisBatch, imageAnalysis, validateToken, generateSbom };
21
23
  export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceModules, discoverUvWorkspaceMembers, discoverWorkspacePackages, discoverWorkspaceCrates, validatePackageJson, resolveWorkspaceDiscoveryIgnore, filterManifestPathsByDiscoveryIgnore, resolveContinueOnError, resolveBatchMetadata, };
22
24
  /**
@@ -50,6 +52,7 @@ export { discoverMavenModules, discoverGradleSubprojects, discoverGoWorkspaceMod
50
52
  * TRUSTIFY_DA_YARN_PATH?: string | undefined,
51
53
  * TRUSTIFY_DA_WORKSPACE_DIR?: string | undefined,
52
54
  * TRUSTIFY_DA_LICENSE_CHECK?: string | undefined,
55
+ * licenseCheck?: boolean | undefined,
53
56
  * MATCH_MANIFEST_VERSIONS?: string | undefined,
54
57
  * TRUSTIFY_DA_SOURCE?: string | undefined,
55
58
  * TRUSTIFY_DA_TOKEN?: string | undefined,
@@ -97,10 +100,8 @@ function readAndPrintVersionFromPackageJson() {
97
100
  logOptionsAndEnvironmentsVariables("trustify-da-javascript-client analysis started, version: ", getPackageVersion());
98
101
  }
99
102
  /**
100
- * This function is used to determine the Trustify DA backend URL.
101
- * The TRUSTIFY_DA_BACKEND_URL is evaluated in the following order and selected when it finds it first:
102
- * 1. Environment Variable
103
- * 2. (key,value) from opts object
103
+ * Selects the Trustify DA backend URL from opts.TRUSTIFY_DA_BACKEND_URL, then environment
104
+ * variable TRUSTIFY_DA_BACKEND_URL. Options take precedence over environment variables.
104
105
  * If TRUSTIFY_DA_BACKEND_URL is not set, the function will throw an error.
105
106
  * @param {{TRUSTIFY_DA_DEBUG?: string | undefined; TRUSTIFY_DA_BACKEND_URL?: string | undefined}} [opts={}]
106
107
  * @return {string} - The selected Trustify DA backend URL
@@ -131,7 +132,7 @@ export function selectTrustifyDABackend(opts = {}) {
131
132
  * @param {string} manifest
132
133
  * @param {false} html
133
134
  * @param {Options} [opts={}]
134
- * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
135
+ * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
135
136
  * @throws {Error}
136
137
  */
137
138
  /**
@@ -140,7 +141,7 @@ export function selectTrustifyDABackend(opts = {}) {
140
141
  * @param {string} manifest - path for the manifest
141
142
  * @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport object.
142
143
  * @param {Options} [opts={}] - optional various options to pass along the application
143
- * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
144
+ * @returns {Promise<string|import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
144
145
  * @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
145
146
  * or backend request failed
146
147
  */
@@ -154,7 +155,7 @@ async function stackAnalysis(manifest, html = false, opts = {}) {
154
155
  * Get component analysis report for a manifest content.
155
156
  * @param {string} manifest - path to the manifest
156
157
  * @param {Options} [opts={}] - optional various options to pass along the application
157
- * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>}
158
+ * @returns {Promise<import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>}
158
159
  * @throws {Error} if no matching provider, failed to get create content, or backend request failed
159
160
  */
160
161
  async function componentAnalysis(manifest, opts = {}) {
@@ -179,7 +180,7 @@ async function componentAnalysis(manifest, opts = {}) {
179
180
  * @param {Array<string>} imageRefs
180
181
  * @param {false} html
181
182
  * @param {Options} [opts={}]
182
- * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
183
+ * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
183
184
  * @throws {Error}
184
185
  */
185
186
  /**
@@ -188,7 +189,7 @@ async function componentAnalysis(manifest, opts = {}) {
188
189
  * @param {Array<string>} imageRefs - OCI image references
189
190
  * @param {boolean} [html=false] - true will return a html string, false will return AnalysisReport
190
191
  * @param {Options} [opts={}] - optional various options to pass along the application
191
- * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
192
+ * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
192
193
  * @throws {Error} if manifest inaccessible, no matching provider, failed to get create content,
193
194
  * or backend request failed
194
195
  */
@@ -197,7 +198,8 @@ async function imageAnalysis(imageRefs, html = false, opts = {}) {
197
198
  return await analysis.requestImages(imageRefs, theUrl, html, opts);
198
199
  }
199
200
  /**
200
- * Max concurrent SBOM generations for batch workspace analysis. Env/opts override default 10.
201
+ * Max concurrent SBOM generations for batch workspace analysis. Options override environment;
202
+ * environment overrides the default of 10.
201
203
  * @param {Options} opts
202
204
  * @returns {number}
203
205
  * @private
@@ -440,7 +442,7 @@ function batchError(message, wantMetadata, metadata) {
440
442
  * @param {string} workspaceRoot
441
443
  * @param {false} html
442
444
  * @param {Options & { batchMetadata: true }} opts
443
- * @returns {Promise<{ analysis: Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
445
+ * @returns {Promise<{ analysis: Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
444
446
  * @throws {Error}
445
447
  */
446
448
  /**
@@ -448,7 +450,7 @@ function batchError(message, wantMetadata, metadata) {
448
450
  * @param {string} workspaceRoot
449
451
  * @param {false} html
450
452
  * @param {Options & { batchMetadata?: false }} [opts={}]
451
- * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>>}
453
+ * @returns {Promise<Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>>}
452
454
  * @throws {Error}
453
455
  */
454
456
  /**
@@ -461,7 +463,7 @@ function batchError(message, wantMetadata, metadata) {
461
463
  * @param {string} workspaceRoot - Path to workspace root (containing lock file and workspace config)
462
464
  * @param {boolean} [html=false] - true returns HTML, false returns JSON report
463
465
  * @param {Options} [opts={}] - `batchConcurrency`, discovery ignores, `continueOnError` (default true), `batchMetadata` (default false)
464
- * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>|{ analysis: string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
466
+ * @returns {Promise<string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>|{ analysis: string|Object.<string, import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport>, metadata: BatchAnalysisMetadata }>}
465
467
  * @throws {Error} if workspace root invalid, no manifests found, no packages pass validation, no SBOMs produced, or backend request failed. When `opts.batchMetadata` is set, `error.batchMetadata` may be set on thrown errors.
466
468
  */
467
469
  async function stackAnalysisBatch(workspaceRoot, html = false, opts = {}) {
@@ -6,10 +6,10 @@
6
6
  * @param {string} manifestPath - path to manifest
7
7
  * @param {string} url - the backend url to send the request to
8
8
  * @param {import('../index.js').Options} [opts={}]
9
- * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport} [analysisResult] - analysis result that includes licenses array from backend
9
+ * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport} [analysisResult] - analysis result that includes licenses array from backend
10
10
  * @returns {Promise<{ projectLicense: { manifest: Object|null, file: Object|null, mismatch: boolean }, incompatibleDependencies: Array<{ purl: string, licenses: string[], category?: string, reason: string }>, error?: string }>}
11
11
  */
12
- export function runLicenseCheck(sbomContent: string, manifestPath: string, url: string, opts?: import("../index.js").Options, analysisResult?: any): Promise<{
12
+ export function runLicenseCheck(sbomContent: string, manifestPath: string, url: string, opts?: import("../index.js").Options, analysisResult?: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport): Promise<{
13
13
  projectLicense: {
14
14
  manifest: Object | null;
15
15
  file: Object | null;
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * License resolution and dependency license compatibility for component analysis.
3
3
  */
4
- import { getProjectLicense, findLicenseFilePath, identifyLicense } from './project_license.js';
5
- import { licensesFromReport, getLicenseDetails } from './licenses_api.js';
6
4
  import { getCompatibility } from './license_utils.js';
5
+ import { licensesFromReport, getLicenseDetails } from './licenses_api.js';
6
+ import { getProjectLicense, findLicenseFilePath, identifyLicense } from './project_license.js';
7
7
  export { getProjectLicense, findLicenseFilePath, identifyLicense } from './project_license.js';
8
8
  export { licensesFromReport, normalizeLicensesResponse, getLicenseDetails } from './licenses_api.js';
9
9
  export { getCompatibility } from './license_utils.js';
@@ -15,7 +15,7 @@ export { getCompatibility } from './license_utils.js';
15
15
  * @param {string} manifestPath - path to manifest
16
16
  * @param {string} url - the backend url to send the request to
17
17
  * @param {import('../index.js').Options} [opts={}]
18
- * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport} [analysisResult] - analysis result that includes licenses array from backend
18
+ * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport} [analysisResult] - analysis result that includes licenses array from backend
19
19
  * @returns {Promise<{ projectLicense: { manifest: Object|null, file: Object|null, mismatch: boolean }, incompatibleDependencies: Array<{ purl: string, licenses: string[], category?: string, reason: string }>, error?: string }>}
20
20
  */
21
21
  export async function runLicenseCheck(sbomContent, manifestPath, url, opts = {}, analysisResult = null) {
@@ -39,10 +39,12 @@ export async function runLicenseCheck(sbomContent, manifestPath, url, opts = {},
39
39
  // Fetch detailed license info from backend (avoid duplicate calls if same license)
40
40
  const licenseDetailsCache = new Map();
41
41
  async function getDetails(spdxId) {
42
- if (!spdxId || !url)
42
+ if (!spdxId || !url) {
43
43
  return null;
44
- if (licenseDetailsCache.has(spdxId))
44
+ }
45
+ if (licenseDetailsCache.has(spdxId)) {
45
46
  return licenseDetailsCache.get(spdxId);
47
+ }
46
48
  try {
47
49
  const details = await getLicenseDetails(spdxId, { ...opts, TRUSTIFY_DA_BACKEND_URL: url });
48
50
  licenseDetailsCache.set(spdxId, details);
@@ -81,8 +83,9 @@ export async function runLicenseCheck(sbomContent, manifestPath, url, opts = {},
81
83
  const incompatibleDependencies = [];
82
84
  for (const purl of purls) {
83
85
  const entry = licenseByPurl.get(purl);
84
- if (!entry)
86
+ if (!entry) {
85
87
  continue;
88
+ }
86
89
  const status = getCompatibility(projectCategory, entry.category);
87
90
  if (status === 'incompatible') {
88
91
  const reason = entry.category?.toUpperCase() === 'UNKNOWN'
@@ -24,11 +24,11 @@ export function normalizeLicensesResponse(data: unknown, purls?: string[]): Map<
24
24
  * Build license map from an analysis report that already includes license data (result.licenses).
25
25
  * Use this when the dependency analysis response already contains the licenses array to avoid a second request.
26
26
  *
27
- * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport} analysisReport - full analysis JSON
27
+ * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport} analysisReport - full analysis JSON
28
28
  * @param {string[]} [purls] - optional list of purls to restrict to
29
29
  * @returns {Map<string, { licenses: string[], category?: string }>}
30
30
  */
31
- export function licensesFromReport(analysisReport: any, purls?: string[]): Map<string, {
31
+ export function licensesFromReport(analysisReport: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts").AnalysisReport, purls?: string[]): Map<string, {
32
32
  licenses: string[];
33
33
  category?: string;
34
34
  }>;
@@ -86,7 +86,7 @@ export function normalizeLicensesResponse(data, purls = []) {
86
86
  * Build license map from an analysis report that already includes license data (result.licenses).
87
87
  * Use this when the dependency analysis response already contains the licenses array to avoid a second request.
88
88
  *
89
- * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport').AnalysisReport} analysisReport - full analysis JSON
89
+ * @param {import('@trustify-da/trustify-da-api-model/model/v5/AnalysisReport.ts').AnalysisReport} analysisReport - full analysis JSON
90
90
  * @param {string[]} [purls] - optional list of purls to restrict to
91
91
  * @returns {Map<string, { licenses: string[], category?: string }>}
92
92
  */
@@ -532,7 +532,7 @@ export default class Base_javascript {
532
532
  }
533
533
  }
534
534
  // Try to find the command in the following order:
535
- // 1. Custom path from environment/opts (via getCustomPath)
535
+ // 1. Custom path from opts/environment (via getCustomPath)
536
536
  // 2. Local node_modules/.bin
537
537
  // 3. Global installation
538
538
  let cmd = this.#cmd;
@@ -280,7 +280,7 @@ function parseCargoLockHashes(manifestDir, opts = {}) {
280
280
  }
281
281
  }
282
282
  catch (error) {
283
- console.warn('Failed to parse Cargo.lock for hashes, SBOM will be generated without hashes');
283
+ console.warn(`Failed to parse Cargo.lock at ${lockPath} for hashes: ${error.message}. SBOM will be generated without hashes.`);
284
284
  }
285
285
  return hashMap;
286
286
  }
@@ -441,7 +441,7 @@ function addTransitiveDeps(sbom, metadata, packageId, ignoredDeps, visited, hash
441
441
  let depPurl = depPackage.source == null
442
442
  ? toPathDepPurl(depPackage.name, depPackage.version)
443
443
  : toPurl(depPackage.name, depPackage.version);
444
- let hashes = hashMap ? hashMap.get(`${depPackage.name}@${depPackage.version}`) : undefined;
444
+ let hashes = hashMap.get(`${depPackage.name}@${depPackage.version}`);
445
445
  sbom.addDependency(sourcePurl, depPurl, undefined, hashes);
446
446
  addTransitiveDeps(sbom, metadata, depId, ignoredDeps, visited, hashMap);
447
447
  }
@@ -473,7 +473,7 @@ function addDirectDeps(sbom, metadata, packageId, parentPurl, ignoredDeps, hashM
473
473
  let depPurl = depPackage.source == null
474
474
  ? toPathDepPurl(depPackage.name, depPackage.version)
475
475
  : toPurl(depPackage.name, depPackage.version);
476
- let hashes = hashMap ? hashMap.get(`${depPackage.name}@${depPackage.version}`) : undefined;
476
+ let hashes = hashMap.get(`${depPackage.name}@${depPackage.version}`);
477
477
  sbom.addDependency(parentPurl, depPurl, undefined, hashes);
478
478
  }
479
479
  }