@vltpkg/cli-sdk 1.3.6 → 1.3.8
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/dist/commands/exec.js +8 -0
- package/dist/commands/outdated.d.ts +91 -0
- package/dist/commands/outdated.js +554 -0
- package/dist/commands/update.js +10 -1
- package/dist/commands/view.js +63 -41
- package/dist/config/definition.d.ts +1 -0
- package/dist/config/definition.js +1 -0
- package/dist/custom-help.js +4 -0
- package/dist/query-auth.d.ts +2 -1
- package/dist/query-auth.js +7 -3
- package/dist/stub-node.d.ts +8 -0
- package/dist/stub-node.js +28 -0
- package/package.json +28 -26
package/dist/commands/exec.js
CHANGED
|
@@ -44,6 +44,11 @@ export const usage = () => commandUsage({
|
|
|
44
44
|
If the appropriate excutable cannot be determined, then
|
|
45
45
|
an error will be raised.
|
|
46
46
|
|
|
47
|
+
If the package is a path to a local directory (eg, \`.\`
|
|
48
|
+
or \`../pkg\`, relative to the current directory), its
|
|
49
|
+
bins are run in place. Nothing is installed, and its own
|
|
50
|
+
\`node_modules\` is used as-is.
|
|
51
|
+
|
|
47
52
|
At no point will \`vlt exec\` change the locally installed
|
|
48
53
|
dependencies. Any installs it performs is done in vlt's XDG
|
|
49
54
|
data directory.
|
|
@@ -58,6 +63,9 @@ export const usage = () => commandUsage({
|
|
|
58
63
|
'eslint@9.24 src/file.js': {
|
|
59
64
|
description: 'Run the default bin provided by eslint version 9.24',
|
|
60
65
|
},
|
|
66
|
+
'.': {
|
|
67
|
+
description: 'Run the default bin of the package in the current directory',
|
|
68
|
+
},
|
|
61
69
|
'create-react-app --call="echo $PWD"': {
|
|
62
70
|
description: 'Install create-react-app and run an arbitrary command with its bins in PATH',
|
|
63
71
|
},
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { PackageAlert } from '@vltpkg/security-archive';
|
|
2
|
+
import type { DependencyTypeShort } from '@vltpkg/types';
|
|
3
|
+
import type { CommandFn, CommandUsage } from '../index.ts';
|
|
4
|
+
export declare const needsRegistry = true;
|
|
5
|
+
export declare const usage: CommandUsage;
|
|
6
|
+
/** How far the newest available version is from the installed one. */
|
|
7
|
+
export type OutdatedKind = 'missing' | 'major' | 'minor' | 'patch' | 'prerelease';
|
|
8
|
+
/** What the security archive knows about one version of a package. */
|
|
9
|
+
export type VersionSecurity = {
|
|
10
|
+
/** The overall security score, from 0 to 100 */
|
|
11
|
+
score: number;
|
|
12
|
+
alerts: PackageAlert[];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* A transitive dependent whose declared range does not admit the latest
|
|
16
|
+
* version. Importers are left out: their ranges are the user's to
|
|
17
|
+
* change, and the `action` field says how.
|
|
18
|
+
*/
|
|
19
|
+
export type HeldBy = {
|
|
20
|
+
dependent: string;
|
|
21
|
+
spec: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* One dependency that is missing, behind its wanted version, or behind
|
|
25
|
+
* the latest published version.
|
|
26
|
+
*/
|
|
27
|
+
export type OutdatedEntry = {
|
|
28
|
+
/** The dependency name as it appears in the dependent's manifest */
|
|
29
|
+
name: string;
|
|
30
|
+
/** The declared spec, for example `^1.2.3` */
|
|
31
|
+
spec: string;
|
|
32
|
+
/** The dependency type in the dependent's manifest */
|
|
33
|
+
type: DependencyTypeShort;
|
|
34
|
+
/** The installed version, absent when the dependency is missing */
|
|
35
|
+
current?: string;
|
|
36
|
+
/**
|
|
37
|
+
* The highest published version that satisfies the declared spec,
|
|
38
|
+
* i.e. the version that `vlt install` would pick. Absent when no
|
|
39
|
+
* published version satisfies the spec.
|
|
40
|
+
*/
|
|
41
|
+
wanted?: string;
|
|
42
|
+
/** The version the registry points its `latest` dist-tag at */
|
|
43
|
+
latest?: string;
|
|
44
|
+
/**
|
|
45
|
+
* The package depending on it: the name of an importer, or
|
|
46
|
+
* `name@version` of a transitive dependent.
|
|
47
|
+
*/
|
|
48
|
+
dependent: string;
|
|
49
|
+
/** The dependent's location relative to the project root */
|
|
50
|
+
location: string;
|
|
51
|
+
/** How far latest (or wanted, when latest is not ahead) is from current */
|
|
52
|
+
kind: OutdatedKind;
|
|
53
|
+
/**
|
|
54
|
+
* True when a newer version satisfies the declared range, so that
|
|
55
|
+
* `vlt update` picks it up without a manifest change.
|
|
56
|
+
*/
|
|
57
|
+
inRange: boolean;
|
|
58
|
+
/** The deprecation message of the installed version, when deprecated */
|
|
59
|
+
deprecated?: string;
|
|
60
|
+
/** Other dependents whose ranges do not admit the latest version */
|
|
61
|
+
heldBy?: HeldBy[];
|
|
62
|
+
/** Requirements of the latest version that this project does not meet */
|
|
63
|
+
requires?: {
|
|
64
|
+
/** The `engines.node` range of latest, when the current node fails it */
|
|
65
|
+
node?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Peer dependency ranges of latest that the dependent's installed
|
|
68
|
+
* peers do not satisfy, by peer name.
|
|
69
|
+
*/
|
|
70
|
+
peers?: Record<string, string>;
|
|
71
|
+
};
|
|
72
|
+
/** Security data for each version the archive has a report for */
|
|
73
|
+
security?: {
|
|
74
|
+
current?: VersionSecurity;
|
|
75
|
+
wanted?: VersionSecurity;
|
|
76
|
+
latest?: VersionSecurity;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* How to upgrade: a `vlt` command for dependencies of an importer,
|
|
80
|
+
* or the catalog edit to make. Absent for transitive dependents,
|
|
81
|
+
* where the upgrade belongs to the dependent's own release.
|
|
82
|
+
*/
|
|
83
|
+
action?: string;
|
|
84
|
+
};
|
|
85
|
+
export type OutdatedResult = OutdatedEntry[];
|
|
86
|
+
export declare const views: {
|
|
87
|
+
readonly json: (result: OutdatedResult) => OutdatedResult;
|
|
88
|
+
readonly count: (result: OutdatedResult) => number;
|
|
89
|
+
readonly human: (result: OutdatedResult) => string;
|
|
90
|
+
};
|
|
91
|
+
export declare const command: CommandFn<OutdatedResult>;
|
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import { actual, Edge, GraphModifier } from '@vltpkg/graph';
|
|
2
|
+
import { PackageInfoClient } from '@vltpkg/package-info';
|
|
3
|
+
import { pickManifest } from '@vltpkg/pick-manifest';
|
|
4
|
+
import { Query } from '@vltpkg/query';
|
|
5
|
+
import { SecurityArchive } from '@vltpkg/security-archive';
|
|
6
|
+
import { gt, parse, rsort, satisfies, stable } from '@vltpkg/semver';
|
|
7
|
+
import { commandUsage } from "../config/usage.js";
|
|
8
|
+
import { assertVltInstalled } from "../is-vlt-installed.js";
|
|
9
|
+
import { createGetAuthHeader } from "../query-auth.js";
|
|
10
|
+
import { createHostContextsMap } from "../query-host-contexts.js";
|
|
11
|
+
import { createStubNode } from "../stub-node.js";
|
|
12
|
+
export const needsRegistry = true;
|
|
13
|
+
export const usage = () => commandUsage({
|
|
14
|
+
command: 'outdated',
|
|
15
|
+
usage: [
|
|
16
|
+
'',
|
|
17
|
+
'[package-names...]',
|
|
18
|
+
'[--target=<query>] [--workspace=<path>]',
|
|
19
|
+
'[--view=human | json | count]',
|
|
20
|
+
],
|
|
21
|
+
description: `List dependencies that have newer versions available, and
|
|
22
|
+
what is keeping them from being upgraded.
|
|
23
|
+
|
|
24
|
+
For every registry dependency, compare the installed version
|
|
25
|
+
against the highest version that satisfies the declared range
|
|
26
|
+
(wanted) and against the registry's \`latest\` dist-tag (latest).
|
|
27
|
+
Only dependencies that are missing or behind on either count are
|
|
28
|
+
reported.
|
|
29
|
+
|
|
30
|
+
Each report goes beyond the version numbers:
|
|
31
|
+
|
|
32
|
+
- the size of the jump (major, minor, patch)
|
|
33
|
+
- whether the installed version is deprecated
|
|
34
|
+
- security scores and alerts for the installed, wanted and latest
|
|
35
|
+
versions, so an upgrade that fixes a vulnerability or one that
|
|
36
|
+
introduces a flagged version stands out
|
|
37
|
+
- other dependents whose ranges hold the package back
|
|
38
|
+
- engine and peer dependency requirements of the latest version
|
|
39
|
+
that this project does not meet
|
|
40
|
+
- the command that performs the upgrade
|
|
41
|
+
|
|
42
|
+
By default the direct dependencies of the project root and its
|
|
43
|
+
workspaces are checked; --workspace and --workspace-group narrow
|
|
44
|
+
that to the given workspaces. The --target option accepts a DSS
|
|
45
|
+
query selector instead, which can reach any dependency in the
|
|
46
|
+
graph. Package names given as positional arguments filter either
|
|
47
|
+
selection.
|
|
48
|
+
|
|
49
|
+
The installed versions come from the graph built by
|
|
50
|
+
\`vlt install\`, so the project must be installed by vlt first.
|
|
51
|
+
Dependencies that do not resolve against a registry (git, file,
|
|
52
|
+
remote tarball or workspace specs) are not checked. Catalog specs
|
|
53
|
+
are checked against the range the catalog resolves them to.`,
|
|
54
|
+
examples: {
|
|
55
|
+
'': {
|
|
56
|
+
description: 'Report outdated direct dependencies of the project and all its workspaces',
|
|
57
|
+
},
|
|
58
|
+
'react react-dom': {
|
|
59
|
+
description: `Only check the dependencies named 'react' and 'react-dom'`,
|
|
60
|
+
},
|
|
61
|
+
'--workspace=packages/app': {
|
|
62
|
+
description: 'Only check the dependencies of a single workspace',
|
|
63
|
+
},
|
|
64
|
+
'--target="*"': {
|
|
65
|
+
description: 'Check every dependency in the graph, transitive ones included',
|
|
66
|
+
},
|
|
67
|
+
'--target=":root > *:dev"': {
|
|
68
|
+
description: 'Only check the dev dependencies of the project root',
|
|
69
|
+
},
|
|
70
|
+
'--target="*:vuln"': {
|
|
71
|
+
description: 'Only check dependencies with known vulnerabilities',
|
|
72
|
+
},
|
|
73
|
+
'--view=json': {
|
|
74
|
+
description: 'Print the report as JSON',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
options: {
|
|
78
|
+
target: {
|
|
79
|
+
value: '<query>',
|
|
80
|
+
description: 'DSS query selector choosing the dependencies to check, in place of the direct dependencies of the selected importers.',
|
|
81
|
+
},
|
|
82
|
+
workspace: {
|
|
83
|
+
value: '<path>',
|
|
84
|
+
description: 'Limit the report to the dependencies of the given workspace(s).',
|
|
85
|
+
},
|
|
86
|
+
'workspace-group': {
|
|
87
|
+
value: '<name>',
|
|
88
|
+
description: 'Limit the report to the dependencies of the workspaces in the given group(s).',
|
|
89
|
+
},
|
|
90
|
+
view: {
|
|
91
|
+
value: '[human | json | count]',
|
|
92
|
+
description: 'Output format. Defaults to human-readable or json if no tty. Count outputs the number of outdated dependencies.',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
const plural = (n, word) => `${n} ${word}${n === 1 ? '' : 's'}`;
|
|
97
|
+
const missingVersion = (version) => version ?? 'missing';
|
|
98
|
+
/**
|
|
99
|
+
* Lay out columns of cells as a text table, each column padded to its
|
|
100
|
+
* widest cell and separated from the next by two spaces.
|
|
101
|
+
*/
|
|
102
|
+
const formatTable = (columns) => {
|
|
103
|
+
const lines = [];
|
|
104
|
+
for (const { title, cells } of columns) {
|
|
105
|
+
const width = Math.max(title.length, ...cells.map(c => c.length));
|
|
106
|
+
const column = [title, ...cells].map(cell => cell.padEnd(width));
|
|
107
|
+
column.forEach((cell, i) => {
|
|
108
|
+
const line = lines[i];
|
|
109
|
+
lines[i] = line === undefined ? cell : `${line} ${cell}`;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// the last column carries no trailing padding
|
|
113
|
+
return lines.map(line => line.trimEnd()).join('\n');
|
|
114
|
+
};
|
|
115
|
+
const alertsNotIn = (alerts, other) => alerts.filter(a => !other.some(o => o.key === a.key));
|
|
116
|
+
/**
|
|
117
|
+
* The alerts worth a mention in a one-line summary. Low and medium
|
|
118
|
+
* severity alerts (network access, env vars, minified files) come by
|
|
119
|
+
* the dozen on any sizeable package and would drown out the rest.
|
|
120
|
+
*/
|
|
121
|
+
const serious = (alerts) => alerts.filter(a => a.severity === 'high' || a.severity === 'critical');
|
|
122
|
+
/**
|
|
123
|
+
* A short explanation of what the upgrade means, for the human table.
|
|
124
|
+
*/
|
|
125
|
+
const explain = (entry) => {
|
|
126
|
+
const parts = [];
|
|
127
|
+
if (entry.kind !== 'missing')
|
|
128
|
+
parts.push(entry.kind);
|
|
129
|
+
if (entry.deprecated !== undefined)
|
|
130
|
+
parts.push('deprecated');
|
|
131
|
+
const { current } = entry.security ?? {};
|
|
132
|
+
if (current) {
|
|
133
|
+
const targets = [
|
|
134
|
+
['wanted', entry.inRange ? entry.security?.wanted : undefined],
|
|
135
|
+
['latest', entry.security?.latest],
|
|
136
|
+
];
|
|
137
|
+
for (const [label, target] of targets) {
|
|
138
|
+
if (!target)
|
|
139
|
+
continue;
|
|
140
|
+
const fixed = alertsNotIn(serious(current.alerts), target.alerts).length;
|
|
141
|
+
const added = alertsNotIn(serious(target.alerts), current.alerts).length;
|
|
142
|
+
if (fixed) {
|
|
143
|
+
parts.push(`${label} fixes ${plural(fixed, 'serious alert')}`);
|
|
144
|
+
}
|
|
145
|
+
if (added) {
|
|
146
|
+
parts.push(`${label} adds ${plural(added, 'serious alert')}`);
|
|
147
|
+
}
|
|
148
|
+
if (current.score - target.score >= 10) {
|
|
149
|
+
parts.push(`${label} score ${target.score} (from ${current.score})`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (entry.heldBy?.length) {
|
|
154
|
+
parts.push(`held by ${entry.heldBy.map(h => h.dependent).join(', ')}`);
|
|
155
|
+
}
|
|
156
|
+
if (entry.requires?.node) {
|
|
157
|
+
parts.push(`latest needs node ${entry.requires.node}`);
|
|
158
|
+
}
|
|
159
|
+
if (entry.requires?.peers) {
|
|
160
|
+
const peers = Object.entries(entry.requires.peers)
|
|
161
|
+
.map(([name, range]) => `${name}@${range}`)
|
|
162
|
+
.join(', ');
|
|
163
|
+
parts.push(`latest needs ${peers}`);
|
|
164
|
+
}
|
|
165
|
+
return parts.join(', ');
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* The commands that perform the upgrades, one `vlt install` per set of
|
|
169
|
+
* flags so that the specs can be combined. Several entries can call
|
|
170
|
+
* for the same step, such as every workspace that shares a catalog
|
|
171
|
+
* entry, so each step is listed once.
|
|
172
|
+
*/
|
|
173
|
+
const suggestActions = (result) => {
|
|
174
|
+
const lines = [];
|
|
175
|
+
const updates = result.filter(e => e.inRange).length;
|
|
176
|
+
if (updates) {
|
|
177
|
+
lines.push(`Run \`vlt update\` to pick up ${plural(updates, 'in-range update')}.`);
|
|
178
|
+
}
|
|
179
|
+
const installs = new Map();
|
|
180
|
+
const edits = new Set();
|
|
181
|
+
for (const { action } of result) {
|
|
182
|
+
if (!action)
|
|
183
|
+
continue;
|
|
184
|
+
if (action.startsWith('vlt install ')) {
|
|
185
|
+
const [spec = '', ...flags] = action
|
|
186
|
+
.slice('vlt install '.length)
|
|
187
|
+
.split(' ');
|
|
188
|
+
const key = flags.join(' ');
|
|
189
|
+
const specs = installs.get(key) ?? new Set();
|
|
190
|
+
specs.add(spec);
|
|
191
|
+
installs.set(key, specs);
|
|
192
|
+
}
|
|
193
|
+
else if (!action.startsWith('vlt ')) {
|
|
194
|
+
edits.add(`${action.charAt(0).toUpperCase()}${action.slice(1)}.`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
for (const [flags, specs] of installs) {
|
|
198
|
+
const args = [...specs, ...(flags ? [flags] : [])].join(' ');
|
|
199
|
+
lines.push(`Run \`vlt install ${args}\` to move to latest.`);
|
|
200
|
+
}
|
|
201
|
+
lines.push(...edits);
|
|
202
|
+
return lines;
|
|
203
|
+
};
|
|
204
|
+
export const views = {
|
|
205
|
+
json: result => result,
|
|
206
|
+
count: result => result.length,
|
|
207
|
+
human: result => {
|
|
208
|
+
if (result.length === 0) {
|
|
209
|
+
return 'All dependencies are up to date.';
|
|
210
|
+
}
|
|
211
|
+
const columns = [
|
|
212
|
+
{ title: 'Package', cells: result.map(e => e.name) },
|
|
213
|
+
{
|
|
214
|
+
title: 'Current',
|
|
215
|
+
cells: result.map(e => missingVersion(e.current)),
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
title: 'Wanted',
|
|
219
|
+
cells: result.map(e => missingVersion(e.wanted)),
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
title: 'Latest',
|
|
223
|
+
cells: result.map(e => missingVersion(e.latest)),
|
|
224
|
+
},
|
|
225
|
+
{ title: 'Type', cells: result.map(e => e.type) },
|
|
226
|
+
];
|
|
227
|
+
// the dependent column only earns its space when the entries do
|
|
228
|
+
// not all belong to the same importer
|
|
229
|
+
if (new Set(result.map(e => e.dependent)).size > 1) {
|
|
230
|
+
columns.push({
|
|
231
|
+
title: 'Dependent',
|
|
232
|
+
cells: result.map(e => e.dependent),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
columns.push({ title: 'Why', cells: result.map(explain) });
|
|
236
|
+
const actions = suggestActions(result);
|
|
237
|
+
return [
|
|
238
|
+
formatTable(columns),
|
|
239
|
+
...(actions.length ? ['', ...actions] : []),
|
|
240
|
+
].join('\n');
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* The version a registry calls `latest`. Registries that have no
|
|
245
|
+
* `latest` tag fall back to the highest stable published version.
|
|
246
|
+
*/
|
|
247
|
+
const latestVersion = (packument) => {
|
|
248
|
+
const tagged = packument['dist-tags'].latest;
|
|
249
|
+
if (tagged)
|
|
250
|
+
return tagged;
|
|
251
|
+
const [highest] = rsort(stable(Object.keys(packument.versions)));
|
|
252
|
+
return highest;
|
|
253
|
+
};
|
|
254
|
+
const gapKind = (current, target) => {
|
|
255
|
+
const c = parse(current);
|
|
256
|
+
const t = parse(target);
|
|
257
|
+
/* c8 ignore next - versions that reached the graph always parse */
|
|
258
|
+
if (!c || !t)
|
|
259
|
+
return 'patch';
|
|
260
|
+
if (t.major > c.major)
|
|
261
|
+
return 'major';
|
|
262
|
+
if (t.minor > c.minor)
|
|
263
|
+
return 'minor';
|
|
264
|
+
if (t.patch > c.patch)
|
|
265
|
+
return 'patch';
|
|
266
|
+
return 'prerelease';
|
|
267
|
+
};
|
|
268
|
+
/** An importer by name, anything else by name and version. */
|
|
269
|
+
const dependentName = (node) => node.importer ? node.name : `${node.name}@${node.version}`;
|
|
270
|
+
/**
|
|
271
|
+
* The other dependents of the installed node whose ranges do not admit
|
|
272
|
+
* the latest version, i.e. what keeps a transitive dependency back
|
|
273
|
+
* even after this dependent's range is widened.
|
|
274
|
+
*/
|
|
275
|
+
const heldBy = (edge, latest) => {
|
|
276
|
+
const holders = [];
|
|
277
|
+
for (const other of edge.to?.edgesIn ?? []) {
|
|
278
|
+
if (other === edge || other.from.importer)
|
|
279
|
+
continue;
|
|
280
|
+
const { range } = other.spec.final;
|
|
281
|
+
if (range && !satisfies(latest, range)) {
|
|
282
|
+
holders.push({
|
|
283
|
+
dependent: dependentName(other.from),
|
|
284
|
+
spec: other.spec.bareSpec,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return holders;
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* Requirements the latest version makes that the dependent does not
|
|
292
|
+
* meet: a node engine range this process fails, and peer dependency
|
|
293
|
+
* ranges that the dependent's installed peers fall outside of.
|
|
294
|
+
*/
|
|
295
|
+
const unmetRequirements = (edge, latest, nodeVersion) => {
|
|
296
|
+
const requires = {};
|
|
297
|
+
const engine = latest.engines?.node;
|
|
298
|
+
if (engine && !satisfies(nodeVersion, engine))
|
|
299
|
+
requires.node = engine;
|
|
300
|
+
const peers = {};
|
|
301
|
+
for (const [peer, range] of Object.entries(latest.peerDependencies ?? {})) {
|
|
302
|
+
const installed = edge.from.edgesOut.get(peer)?.to?.version;
|
|
303
|
+
if (installed && !satisfies(installed, range))
|
|
304
|
+
peers[peer] = range;
|
|
305
|
+
}
|
|
306
|
+
if (Object.keys(peers).length)
|
|
307
|
+
requires.peers = peers;
|
|
308
|
+
return requires.node || requires.peers ? requires : undefined;
|
|
309
|
+
};
|
|
310
|
+
/**
|
|
311
|
+
* The spec to hand `vlt install` so that an alias or a named registry
|
|
312
|
+
* prefix survives the range change, e.g. `foo@npm:bar@^2.0.0`.
|
|
313
|
+
*/
|
|
314
|
+
const installSpec = (edge, range) => {
|
|
315
|
+
const { bareSpec, final } = edge.spec;
|
|
316
|
+
const prefix = bareSpec.slice(0, bareSpec.length - final.bareSpec.length);
|
|
317
|
+
return `${edge.name}@${prefix}${range}`;
|
|
318
|
+
};
|
|
319
|
+
const saveFlags = {
|
|
320
|
+
prod: '',
|
|
321
|
+
dev: ' --save-dev',
|
|
322
|
+
optional: ' --save-optional',
|
|
323
|
+
peer: ' --save-peer',
|
|
324
|
+
peerOptional: ' --save-peer',
|
|
325
|
+
};
|
|
326
|
+
/**
|
|
327
|
+
* How an importer upgrades this dependency. A version within the
|
|
328
|
+
* declared range is one `vlt update` away. Moving past the range means
|
|
329
|
+
* changing the spec, with `vlt install` or in the catalog.
|
|
330
|
+
*/
|
|
331
|
+
const actionFor = (edge, entry, { savePrefix }) => {
|
|
332
|
+
if (!edge.from.importer)
|
|
333
|
+
return undefined;
|
|
334
|
+
if (!entry.current)
|
|
335
|
+
return 'vlt install';
|
|
336
|
+
const { latest } = entry;
|
|
337
|
+
const { range } = edge.spec.final;
|
|
338
|
+
if (latest && gt(latest, entry.current) && range) {
|
|
339
|
+
if (satisfies(latest, range))
|
|
340
|
+
return 'vlt update';
|
|
341
|
+
const newRange = `${savePrefix}${latest}`;
|
|
342
|
+
if (edge.spec.type === 'catalog') {
|
|
343
|
+
const catalog = edge.spec.catalog ?
|
|
344
|
+
`"${edge.spec.catalog}" catalog`
|
|
345
|
+
: 'catalog';
|
|
346
|
+
return `set the ${catalog} entry for ${edge.name} in vlt.json to ${newRange}`;
|
|
347
|
+
}
|
|
348
|
+
const workspace = edge.from.mainImporter ?
|
|
349
|
+
''
|
|
350
|
+
: ` --workspace=${edge.from.location.replace(/^\.\//, '')}`;
|
|
351
|
+
return `vlt install ${installSpec(edge, newRange)}${saveFlags[edge.type]}${workspace}`;
|
|
352
|
+
}
|
|
353
|
+
return 'vlt update';
|
|
354
|
+
};
|
|
355
|
+
/**
|
|
356
|
+
* Describe the dependency `edge` declares, or nothing when its
|
|
357
|
+
* installed version is as new as the registry offers.
|
|
358
|
+
*/
|
|
359
|
+
const buildEntry = (edge, packument, options) => {
|
|
360
|
+
const current = edge.to?.version;
|
|
361
|
+
const wanted = pickManifest(packument, edge.spec, options.pick)?.version;
|
|
362
|
+
const latest = latestVersion(packument);
|
|
363
|
+
const wantedAhead = !!wanted && (!current || gt(wanted, current));
|
|
364
|
+
const latestAhead = !!latest && (!current || gt(latest, current));
|
|
365
|
+
if (current && !wantedAhead && !latestAhead)
|
|
366
|
+
return undefined;
|
|
367
|
+
const entry = {
|
|
368
|
+
name: edge.name,
|
|
369
|
+
spec: edge.spec.bareSpec,
|
|
370
|
+
type: edge.type,
|
|
371
|
+
current,
|
|
372
|
+
wanted,
|
|
373
|
+
latest,
|
|
374
|
+
dependent: dependentName(edge.from),
|
|
375
|
+
location: edge.from.location,
|
|
376
|
+
kind: !current ? 'missing'
|
|
377
|
+
: latestAhead ? gapKind(current, latest)
|
|
378
|
+
: gapKind(current, String(wanted)),
|
|
379
|
+
inRange: wantedAhead,
|
|
380
|
+
};
|
|
381
|
+
const deprecated = current ? packument.versions[current]?.deprecated : undefined;
|
|
382
|
+
if (deprecated)
|
|
383
|
+
entry.deprecated = deprecated;
|
|
384
|
+
if (latestAhead) {
|
|
385
|
+
const holders = heldBy(edge, latest);
|
|
386
|
+
if (holders.length)
|
|
387
|
+
entry.heldBy = holders;
|
|
388
|
+
const manifest = packument.versions[latest];
|
|
389
|
+
const requires = manifest &&
|
|
390
|
+
unmetRequirements(edge, manifest, options.nodeVersion);
|
|
391
|
+
if (requires)
|
|
392
|
+
entry.requires = requires;
|
|
393
|
+
}
|
|
394
|
+
const action = actionFor(edge, entry, options);
|
|
395
|
+
if (action)
|
|
396
|
+
entry.action = action;
|
|
397
|
+
return entry;
|
|
398
|
+
};
|
|
399
|
+
/**
|
|
400
|
+
* Pick the importers whose dependencies are reported. All importers by
|
|
401
|
+
* default, or only the selected workspaces when `--workspace` or
|
|
402
|
+
* `--workspace-group` is set in a monorepo.
|
|
403
|
+
*/
|
|
404
|
+
const selectImporters = (conf, graph) => {
|
|
405
|
+
const { monorepo } = conf.options;
|
|
406
|
+
const selected = 'workspace' in conf.values || 'workspace-group' in conf.values;
|
|
407
|
+
if (monorepo && selected) {
|
|
408
|
+
const importers = [];
|
|
409
|
+
for (const workspace of monorepo.filter(conf.values)) {
|
|
410
|
+
const node = graph.nodes.get(workspace.id);
|
|
411
|
+
/* c8 ignore next - every loaded workspace has a node */
|
|
412
|
+
if (node)
|
|
413
|
+
importers.push(node);
|
|
414
|
+
}
|
|
415
|
+
return importers;
|
|
416
|
+
}
|
|
417
|
+
return [...graph.importers];
|
|
418
|
+
};
|
|
419
|
+
/**
|
|
420
|
+
* The edges to check: those a `--target` query selects, or else the
|
|
421
|
+
* direct dependencies of the selected importers.
|
|
422
|
+
*/
|
|
423
|
+
const selectEdges = async (conf, graph, archive) => {
|
|
424
|
+
const target = conf.get('target');
|
|
425
|
+
if (!target) {
|
|
426
|
+
return selectImporters(conf, graph).flatMap(importer => [
|
|
427
|
+
...importer.edgesOut.values(),
|
|
428
|
+
]);
|
|
429
|
+
}
|
|
430
|
+
const query = new Query({
|
|
431
|
+
nodes: new Set(graph.nodes.values()),
|
|
432
|
+
edges: graph.edges,
|
|
433
|
+
importers: graph.importers,
|
|
434
|
+
securityArchive: archive,
|
|
435
|
+
hostContexts: await createHostContextsMap(conf),
|
|
436
|
+
getAuthHeader: createGetAuthHeader(conf),
|
|
437
|
+
});
|
|
438
|
+
const { edges } = await query.search(target, {
|
|
439
|
+
signal: new AbortController().signal,
|
|
440
|
+
});
|
|
441
|
+
return edges.filter(edge => edge instanceof Edge);
|
|
442
|
+
};
|
|
443
|
+
/**
|
|
444
|
+
* The security archive is a bonus, never a requirement: when it cannot
|
|
445
|
+
* be reached the report simply carries no security data.
|
|
446
|
+
*/
|
|
447
|
+
const startArchive = async (nodes) => {
|
|
448
|
+
try {
|
|
449
|
+
return await SecurityArchive.start({ nodes });
|
|
450
|
+
}
|
|
451
|
+
catch {
|
|
452
|
+
return undefined;
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
const securityOf = (archive, id) => {
|
|
456
|
+
const report = archive.get(id);
|
|
457
|
+
if (!report)
|
|
458
|
+
return undefined;
|
|
459
|
+
return {
|
|
460
|
+
score: Math.round(report.score.overall * 100),
|
|
461
|
+
alerts: report.alerts,
|
|
462
|
+
};
|
|
463
|
+
};
|
|
464
|
+
export const command = async (conf) => {
|
|
465
|
+
const { projectRoot, packageJson, monorepo } = conf.options;
|
|
466
|
+
assertVltInstalled(projectRoot, 'outdated');
|
|
467
|
+
const mainManifest = packageJson.read(projectRoot);
|
|
468
|
+
const graph = actual.load({
|
|
469
|
+
...conf.options,
|
|
470
|
+
mainManifest,
|
|
471
|
+
modifiers: GraphModifier.maybeLoad(conf.options),
|
|
472
|
+
monorepo,
|
|
473
|
+
loadManifests: true,
|
|
474
|
+
});
|
|
475
|
+
// a query may use security selectors, which need the archive to
|
|
476
|
+
// know every node in the graph before the search runs
|
|
477
|
+
let archive = conf.get('target') ?
|
|
478
|
+
await startArchive([...graph.nodes.values()])
|
|
479
|
+
: undefined;
|
|
480
|
+
const names = new Set(conf.positionals);
|
|
481
|
+
const edges = (await selectEdges(conf, graph, archive)).filter(edge => (names.size === 0 || names.has(edge.name)) &&
|
|
482
|
+
edge.spec.final.type === 'registry');
|
|
483
|
+
const pic = new PackageInfoClient(conf.options);
|
|
484
|
+
// one packument request per registry package, however many
|
|
485
|
+
// dependents it has
|
|
486
|
+
const packuments = new Map();
|
|
487
|
+
const fetchPackument = (edge) => {
|
|
488
|
+
// the final spec of a registry dependency always carries its
|
|
489
|
+
// registry, so the key is unique per package per registry
|
|
490
|
+
const { registry, name } = edge.spec.final;
|
|
491
|
+
const key = `${String(registry)}:${name}`;
|
|
492
|
+
let p = packuments.get(key);
|
|
493
|
+
if (!p) {
|
|
494
|
+
p = pic.packument(edge.spec);
|
|
495
|
+
packuments.set(key, p);
|
|
496
|
+
}
|
|
497
|
+
return p;
|
|
498
|
+
};
|
|
499
|
+
const options = {
|
|
500
|
+
nodeVersion: conf.options['node-version'],
|
|
501
|
+
savePrefix: conf.options['save-exact'] ? '' : conf.options['save-prefix'],
|
|
502
|
+
pick: conf.options,
|
|
503
|
+
};
|
|
504
|
+
const found = [];
|
|
505
|
+
for (const edge of edges) {
|
|
506
|
+
const entry = buildEntry(edge, await fetchPackument(edge), options);
|
|
507
|
+
if (entry)
|
|
508
|
+
found.push({ edge, entry });
|
|
509
|
+
}
|
|
510
|
+
// look up the installed, wanted and latest versions in the security
|
|
511
|
+
// archive, the two candidates as stand-in nodes
|
|
512
|
+
const stub = (edge, version) => createStubNode(edge.spec, edge.spec.final.name, version, conf.options);
|
|
513
|
+
const securityNodes = new Map();
|
|
514
|
+
const candidates = [];
|
|
515
|
+
for (const { edge, entry } of found) {
|
|
516
|
+
const ids = {};
|
|
517
|
+
if (edge.to) {
|
|
518
|
+
ids.current = edge.to.id;
|
|
519
|
+
securityNodes.set(edge.to.id, edge.to);
|
|
520
|
+
}
|
|
521
|
+
for (const which of ['wanted', 'latest']) {
|
|
522
|
+
const version = entry[which];
|
|
523
|
+
if (!version)
|
|
524
|
+
continue;
|
|
525
|
+
const node = stub(edge, version);
|
|
526
|
+
ids[which] = node.id;
|
|
527
|
+
securityNodes.set(node.id, node);
|
|
528
|
+
}
|
|
529
|
+
candidates.push({ entry, ids });
|
|
530
|
+
}
|
|
531
|
+
const nodes = [...securityNodes.values()];
|
|
532
|
+
if (archive) {
|
|
533
|
+
await archive.refresh({ nodes }).catch(() => {
|
|
534
|
+
archive = undefined;
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
else if (nodes.length) {
|
|
538
|
+
archive = await startArchive(nodes);
|
|
539
|
+
}
|
|
540
|
+
if (archive) {
|
|
541
|
+
for (const { entry, ids } of candidates) {
|
|
542
|
+
const security = {};
|
|
543
|
+
for (const which of ['current', 'wanted', 'latest']) {
|
|
544
|
+
const id = ids[which];
|
|
545
|
+
const report = id && securityOf(archive, id);
|
|
546
|
+
if (report)
|
|
547
|
+
security[which] = report;
|
|
548
|
+
}
|
|
549
|
+
if (Object.keys(security).length)
|
|
550
|
+
entry.security = security;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return found.map(({ entry }) => entry);
|
|
554
|
+
};
|
package/dist/commands/update.js
CHANGED
|
@@ -10,8 +10,17 @@ export const usage = () => commandUsage({
|
|
|
10
10
|
command: 'update',
|
|
11
11
|
usage: '',
|
|
12
12
|
description: `Update dependencies to their latest in-range versions.
|
|
13
|
-
Discards the lockfile and resolves dependencies from scratch
|
|
13
|
+
Discards the lockfile and resolves dependencies from scratch
|
|
14
|
+
(with -w: only matching workspaces' dependencies).`,
|
|
14
15
|
options: {
|
|
16
|
+
workspace: {
|
|
17
|
+
value: '<path|glob>',
|
|
18
|
+
description: 'Limit update to matching workspaces. Others, and deps they share, keep their locked versions.',
|
|
19
|
+
},
|
|
20
|
+
'workspace-group': {
|
|
21
|
+
value: '<name>',
|
|
22
|
+
description: 'Limit update to workspace groups. Others, and deps they share, keep their locked versions.',
|
|
23
|
+
},
|
|
15
24
|
'save-config': {
|
|
16
25
|
description: 'Save registry and git host options given on the command line or via env (e.g. `--registries name=url`) to the project vlt.json, or the user vlt.json with --config=user.',
|
|
17
26
|
},
|
package/dist/commands/view.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as dotProp from '@vltpkg/dot-prop';
|
|
2
2
|
import { error } from '@vltpkg/error-cause';
|
|
3
|
-
import { getId } from '@vltpkg/dep-id';
|
|
4
3
|
import { PackageInfoClient } from '@vltpkg/package-info';
|
|
5
4
|
import { SecurityArchive } from '@vltpkg/security-archive';
|
|
6
5
|
import { Spec } from '@vltpkg/spec';
|
|
7
6
|
import { commandUsage } from "../config/usage.js";
|
|
7
|
+
import { createStubNode } from "../stub-node.js";
|
|
8
8
|
export const needsRegistry = true;
|
|
9
9
|
export const usage = () => commandUsage({
|
|
10
10
|
command: 'view',
|
|
@@ -22,7 +22,11 @@ export const usage = () => commandUsage({
|
|
|
22
22
|
(e.g., \`dist-tags.latest\`, \`dependencies.lodash\`).
|
|
23
23
|
|
|
24
24
|
Security data from the vlt security archive is shown when
|
|
25
|
-
available, including scores and alerts
|
|
25
|
+
available, including scores and alerts.
|
|
26
|
+
|
|
27
|
+
Use \`.\` (or no args) for the package in the current directory
|
|
28
|
+
(nearest package.json), \`.@<version>\` to pick a version.
|
|
29
|
+
Private packages show local package.json info.`,
|
|
26
30
|
examples: {
|
|
27
31
|
express: {
|
|
28
32
|
description: 'View info about the latest version of express',
|
|
@@ -42,6 +46,12 @@ export const usage = () => commandUsage({
|
|
|
42
46
|
'express dist-tags.latest': {
|
|
43
47
|
description: 'Show the latest dist-tag value',
|
|
44
48
|
},
|
|
49
|
+
'.': {
|
|
50
|
+
description: 'View the package in the current directory',
|
|
51
|
+
},
|
|
52
|
+
'. version': {
|
|
53
|
+
description: 'Show the latest published version of the current package',
|
|
54
|
+
},
|
|
45
55
|
},
|
|
46
56
|
options: {
|
|
47
57
|
view: {
|
|
@@ -114,7 +124,7 @@ const formatHuman = (result) => {
|
|
|
114
124
|
// Name and version
|
|
115
125
|
const name = manifest.name ?? packument.name;
|
|
116
126
|
const version = manifest.version ?? '';
|
|
117
|
-
lines.push(`${name}@${version}`);
|
|
127
|
+
lines.push(version ? `${name}@${version}` : name);
|
|
118
128
|
// Description
|
|
119
129
|
if (manifest.description) {
|
|
120
130
|
lines.push(manifest.description);
|
|
@@ -227,32 +237,6 @@ export const views = {
|
|
|
227
237
|
};
|
|
228
238
|
},
|
|
229
239
|
};
|
|
230
|
-
/**
|
|
231
|
-
* Create a minimal NodeLike for SecurityArchive lookup.
|
|
232
|
-
* Only the fields used by SecurityArchive.start() are needed.
|
|
233
|
-
*/
|
|
234
|
-
const createFakeNode = (spec, name, version, options) => ({
|
|
235
|
-
id: getId(spec, { name, version }),
|
|
236
|
-
name,
|
|
237
|
-
version,
|
|
238
|
-
confused: false,
|
|
239
|
-
edgesIn: new Set(),
|
|
240
|
-
edgesOut: new Map(),
|
|
241
|
-
workspaces: undefined,
|
|
242
|
-
importer: false,
|
|
243
|
-
mainImporter: false,
|
|
244
|
-
projectRoot: '',
|
|
245
|
-
dev: false,
|
|
246
|
-
optional: false,
|
|
247
|
-
graph: {},
|
|
248
|
-
options,
|
|
249
|
-
/* c8 ignore next 5 - stub methods for NodeLike interface */
|
|
250
|
-
toJSON: () => ({}),
|
|
251
|
-
toString: () => `${name}@${version}`,
|
|
252
|
-
setResolved: () => { },
|
|
253
|
-
setConfusedManifest: () => { },
|
|
254
|
-
maybeSetConfusedManifest: () => { },
|
|
255
|
-
});
|
|
256
240
|
/**
|
|
257
241
|
* Lookup fields from a combined packument+manifest view.
|
|
258
242
|
*
|
|
@@ -289,15 +273,30 @@ const lookupField = (result, path) => {
|
|
|
289
273
|
// Default: look up in manifest
|
|
290
274
|
return dotProp.get(result.manifest, path);
|
|
291
275
|
};
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
if (!
|
|
295
|
-
throw error('
|
|
276
|
+
const readLocalManifest = (conf) => {
|
|
277
|
+
const file = conf.options.packageJson.find(process.cwd());
|
|
278
|
+
if (!file) {
|
|
279
|
+
throw error('No local package.json found', {
|
|
296
280
|
code: 'EUSAGE',
|
|
281
|
+
path: process.cwd(),
|
|
297
282
|
});
|
|
298
283
|
}
|
|
299
|
-
const
|
|
300
|
-
const
|
|
284
|
+
const manifest = conf.options.packageJson.read(file);
|
|
285
|
+
const { name } = manifest;
|
|
286
|
+
if (!name) {
|
|
287
|
+
throw error('No package name found in package.json', {
|
|
288
|
+
code: 'EUSAGE',
|
|
289
|
+
found: file,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
return { name, manifest };
|
|
293
|
+
};
|
|
294
|
+
// nothing published: no dist-tags or versions
|
|
295
|
+
const localView = (name, manifest) => ({
|
|
296
|
+
manifest,
|
|
297
|
+
packument: { name, 'dist-tags': {}, versions: {} },
|
|
298
|
+
});
|
|
299
|
+
const fetchView = async (spec, conf) => {
|
|
301
300
|
const pic = new PackageInfoClient(conf.options);
|
|
302
301
|
// Fetch the full packument (needs time, maintainers) and resolved manifest
|
|
303
302
|
const [packument, resolvedManifest] = await Promise.all([
|
|
@@ -311,7 +310,7 @@ export const command = async (conf) => {
|
|
|
311
310
|
const version = manifest.version;
|
|
312
311
|
if (name && version) {
|
|
313
312
|
try {
|
|
314
|
-
const node =
|
|
313
|
+
const node = createStubNode(spec, name, version, conf.options);
|
|
315
314
|
const archive = await SecurityArchive.start({
|
|
316
315
|
nodes: [node],
|
|
317
316
|
});
|
|
@@ -321,11 +320,34 @@ export const command = async (conf) => {
|
|
|
321
320
|
// Security data is optional, don't fail the command
|
|
322
321
|
}
|
|
323
322
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
323
|
+
return { packument, manifest, security };
|
|
324
|
+
};
|
|
325
|
+
export const command = async (conf) => {
|
|
326
|
+
// `.` (or no args) is the package in the current directory
|
|
327
|
+
const specArg = conf.positionals[0] || '.';
|
|
328
|
+
const fieldPath = conf.positionals[1];
|
|
329
|
+
let result;
|
|
330
|
+
if (specArg === '.' || specArg.startsWith('.@')) {
|
|
331
|
+
const { name, manifest } = readLocalManifest(conf);
|
|
332
|
+
const rest = specArg.slice(1);
|
|
333
|
+
if (manifest.private) {
|
|
334
|
+
// never query the registry for private pkgs
|
|
335
|
+
if (rest) {
|
|
336
|
+
throw error('Cannot select a version of a private package', {
|
|
337
|
+
code: 'EUSAGE',
|
|
338
|
+
found: specArg,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
result = localView(name, manifest);
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
// keep name literal; rest is '' or '@<spec>'
|
|
345
|
+
result = await fetchView(Spec.parse(name, rest.slice(1), conf.options), conf);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
result = await fetchView(Spec.parseArgs(specArg, conf.options), conf);
|
|
350
|
+
}
|
|
329
351
|
// If a field path is provided, resolve it
|
|
330
352
|
if (fieldPath !== undefined) {
|
|
331
353
|
result.fieldPath = fieldPath;
|
package/dist/custom-help.js
CHANGED
|
@@ -129,6 +129,10 @@ const commandHelp = {
|
|
|
129
129
|
args: '',
|
|
130
130
|
desc: 'Log out from a registry',
|
|
131
131
|
},
|
|
132
|
+
outdated: {
|
|
133
|
+
args: '[package-names...]',
|
|
134
|
+
desc: 'List dependencies with newer versions available',
|
|
135
|
+
},
|
|
132
136
|
pack: {
|
|
133
137
|
args: '',
|
|
134
138
|
desc: 'Create a tarball from a package',
|
package/dist/query-auth.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import type { GetAuthHeader } from '@vltpkg/query';
|
|
|
2
2
|
import type { LoadedConfig } from './config/index.ts';
|
|
3
3
|
/**
|
|
4
4
|
* Returns a {@link GetAuthHeader} implementation that looks up
|
|
5
|
-
* authorization tokens from the local keychain / env
|
|
5
|
+
* authorization tokens from the local keychain / env (incl.
|
|
6
|
+
* `VLT_TOKEN_<key>` for configured registries), used by
|
|
6
7
|
* network-reliant query selectors, e.g: `:outdated()`.
|
|
7
8
|
*/
|
|
8
9
|
export declare const createGetAuthHeader: (conf: LoadedConfig) => GetAuthHeader;
|
package/dist/query-auth.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import { getTokenByURL } from '@vltpkg/registry-client';
|
|
1
|
+
import { getTokenByURL, registryKeys } from '@vltpkg/registry-client';
|
|
2
2
|
/**
|
|
3
3
|
* Returns a {@link GetAuthHeader} implementation that looks up
|
|
4
|
-
* authorization tokens from the local keychain / env
|
|
4
|
+
* authorization tokens from the local keychain / env (incl.
|
|
5
|
+
* `VLT_TOKEN_<key>` for configured registries), used by
|
|
5
6
|
* network-reliant query selectors, e.g: `:outdated()`.
|
|
6
7
|
*/
|
|
7
|
-
export const createGetAuthHeader = (conf) =>
|
|
8
|
+
export const createGetAuthHeader = (conf) => {
|
|
9
|
+
const keys = registryKeys(conf.options);
|
|
10
|
+
return async (url) => getTokenByURL(url, conf.options.identity, keys);
|
|
11
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Spec, SpecOptions } from '@vltpkg/spec';
|
|
2
|
+
import type { NodeLike } from '@vltpkg/types';
|
|
3
|
+
/**
|
|
4
|
+
* Create a minimal NodeLike for a package version that is not part of
|
|
5
|
+
* any graph, so that it can be looked up in the security archive.
|
|
6
|
+
* Only the fields used by `SecurityArchive.start()` are needed.
|
|
7
|
+
*/
|
|
8
|
+
export declare const createStubNode: (spec: Spec, name: string, version: string, options: SpecOptions) => NodeLike;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { getId } from '@vltpkg/dep-id';
|
|
2
|
+
/**
|
|
3
|
+
* Create a minimal NodeLike for a package version that is not part of
|
|
4
|
+
* any graph, so that it can be looked up in the security archive.
|
|
5
|
+
* Only the fields used by `SecurityArchive.start()` are needed.
|
|
6
|
+
*/
|
|
7
|
+
export const createStubNode = (spec, name, version, options) => ({
|
|
8
|
+
id: getId(spec, { name, version }),
|
|
9
|
+
name,
|
|
10
|
+
version,
|
|
11
|
+
confused: false,
|
|
12
|
+
edgesIn: new Set(),
|
|
13
|
+
edgesOut: new Map(),
|
|
14
|
+
workspaces: undefined,
|
|
15
|
+
importer: false,
|
|
16
|
+
mainImporter: false,
|
|
17
|
+
projectRoot: '',
|
|
18
|
+
dev: false,
|
|
19
|
+
optional: false,
|
|
20
|
+
graph: {},
|
|
21
|
+
options,
|
|
22
|
+
/* c8 ignore next 5 - stub methods for NodeLike interface */
|
|
23
|
+
toJSON: () => ({}),
|
|
24
|
+
toString: () => `${name}@${version}`,
|
|
25
|
+
setResolved: () => { },
|
|
26
|
+
setConfusedManifest: () => { },
|
|
27
|
+
maybeSetConfusedManifest: () => { },
|
|
28
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vltpkg/cli-sdk",
|
|
3
3
|
"description": "The source for the vlt CLI",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.8",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/vltpkg/vltpkg.git",
|
|
@@ -14,31 +14,33 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@resvg/resvg-wasm": "^2.6.2",
|
|
17
|
-
"@vltpkg/config": "1.3.
|
|
18
|
-
"@vltpkg/dep-id": "1.3.
|
|
19
|
-
"@vltpkg/dot-prop": "1.3.
|
|
20
|
-
"@vltpkg/error-cause": "1.3.
|
|
21
|
-
"@vltpkg/git": "1.3.
|
|
22
|
-
"@vltpkg/graph": "1.3.
|
|
23
|
-
"@vltpkg/graph-run": "1.3.
|
|
24
|
-
"@vltpkg/init": "1.3.
|
|
25
|
-
"@vltpkg/output": "1.3.
|
|
26
|
-
"@vltpkg/package-info": "1.3.
|
|
27
|
-
"@vltpkg/package-json": "1.3.
|
|
28
|
-
"@vltpkg/
|
|
29
|
-
"@vltpkg/
|
|
30
|
-
"@vltpkg/
|
|
31
|
-
"@vltpkg/
|
|
32
|
-
"@vltpkg/
|
|
33
|
-
"@vltpkg/
|
|
34
|
-
"@vltpkg/
|
|
35
|
-
"@vltpkg/
|
|
36
|
-
"@vltpkg/
|
|
37
|
-
"@vltpkg/
|
|
38
|
-
"@vltpkg/
|
|
39
|
-
"@vltpkg/
|
|
40
|
-
"@vltpkg/
|
|
41
|
-
"@vltpkg/
|
|
17
|
+
"@vltpkg/config": "1.3.8",
|
|
18
|
+
"@vltpkg/dep-id": "1.3.8",
|
|
19
|
+
"@vltpkg/dot-prop": "1.3.8",
|
|
20
|
+
"@vltpkg/error-cause": "1.3.8",
|
|
21
|
+
"@vltpkg/git": "1.3.8",
|
|
22
|
+
"@vltpkg/graph": "1.3.8",
|
|
23
|
+
"@vltpkg/graph-run": "1.3.8",
|
|
24
|
+
"@vltpkg/init": "1.3.8",
|
|
25
|
+
"@vltpkg/output": "1.3.8",
|
|
26
|
+
"@vltpkg/package-info": "1.3.8",
|
|
27
|
+
"@vltpkg/package-json": "1.3.8",
|
|
28
|
+
"@vltpkg/pick-manifest": "1.3.8",
|
|
29
|
+
"@vltpkg/promise-spawn": "1.3.8",
|
|
30
|
+
"@vltpkg/query": "1.3.8",
|
|
31
|
+
"@vltpkg/registry-client": "1.3.8",
|
|
32
|
+
"@vltpkg/rollback-remove": "1.3.8",
|
|
33
|
+
"@vltpkg/run": "1.3.8",
|
|
34
|
+
"@vltpkg/security-archive": "1.3.8",
|
|
35
|
+
"@vltpkg/semver": "1.3.8",
|
|
36
|
+
"@vltpkg/spec": "1.3.8",
|
|
37
|
+
"@vltpkg/tar": "1.3.8",
|
|
38
|
+
"@vltpkg/types": "1.3.8",
|
|
39
|
+
"@vltpkg/url-open": "1.3.8",
|
|
40
|
+
"@vltpkg/vlt-json": "1.3.8",
|
|
41
|
+
"@vltpkg/vlx": "1.3.8",
|
|
42
|
+
"@vltpkg/workspaces": "1.3.8",
|
|
43
|
+
"@vltpkg/xdg": "1.3.8",
|
|
42
44
|
"ansi-to-pre": "^1.0.6",
|
|
43
45
|
"beautiful-mermaid": "^1.1.3",
|
|
44
46
|
"chalk": "^5.6.2",
|