@pnpm/deps.compliance.commands 1101.0.1 → 1101.1.0
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/lib/audit/audit.d.ts +1 -0
- package/lib/audit/audit.js +106 -13
- package/lib/audit/fix.d.ts +1 -0
- package/lib/audit/fix.js +9 -1
- package/lib/audit/getAuditFixChoices.d.ts +14 -0
- package/lib/audit/getAuditFixChoices.js +116 -0
- package/lib/sbom/sbom.js +9 -1
- package/package.json +26 -19
package/lib/audit/audit.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare function help(): string;
|
|
|
12
12
|
export type AuditOptions = Pick<UniversalOptions, 'dir'> & {
|
|
13
13
|
fix?: boolean | 'override' | 'update';
|
|
14
14
|
ignoreRegistryErrors?: boolean;
|
|
15
|
+
interactive?: boolean;
|
|
15
16
|
json?: boolean;
|
|
16
17
|
lockfileDir?: string;
|
|
17
18
|
registries: Registries;
|
package/lib/audit/audit.js
CHANGED
|
@@ -5,13 +5,16 @@ import { audit, normalizeGhsaId } from '@pnpm/deps.compliance.audit';
|
|
|
5
5
|
import { PnpmError } from '@pnpm/error';
|
|
6
6
|
import { update } from '@pnpm/installing.commands';
|
|
7
7
|
import { readEnvLockfile, readWantedLockfile } from '@pnpm/lockfile.fs';
|
|
8
|
+
import { globalInfo } from '@pnpm/logger';
|
|
8
9
|
import { createGetAuthHeaderByURI } from '@pnpm/network.auth-header';
|
|
9
10
|
import { table } from '@zkochan/table';
|
|
10
11
|
import chalk, {} from 'chalk';
|
|
12
|
+
import enquirer from 'enquirer';
|
|
11
13
|
import { pick, pickBy } from 'ramda';
|
|
12
14
|
import { renderHelp } from 'render-help';
|
|
13
15
|
import { fix } from './fix.js';
|
|
14
16
|
import { fixWithUpdate } from './fixWithUpdate.js';
|
|
17
|
+
import { getAuditFixChoices } from './getAuditFixChoices.js';
|
|
15
18
|
import { ignore } from './ignore.js';
|
|
16
19
|
const AUDIT_LEVEL_NUMBER = {
|
|
17
20
|
info: 0,
|
|
@@ -64,6 +67,7 @@ export function cliOptionsTypes() {
|
|
|
64
67
|
'workspace',
|
|
65
68
|
], update.cliOptionsTypes()),
|
|
66
69
|
...rcOptionsTypes(),
|
|
70
|
+
interactive: Boolean,
|
|
67
71
|
};
|
|
68
72
|
}
|
|
69
73
|
export const shorthands = {
|
|
@@ -117,6 +121,11 @@ export function help() {
|
|
|
117
121
|
description: 'Ignore all vulnerabilities for which no fix exists',
|
|
118
122
|
name: '--ignore-unfixable',
|
|
119
123
|
},
|
|
124
|
+
{
|
|
125
|
+
description: 'Show vulnerabilities and select which ones to fix interactively',
|
|
126
|
+
name: '--interactive',
|
|
127
|
+
shortAlias: '-i',
|
|
128
|
+
},
|
|
120
129
|
],
|
|
121
130
|
},
|
|
122
131
|
],
|
|
@@ -178,7 +187,7 @@ export async function handler(opts) {
|
|
|
178
187
|
if (opts.fix === 'update' || opts.fix === 'override') {
|
|
179
188
|
fixMethod = opts.fix;
|
|
180
189
|
}
|
|
181
|
-
else if (opts.fix === true) {
|
|
190
|
+
else if (opts.fix === true || (opts.interactive && !opts.fix)) {
|
|
182
191
|
fixMethod = DEFAULT_FIX_METHOD;
|
|
183
192
|
}
|
|
184
193
|
else if (!opts.fix) {
|
|
@@ -187,19 +196,29 @@ export async function handler(opts) {
|
|
|
187
196
|
else {
|
|
188
197
|
throw new PnpmError('INVALID_FIX_OPTION', `Invalid value for --fix: ${opts.fix}. Should be one of "override" or "update"`);
|
|
189
198
|
}
|
|
190
|
-
if (fixMethod
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
exitCode: result.remaining.length > 0 ? 1 : 0,
|
|
198
|
-
output,
|
|
199
|
+
if (fixMethod != null) {
|
|
200
|
+
// Pre-filter by auditLevel and ignoreGhsas so the interactive prompt
|
|
201
|
+
// and the update-method path see the same set of advisories that
|
|
202
|
+
// fix.ts's getFixableAdvisories filters for the override path.
|
|
203
|
+
let filteredAuditReport = {
|
|
204
|
+
...auditReport,
|
|
205
|
+
advisories: filterAdvisoriesForFix(auditReport.advisories, opts),
|
|
199
206
|
};
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
207
|
+
if (opts.interactive) {
|
|
208
|
+
filteredAuditReport = await interactiveAuditFix(filteredAuditReport);
|
|
209
|
+
}
|
|
210
|
+
if (fixMethod === 'update') {
|
|
211
|
+
const result = await fixWithUpdate(filteredAuditReport, { ...opts, include });
|
|
212
|
+
let output = formatFixWithUpdateOutput(result, filteredAuditReport);
|
|
213
|
+
if (result.addedAgeExcludes.length > 0) {
|
|
214
|
+
output += `\n${result.addedAgeExcludes.length} entries were added to minimumReleaseAgeExclude to allow installing the patched versions:\n${result.addedAgeExcludes.join('\n')}\n`;
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
exitCode: result.remaining.length > 0 ? 1 : 0,
|
|
218
|
+
output,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
const { vulnOverrides, addedAgeExcludes } = await fix(filteredAuditReport, opts);
|
|
203
222
|
if (Object.values(vulnOverrides).length === 0) {
|
|
204
223
|
return {
|
|
205
224
|
exitCode: 0,
|
|
@@ -350,4 +369,78 @@ export function formatFixWithUpdateOutput(result, auditReport) {
|
|
|
350
369
|
output.push('');
|
|
351
370
|
return output.join('\n');
|
|
352
371
|
}
|
|
372
|
+
function filterAdvisoriesForFix(advisories, opts) {
|
|
373
|
+
const auditLevel = AUDIT_LEVEL_NUMBER[opts.auditLevel ?? 'low'];
|
|
374
|
+
const ignoreGhsas = opts.auditConfig?.ignoreGhsas;
|
|
375
|
+
const ignoreGhsaSet = ignoreGhsas?.length ? new Set(ignoreGhsas.map(normalizeGhsaId)) : undefined;
|
|
376
|
+
return Object.fromEntries(Object.entries(advisories).filter(([, { severity, github_advisory_id: ghsaId }]) => {
|
|
377
|
+
if (AUDIT_LEVEL_NUMBER[severity] < auditLevel)
|
|
378
|
+
return false;
|
|
379
|
+
if (ignoreGhsaSet && ghsaId && ignoreGhsaSet.has(normalizeGhsaId(ghsaId)))
|
|
380
|
+
return false;
|
|
381
|
+
return true;
|
|
382
|
+
}));
|
|
383
|
+
}
|
|
384
|
+
async function interactiveAuditFix(auditReport) {
|
|
385
|
+
const choices = getAuditFixChoices(Object.values(auditReport.advisories));
|
|
386
|
+
if (choices.length === 0) {
|
|
387
|
+
return auditReport;
|
|
388
|
+
}
|
|
389
|
+
const { selectedVulnerabilities } = await enquirer.prompt({
|
|
390
|
+
choices,
|
|
391
|
+
footer: '\nEnter to start fixing. Ctrl-c to cancel.',
|
|
392
|
+
indicator(state, choice) {
|
|
393
|
+
return ` ${choice.enabled ? '●' : '○'}`;
|
|
394
|
+
},
|
|
395
|
+
message: 'Choose which vulnerabilities to fix ' +
|
|
396
|
+
`(Press ${chalk.cyan('<space>')} to select, ` +
|
|
397
|
+
`${chalk.cyan('<a>')} to toggle all, ` +
|
|
398
|
+
`${chalk.cyan('<i>')} to invert selection)`,
|
|
399
|
+
name: 'selectedVulnerabilities',
|
|
400
|
+
pointer: '❯',
|
|
401
|
+
result() {
|
|
402
|
+
return this.selected;
|
|
403
|
+
},
|
|
404
|
+
format() {
|
|
405
|
+
if (!this.state.submitted || this.state.cancelled)
|
|
406
|
+
return '';
|
|
407
|
+
if (Array.isArray(this.selected)) {
|
|
408
|
+
return this.selected
|
|
409
|
+
.filter((choice) => !/^\[.+\]$/.test(choice.name))
|
|
410
|
+
.map((choice) => this.styles.primary(choice.name)).join(', ');
|
|
411
|
+
}
|
|
412
|
+
return this.styles.primary(this.selected.name);
|
|
413
|
+
},
|
|
414
|
+
styles: {
|
|
415
|
+
dark: chalk.reset,
|
|
416
|
+
em: chalk.bgBlack.whiteBright,
|
|
417
|
+
success: chalk.reset,
|
|
418
|
+
},
|
|
419
|
+
type: 'multiselect',
|
|
420
|
+
validate(value) {
|
|
421
|
+
if (value.length === 0) {
|
|
422
|
+
return 'You must choose at least one vulnerability.';
|
|
423
|
+
}
|
|
424
|
+
return true;
|
|
425
|
+
},
|
|
426
|
+
j() {
|
|
427
|
+
return this.down();
|
|
428
|
+
},
|
|
429
|
+
k() {
|
|
430
|
+
return this.up();
|
|
431
|
+
},
|
|
432
|
+
cancel() {
|
|
433
|
+
// By default, canceling the prompt via Ctrl+c throws an empty string.
|
|
434
|
+
// The custom cancel function prevents that behavior.
|
|
435
|
+
// Otherwise, pnpm CLI would print an error and confuse users.
|
|
436
|
+
// See related issue: https://github.com/enquirer/enquirer/issues/225
|
|
437
|
+
globalInfo('Audit fix canceled');
|
|
438
|
+
process.exit(0);
|
|
439
|
+
},
|
|
440
|
+
}); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
441
|
+
const selectedKeys = new Set(selectedVulnerabilities.map((c) => c.value));
|
|
442
|
+
const selectedAdvisories = Object.fromEntries(Object.entries(auditReport.advisories)
|
|
443
|
+
.filter(([, advisory]) => selectedKeys.has(`${advisory.module_name}@${advisory.vulnerable_versions}`)));
|
|
444
|
+
return { ...auditReport, advisories: selectedAdvisories };
|
|
445
|
+
}
|
|
353
446
|
//# sourceMappingURL=audit.js.map
|
package/lib/audit/fix.d.ts
CHANGED
|
@@ -5,4 +5,5 @@ export interface FixResult {
|
|
|
5
5
|
addedAgeExcludes: string[];
|
|
6
6
|
}
|
|
7
7
|
export declare function fix(auditReport: AuditReport, opts: AuditOptions): Promise<FixResult>;
|
|
8
|
+
export declare function caretRangeForPatched(patchedRange: string): string;
|
|
8
9
|
export declare function createMinimumReleaseAgeExcludes(advisories: AuditAdvisory[]): string[];
|
package/lib/audit/fix.js
CHANGED
|
@@ -32,10 +32,18 @@ function createOverrides(advisories) {
|
|
|
32
32
|
for (const advisory of advisories) {
|
|
33
33
|
if (!advisory.patched_versions)
|
|
34
34
|
continue;
|
|
35
|
-
entries.push([`${advisory.module_name}@${advisory.vulnerable_versions}`, advisory.patched_versions]);
|
|
35
|
+
entries.push([`${advisory.module_name}@${advisory.vulnerable_versions}`, caretRangeForPatched(advisory.patched_versions)]);
|
|
36
36
|
}
|
|
37
37
|
return Object.fromEntries(entries);
|
|
38
38
|
}
|
|
39
|
+
// Use the minimum patched version with a caret so pnpm stays within the
|
|
40
|
+
// same major as the fix. `>=X.Y.Z` alone can silently promote a dep to a
|
|
41
|
+
// later breaking major; `^X.Y.Z` still satisfies the patch while
|
|
42
|
+
// preserving the major the user originally pinned to.
|
|
43
|
+
export function caretRangeForPatched(patchedRange) {
|
|
44
|
+
const min = semver.minVersion(patchedRange);
|
|
45
|
+
return min ? `^${min.version}` : patchedRange;
|
|
46
|
+
}
|
|
39
47
|
export function createMinimumReleaseAgeExcludes(advisories) {
|
|
40
48
|
const excludes = new Set();
|
|
41
49
|
for (const advisory of advisories) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AuditAdvisory } from '@pnpm/deps.compliance.audit';
|
|
2
|
+
export interface AuditChoiceRow {
|
|
3
|
+
name: string;
|
|
4
|
+
value: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
type AuditChoiceGroup = Array<{
|
|
8
|
+
name: string;
|
|
9
|
+
message: string;
|
|
10
|
+
choices: AuditChoiceRow[];
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function getAuditFixChoices(advisories: AuditAdvisory[]): AuditChoiceGroup;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { getBorderCharacters, table } from '@zkochan/table';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { groupBy } from 'ramda';
|
|
4
|
+
import { caretRangeForPatched } from './fix.js';
|
|
5
|
+
const AUDIT_COLOR = {
|
|
6
|
+
info: chalk.dim,
|
|
7
|
+
low: chalk.bold,
|
|
8
|
+
moderate: chalk.bold.yellow,
|
|
9
|
+
high: chalk.bold.red,
|
|
10
|
+
critical: chalk.bold.red,
|
|
11
|
+
};
|
|
12
|
+
const SEVERITY_ORDER = ['critical', 'high', 'moderate', 'low', 'info'];
|
|
13
|
+
const SEVERITY_RANK = {
|
|
14
|
+
info: 0,
|
|
15
|
+
low: 1,
|
|
16
|
+
moderate: 2,
|
|
17
|
+
high: 3,
|
|
18
|
+
critical: 4,
|
|
19
|
+
};
|
|
20
|
+
const COLUMN_HEADER = ['Package', 'Vulnerable', 'Patched', 'Advisories'];
|
|
21
|
+
export function getAuditFixChoices(advisories) {
|
|
22
|
+
if (advisories.length === 0) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
const fixable = advisories.filter(({ patched_versions: p }) => p != null);
|
|
26
|
+
if (fixable.length === 0) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
// Collapse advisories that share module_name@vulnerable_versions: they
|
|
30
|
+
// produce the same override, so showing them as separate rows would
|
|
31
|
+
// duplicate every choice. Titles are joined; the highest severity wins.
|
|
32
|
+
const deduped = dedupeByFixKey(fixable);
|
|
33
|
+
const grouped = groupBy((a) => a.severity, deduped);
|
|
34
|
+
const finalChoices = [];
|
|
35
|
+
for (const severity of SEVERITY_ORDER) {
|
|
36
|
+
const groupAdvisories = grouped[severity];
|
|
37
|
+
if (!groupAdvisories?.length)
|
|
38
|
+
continue;
|
|
39
|
+
const rows = [
|
|
40
|
+
{ raw: COLUMN_HEADER, key: '', disabled: true },
|
|
41
|
+
];
|
|
42
|
+
for (const advisory of groupAdvisories) {
|
|
43
|
+
const key = `${advisory.module_name}@${advisory.vulnerable_versions}`;
|
|
44
|
+
rows.push({
|
|
45
|
+
raw: [
|
|
46
|
+
advisory.module_name,
|
|
47
|
+
advisory.vulnerable_versions,
|
|
48
|
+
advisory.patched_versions ? caretRangeForPatched(advisory.patched_versions) : '',
|
|
49
|
+
advisory.github_advisory_id ?? '',
|
|
50
|
+
],
|
|
51
|
+
key,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const rendered = alignColumns(rows.map(r => r.raw));
|
|
55
|
+
const choices = rows.map((row, i) => {
|
|
56
|
+
if (i === 0) {
|
|
57
|
+
return {
|
|
58
|
+
name: rendered[i],
|
|
59
|
+
value: '',
|
|
60
|
+
disabled: true,
|
|
61
|
+
hint: '',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
name: row.key,
|
|
66
|
+
message: rendered[i],
|
|
67
|
+
value: row.key,
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
finalChoices.push({
|
|
71
|
+
name: `[${severity}]`,
|
|
72
|
+
choices,
|
|
73
|
+
message: AUDIT_COLOR[severity](severity),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return finalChoices;
|
|
77
|
+
}
|
|
78
|
+
function alignColumns(rows) {
|
|
79
|
+
// No per-column width / truncate: each cell is short (package name,
|
|
80
|
+
// semver range, one or two GHSA IDs), so the table library can
|
|
81
|
+
// auto-size columns and still keep each row on a single line. Keeping
|
|
82
|
+
// rows 1:1 with rendered lines is required because the caller picks
|
|
83
|
+
// `rendered[i]` for choice `i`.
|
|
84
|
+
return table(rows, {
|
|
85
|
+
border: getBorderCharacters('void'),
|
|
86
|
+
columnDefault: {
|
|
87
|
+
paddingLeft: 0,
|
|
88
|
+
paddingRight: 2,
|
|
89
|
+
},
|
|
90
|
+
drawHorizontalLine: () => false,
|
|
91
|
+
}).split('\n').filter((line) => line.trim() !== '');
|
|
92
|
+
}
|
|
93
|
+
function dedupeByFixKey(advisories) {
|
|
94
|
+
const byKey = new Map();
|
|
95
|
+
for (const advisory of advisories) {
|
|
96
|
+
const key = `${advisory.module_name}@${advisory.vulnerable_versions}`;
|
|
97
|
+
const existing = byKey.get(key);
|
|
98
|
+
if (!existing) {
|
|
99
|
+
byKey.set(key, advisory);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const keepSeverity = SEVERITY_RANK[advisory.severity] > SEVERITY_RANK[existing.severity]
|
|
103
|
+
? advisory.severity
|
|
104
|
+
: existing.severity;
|
|
105
|
+
const mergedId = existing.github_advisory_id && advisory.github_advisory_id && existing.github_advisory_id !== advisory.github_advisory_id
|
|
106
|
+
? `${existing.github_advisory_id}, ${advisory.github_advisory_id}`
|
|
107
|
+
: existing.github_advisory_id || advisory.github_advisory_id;
|
|
108
|
+
byKey.set(key, {
|
|
109
|
+
...existing,
|
|
110
|
+
severity: keepSeverity,
|
|
111
|
+
github_advisory_id: mergedId,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return Array.from(byKey.values());
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=getAuditFixChoices.js.map
|
package/lib/sbom/sbom.js
CHANGED
|
@@ -3,6 +3,7 @@ import { packageManager } from '@pnpm/cli.meta';
|
|
|
3
3
|
import { docsUrl, readProjectManifestOnly } from '@pnpm/cli.utils';
|
|
4
4
|
import { types as allTypes } from '@pnpm/config.reader';
|
|
5
5
|
import { WANTED_LOCKFILE } from '@pnpm/constants';
|
|
6
|
+
import { isSpdxLicenseExpression, resolveLicenseFromDir } from '@pnpm/deps.compliance.license-resolver';
|
|
6
7
|
import { collectSbomComponents, serializeCycloneDx, serializeSpdx, } from '@pnpm/deps.compliance.sbom';
|
|
7
8
|
import { PnpmError } from '@pnpm/error';
|
|
8
9
|
import { getLockfileImporterId, readWantedLockfile } from '@pnpm/lockfile.fs';
|
|
@@ -103,7 +104,14 @@ export async function handler(opts, _params = []) {
|
|
|
103
104
|
const manifest = await readProjectManifestOnly(opts.dir);
|
|
104
105
|
const rootName = manifest.name ?? 'unknown';
|
|
105
106
|
const rootVersion = manifest.version ?? '0.0.0';
|
|
106
|
-
|
|
107
|
+
// Keep the root in sync with transitive deps: consult manifest `license` /
|
|
108
|
+
// legacy `licenses` first, then fall back to an on-disk LICENSE file. Drop
|
|
109
|
+
// file-scanned values that aren't SPDX-valid to avoid non-compliant output.
|
|
110
|
+
const rootLicenseInfo = await resolveLicenseFromDir({ manifest, dir: opts.dir });
|
|
111
|
+
const rootLicense = rootLicenseInfo && rootLicenseInfo.name !== 'Unknown' &&
|
|
112
|
+
(!rootLicenseInfo.licenseFile || isSpdxLicenseExpression(rootLicenseInfo.name))
|
|
113
|
+
? rootLicenseInfo.name
|
|
114
|
+
: undefined;
|
|
107
115
|
const rootAuthor = typeof manifest.author === 'string'
|
|
108
116
|
? manifest.author
|
|
109
117
|
: manifest.author?.name;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/deps.compliance.commands",
|
|
3
|
-
"version": "1101.0
|
|
3
|
+
"version": "1101.1.0",
|
|
4
4
|
"description": "pnpm commands for audit, licenses, and sbom",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -29,32 +29,38 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@zkochan/table": "^2.0.1",
|
|
31
31
|
"chalk": "^5.6.0",
|
|
32
|
+
"enquirer": "^2.4.1",
|
|
32
33
|
"memoize": "^10.2.0",
|
|
33
34
|
"ramda": "npm:@pnpm/ramda@0.28.1",
|
|
34
35
|
"render-help": "^2.0.0",
|
|
35
36
|
"semver": "^7.7.2",
|
|
36
|
-
"@pnpm/cli.common-cli-options-help": "1100.0.0",
|
|
37
|
-
"@pnpm/cli.utils": "1101.0.0",
|
|
38
37
|
"@pnpm/cli.command": "1100.0.0",
|
|
38
|
+
"@pnpm/config.writer": "1100.0.2",
|
|
39
|
+
"@pnpm/config.reader": "1101.1.0",
|
|
39
40
|
"@pnpm/cli.meta": "1100.0.1",
|
|
40
|
-
"@pnpm/
|
|
41
|
+
"@pnpm/cli.common-cli-options-help": "1100.0.0",
|
|
42
|
+
"@pnpm/deps.compliance.audit": "1101.0.1",
|
|
41
43
|
"@pnpm/constants": "1100.0.0",
|
|
42
|
-
"@pnpm/config.writer": "1100.0.1",
|
|
43
|
-
"@pnpm/deps.compliance.license-scanner": "1100.0.2",
|
|
44
44
|
"@pnpm/error": "1100.0.0",
|
|
45
|
-
"@pnpm/
|
|
46
|
-
"@pnpm/deps.compliance.
|
|
47
|
-
"@pnpm/
|
|
48
|
-
"@pnpm/
|
|
49
|
-
"@pnpm/lockfile.
|
|
45
|
+
"@pnpm/deps.compliance.license-resolver": "1100.0.0",
|
|
46
|
+
"@pnpm/deps.compliance.license-scanner": "1100.0.3",
|
|
47
|
+
"@pnpm/cli.utils": "1101.0.0",
|
|
48
|
+
"@pnpm/installing.commands": "1100.1.1",
|
|
49
|
+
"@pnpm/lockfile.fs": "1100.0.2",
|
|
50
|
+
"@pnpm/lockfile.types": "1100.0.2",
|
|
51
|
+
"@pnpm/deps.compliance.sbom": "1100.0.3",
|
|
52
|
+
"@pnpm/lockfile.utils": "1100.0.2",
|
|
53
|
+
"@pnpm/lockfile.walker": "1100.0.2",
|
|
50
54
|
"@pnpm/network.auth-header": "1100.0.1",
|
|
51
|
-
"@pnpm/lockfile.types": "1100.0.1",
|
|
52
|
-
"@pnpm/store.path": "1100.0.0",
|
|
53
55
|
"@pnpm/types": "1101.0.0",
|
|
54
56
|
"@pnpm/workspace.project-manifest-reader": "1100.0.2",
|
|
55
|
-
"@pnpm/
|
|
57
|
+
"@pnpm/store.path": "1100.0.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"@pnpm/logger": ">=1001.0.0 <1002.0.0"
|
|
56
61
|
},
|
|
57
62
|
"devDependencies": {
|
|
63
|
+
"@jest/globals": "30.3.0",
|
|
58
64
|
"@pnpm/registry-mock": "6.0.0",
|
|
59
65
|
"@types/ramda": "0.31.1",
|
|
60
66
|
"@types/semver": "7.7.1",
|
|
@@ -63,13 +69,14 @@
|
|
|
63
69
|
"nock": "13.3.4",
|
|
64
70
|
"read-yaml-file": "^3.0.0",
|
|
65
71
|
"tempy": "3.0.0",
|
|
66
|
-
"@pnpm/
|
|
72
|
+
"@pnpm/test-fixtures": "1100.0.0",
|
|
73
|
+
"@pnpm/logger": "1100.0.0",
|
|
74
|
+
"@pnpm/testing.command-defaults": "1100.0.1",
|
|
67
75
|
"@pnpm/pkg-manifest.reader": "1100.0.1",
|
|
68
|
-
"@pnpm/prepare": "1100.0.1",
|
|
69
76
|
"@pnpm/testing.mock-agent": "1100.0.1",
|
|
70
|
-
"@pnpm/
|
|
71
|
-
"@pnpm/
|
|
72
|
-
"@pnpm/
|
|
77
|
+
"@pnpm/workspace.projects-filter": "1100.0.3",
|
|
78
|
+
"@pnpm/prepare": "1100.0.2",
|
|
79
|
+
"@pnpm/deps.compliance.commands": "1101.1.0"
|
|
73
80
|
},
|
|
74
81
|
"engines": {
|
|
75
82
|
"node": ">=22.13"
|