@vltpkg/graph 1.0.2 → 1.0.4

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.
@@ -4,6 +4,7 @@ import type { DepID } from '@vltpkg/dep-id';
4
4
  import type { PackageJson } from '@vltpkg/package-json';
5
5
  import type { SpecOptions } from '@vltpkg/spec';
6
6
  import type { NormalizedManifest } from '@vltpkg/types';
7
+ import type { LockfileData, SpecCache } from '../lockfile/types.ts';
7
8
  import type { Monorepo } from '@vltpkg/workspaces';
8
9
  import type { Path, PathScurry } from 'path-scurry';
9
10
  import type { GraphModifier } from '../modifiers.ts';
@@ -65,6 +66,15 @@ export type LoadOptions = SpecOptions & {
65
66
  * operations. Skips package extraction, filesystem operations, and hidden lockfile saves.
66
67
  */
67
68
  lockfileOnly?: boolean;
69
+ /**
70
+ * Shared intern cache for parsed lockfile specs.
71
+ */
72
+ specCache?: SpecCache;
73
+ /**
74
+ * Already-parsed main lockfile data. When set, ideal build skips
75
+ * re-reading `vlt-lock.json`.
76
+ */
77
+ lockfileData?: LockfileData;
68
78
  };
69
79
  export type ReadEntry = {
70
80
  alias: string;
package/dist/browser.d.ts CHANGED
@@ -5,7 +5,7 @@ import { stringifyNode } from './stringify-node.ts';
5
5
  export type { LoadResult, TransferData, } from './transfer-data/load.ts';
6
6
  export * from './virtual-root.ts';
7
7
  declare const lockfile: {
8
- loadEdges: (graph: import("@vltpkg/types").GraphLike, edges: import("./lockfile/types.ts").LockfileData["edges"], options: import("@vltpkg/spec").SpecOptions) => void;
8
+ loadEdges: (graph: import("@vltpkg/types").GraphLike, edges: import("./lockfile/types.ts").LockfileData["edges"], options: import("@vltpkg/spec").SpecOptions, cache?: import("./lockfile/load-edges.ts").LoadEdgesCache) => void;
9
9
  loadNodes: (graph: import("@vltpkg/types").GraphLike, nodes: import("./lockfile/types.ts").LockfileData["nodes"], options: import("@vltpkg/spec").SpecOptions, actual?: import("@vltpkg/types").GraphLike, throwOnMissingManifest?: boolean) => void;
10
10
  };
11
11
  declare const transfer: {
@@ -41,6 +41,17 @@ export declare const checkPeerEdgesCompatible: (existingNode: Node, fromNode: No
41
41
  * Retrieve a unique hash value for a given peer context set.
42
42
  */
43
43
  export declare const retrievePeerContextHash: (peerContext: PeerContext | undefined) => string | undefined;
44
+ /**
45
+ * Generate a dedupe key for a spec stored in a peer context entry's
46
+ * `specs` map. Two specs with the same key are interchangeable for the
47
+ * purposes of `incompatibleSpecs()`/`satisfies()` checks, so only one
48
+ * needs to be retained regardless of how many dependents contribute it.
49
+ *
50
+ * Keyed on type + textual spec (name@bareSpec) + registry, since specs
51
+ * that are textually identical but resolve against different registries
52
+ * are not interchangeable.
53
+ */
54
+ export declare const peerSpecKey: (spec: Spec) => string;
44
55
  /**
45
56
  * Checks if a given spec is compatible with the specs already
46
57
  * assigned to a peer context entry.
@@ -179,7 +179,7 @@ export const checkPeerEdgesCompatible = (existingNode, fromNode, peerContext, gr
179
179
  const existingTarget = existingEdge.to;
180
180
  const existingTargetSatisfiesPeer = satisfiesNodeSpec(existingTarget, peerSpec);
181
181
  if (existingTargetSatisfiesPeer &&
182
- nodeSatisfiesAll(existingTarget, contextEntry.specs)) {
182
+ nodeSatisfiesAll(existingTarget, contextEntry.specs.values())) {
183
183
  continue; // Truly no conflict
184
184
  }
185
185
  const result = buildIncompatibleResult(contextEntry.target, peerSpec, contextEntry.type, fromNode, graph);
@@ -259,6 +259,20 @@ export const retrievePeerContextHash = (peerContext) => {
259
259
  return undefined;
260
260
  return `peer.${peerContext.index}`;
261
261
  };
262
+ /**
263
+ * Generate a dedupe key for a spec stored in a peer context entry's
264
+ * `specs` map. Two specs with the same key are interchangeable for the
265
+ * purposes of `incompatibleSpecs()`/`satisfies()` checks, so only one
266
+ * needs to be retained regardless of how many dependents contribute it.
267
+ *
268
+ * Keyed on type + textual spec (name@bareSpec) + registry, since specs
269
+ * that are textually identical but resolve against different registries
270
+ * are not interchangeable.
271
+ */
272
+ export const peerSpecKey = (spec) => {
273
+ const f = spec.final;
274
+ return `${f.type}\0${String(f)}\0${f.registry ?? ''}`;
275
+ };
262
276
  /**
263
277
  * Checks if a given spec is compatible with the specs already
264
278
  * assigned to a peer context entry.
@@ -276,7 +290,7 @@ export const retrievePeerContextHash = (peerContext) => {
276
290
  */
277
291
  export const incompatibleSpecs = (spec, entry) => {
278
292
  if (entry.specs.size > 0) {
279
- for (const s_ of entry.specs) {
293
+ for (const s_ of entry.specs.values()) {
280
294
  const s = s_.final;
281
295
  if (
282
296
  // Registry types: check semver range intersection
@@ -337,7 +351,7 @@ export const addEntriesToPeerContext = (peerContext, entries, fromNode, monorepo
337
351
  if (!entry) {
338
352
  entry = {
339
353
  active: true,
340
- specs: new Set([spec]),
354
+ specs: new Map([[peerSpecKey(spec), spec]]),
341
355
  target,
342
356
  type,
343
357
  contextDependents: new Set(),
@@ -352,7 +366,7 @@ export const addEntriesToPeerContext = (peerContext, entries, fromNode, monorepo
352
366
  return true;
353
367
  // update target if compatible with all specs
354
368
  if (target &&
355
- [...entry.specs].every(s => satisfies(target.id, s, fromNode.location, fromNode.projectRoot, monorepo))) {
369
+ [...entry.specs.values()].every(s => satisfies(target.id, s, fromNode.location, fromNode.projectRoot, monorepo))) {
356
370
  if (target.id !== entry.target?.id &&
357
371
  target.version !== entry.target?.version) {
358
372
  // update dependents to point to new target
@@ -368,7 +382,9 @@ export const addEntriesToPeerContext = (peerContext, entries, fromNode, monorepo
368
382
  }
369
383
  entry.target ??= target;
370
384
  }
371
- entry.specs.add(spec);
385
+ const specKey = peerSpecKey(spec);
386
+ if (!entry.specs.has(specKey))
387
+ entry.specs.set(specKey, spec);
372
388
  if (dependent)
373
389
  entry.contextDependents.add(dependent);
374
390
  }
@@ -395,7 +411,7 @@ export const forkPeerContext = (graph, peerContext, entries) => {
395
411
  for (const [name, entry] of peerContext.entries()) {
396
412
  nextPeerContext.set(name, {
397
413
  active: false,
398
- specs: new Set(entry.specs),
414
+ specs: new Map(entry.specs),
399
415
  target: undefined,
400
416
  type: entry.type,
401
417
  contextDependents: new Set(entry.contextDependents),
@@ -408,7 +424,7 @@ export const forkPeerContext = (graph, peerContext, entries) => {
408
424
  const name = target?.name /* c8 ignore next */ ?? spec.final.name;
409
425
  const newEntry = {
410
426
  active: true,
411
- specs: new Set([spec]),
427
+ specs: new Map([[peerSpecKey(spec), spec]]),
412
428
  target,
413
429
  type,
414
430
  contextDependents: dependent ? new Set([dependent]) : new Set(),
@@ -610,7 +626,10 @@ export const endPeerPlacement = (peerContext, nextDeps, nextPeerDeps, graph, spe
610
626
  entry?.target &&
611
627
  nodeSatisfiesSpec(entry.target, spec, fromNode, graph)) {
612
628
  graph.addEdge(type, spec, node, entry.target);
613
- entry.specs.add(spec.final);
629
+ const finalKey = peerSpecKey(spec.final);
630
+ if (!entry.specs.has(finalKey)) {
631
+ entry.specs.set(finalKey, spec.final);
632
+ }
614
633
  continue;
615
634
  }
616
635
  // PRIORITY 4: Fallback - add to nextDeps or create dangling edge
@@ -81,8 +81,12 @@ export type PeerContextEntry = {
81
81
  * peer context set and should not be considered for resolution.
82
82
  */
83
83
  active: boolean;
84
- /** List of full Spec objects that are part of this peer context entry */
85
- specs: Set<Spec>;
84
+ /**
85
+ * Full Spec objects that are part of this peer context entry, keyed by
86
+ * `peerSpecKey()` so textually identical specs collapse into one entry
87
+ * instead of piling up per dependent.
88
+ */
89
+ specs: Map<string, Spec>;
86
90
  /** The target Node that satisfies all specs for this peer context entry */
87
91
  target: Node | undefined;
88
92
  /** The type of dependency this entry represents */
package/dist/index.d.ts CHANGED
@@ -22,7 +22,8 @@ export declare const actual: {
22
22
  import type { LoadOptions as LockfileLoadOptions } from './lockfile/load.ts';
23
23
  export declare const lockfile: {
24
24
  load: (options: LockfileLoadOptions) => import("./graph.ts").Graph;
25
- loadEdges: (graph: import("@vltpkg/types").GraphLike, edges: import("./lockfile/types.ts").LockfileData["edges"], options: import("@vltpkg/spec").SpecOptions) => void;
25
+ loadData: (projectRoot: string, lockfilePath?: string) => import("./lockfile/types.ts").LockfileData;
26
+ loadEdges: (graph: import("@vltpkg/types").GraphLike, edges: import("./lockfile/types.ts").LockfileData["edges"], options: import("@vltpkg/spec").SpecOptions, cache?: import("./lockfile/load-edges.ts").LoadEdgesCache) => void;
26
27
  loadNodes: (graph: import("@vltpkg/types").GraphLike, nodes: import("./lockfile/types.ts").LockfileData["nodes"], options: import("@vltpkg/spec").SpecOptions, actual?: import("@vltpkg/types").GraphLike, throwOnMissingManifest?: boolean) => void;
27
28
  save: (options: Omit<import("./lockfile/save.ts").SaveOptions, "saveManifests">) => void;
28
29
  };
package/dist/index.js CHANGED
@@ -17,12 +17,13 @@ export * from "./modifiers.js";
17
17
  export * from "./virtual-root.js";
18
18
  import { load as actualLoad } from "./actual/load.js";
19
19
  export const actual = { load: actualLoad };
20
- import { load as lockfileLoad } from "./lockfile/load.js";
20
+ import { load as lockfileLoad, loadData as lockfileLoadData, } from "./lockfile/load.js";
21
21
  import { loadEdges } from "./lockfile/load-edges.js";
22
22
  import { loadNodes } from "./lockfile/load-nodes.js";
23
23
  import { save } from "./lockfile/save.js";
24
24
  export const lockfile = {
25
25
  load: lockfileLoad,
26
+ loadData: lockfileLoadData,
26
27
  loadEdges,
27
28
  loadNodes,
28
29
  save,
package/dist/install.js CHANGED
@@ -9,7 +9,7 @@ import { getDependencies } from "./dependencies.js";
9
9
  import { RollbackRemove } from '@vltpkg/rollback-remove';
10
10
  import { existsSync, rmSync } from 'node:fs';
11
11
  import { resolve } from 'node:path';
12
- import { load as loadVirtual } from "./lockfile/load.js";
12
+ import { load as loadVirtual, loadData } from "./lockfile/load.js";
13
13
  import { getImporterSpecs } from "./ideal/get-importer-specs.js";
14
14
  import { lockfile } from "./index.js";
15
15
  import { updatePackageJson } from "./reify/update-importers-package-json.js";
@@ -50,6 +50,8 @@ export const install = async (options, add) => {
50
50
  packageJson: options.packageJson,
51
51
  scurry: options.scurry,
52
52
  });
53
+ const specCache = new Map();
54
+ const lockfileData = options.frozenLockfile ? loadData(options.projectRoot) : undefined;
53
55
  if (options.frozenLockfile) {
54
56
  // validates no add/remove operations are requested
55
57
  if (add?.modifiedDependencies) {
@@ -65,6 +67,8 @@ export const install = async (options, add) => {
65
67
  ...options,
66
68
  mainManifest,
67
69
  monorepo: fullMonorepo,
70
+ specCache,
71
+ lockfileData,
68
72
  });
69
73
  const emptyAdd = Object.assign(new Map(), { modifiedDependencies: false });
70
74
  const emptyRemove = Object.assign(new Map(), {
@@ -149,6 +153,7 @@ export const install = async (options, add) => {
149
153
  loadManifests: true,
150
154
  modifiers: undefined, // modifiers should not be used here
151
155
  monorepo: fullMonorepo,
156
+ specCache,
152
157
  });
153
158
  // if the actual graph has no dependencies, it's simpler to ignore it
154
159
  // this allows us to check for its availability later on for properly
@@ -166,6 +171,8 @@ export const install = async (options, add) => {
166
171
  remove,
167
172
  remover,
168
173
  monorepo: fullMonorepo,
174
+ specCache,
175
+ lockfileData,
169
176
  });
170
177
  // If lockfileOnly is enabled, skip reify and only save the lockfile
171
178
  if (options.lockfileOnly) {
@@ -1,11 +1,15 @@
1
1
  import { Spec } from '@vltpkg/spec/browser';
2
2
  import type { SpecOptions } from '@vltpkg/spec/browser';
3
3
  import type { DependencyTypeShort, GraphLike, NodeLike } from '@vltpkg/types';
4
- import type { LockfileData } from './types.ts';
4
+ import type { LockfileData, SpecCache } from './types.ts';
5
+ export type LoadEdgesCache = {
6
+ specCache: SpecCache;
7
+ prefix: string;
8
+ };
5
9
  export type ProcessingEdge = {
6
10
  fromNode: NodeLike;
7
11
  toNode: NodeLike | undefined;
8
12
  depType: DependencyTypeShort;
9
13
  spec: Spec;
10
14
  };
11
- export declare const loadEdges: (graph: GraphLike, edges: LockfileData["edges"], options: SpecOptions) => void;
15
+ export declare const loadEdges: (graph: GraphLike, edges: LockfileData["edges"], options: SpecOptions, cache?: LoadEdgesCache) => void;
@@ -26,7 +26,20 @@ const retrieveNodeFromGraph = (key, value, graph, fromId, seenNodes) => {
26
26
  }
27
27
  return foundNode;
28
28
  };
29
- export const loadEdges = (graph, edges, options) => {
29
+ const parseEdgeSpec = (specName, bareSpec, options, cache) => {
30
+ // graph.addEdge mutates name/spec on '(unknown)' instances
31
+ if (!cache || specName === '(unknown)') {
32
+ return Spec.parse(specName, bareSpec, options);
33
+ }
34
+ const key = `${cache.prefix}\0${specName}\0${bareSpec}`;
35
+ const cached = cache.specCache.get(key);
36
+ if (cached)
37
+ return cached;
38
+ const spec = Spec.parse(specName, bareSpec, options);
39
+ cache.specCache.set(key, spec);
40
+ return spec;
41
+ };
42
+ export const loadEdges = (graph, edges, options, cache) => {
30
43
  const entries = Object.entries(edges);
31
44
  const edgeCount = entries.length;
32
45
  // Only use optimizations for non-trivial graphs
@@ -93,7 +106,7 @@ export const loadEdges = (graph, edges, options) => {
93
106
  // loaded), so overriding here would always inject `undefined`
94
107
  // and silently revert to the default npm registry.
95
108
  // See vltpkg/vltpkg#1580.
96
- const spec = Spec.parse(specName, valRest.substring(0, vrSplit), options);
109
+ const spec = parseEdgeSpec(specName, valRest.substring(0, vrSplit), options, cache);
97
110
  if (useOptimizations) {
98
111
  edgeProcessingQueue.push({
99
112
  fromNode,
@@ -73,6 +73,7 @@ export const loadNodes = (graph, nodes, options, actual, throwOnMissingManifest)
73
73
  node.integrity = integrity ?? referenceNode?.integrity;
74
74
  node.resolved =
75
75
  type === 'remote' ? filepath : ((resolved ?? referenceNode?.resolved));
76
+ node.resolvedFromLockfile = !!(node.integrity && node.resolved);
76
77
  node.projectRoot = graph.projectRoot;
77
78
  if (!node.resolved)
78
79
  node.setResolved();
@@ -4,7 +4,7 @@ import { Graph } from '../graph.ts';
4
4
  import type { PathScurry } from 'path-scurry';
5
5
  import type { NormalizedManifest } from '@vltpkg/types';
6
6
  import type { SpecOptions } from '@vltpkg/spec';
7
- import type { LockfileData } from './types.ts';
7
+ import type { LockfileData, SpecCache } from './types.ts';
8
8
  import type { GraphModifier } from '../modifiers.ts';
9
9
  export type LoadOptions = SpecOptions & {
10
10
  /**
@@ -39,7 +39,16 @@ export type LoadOptions = SpecOptions & {
39
39
  * Whether to throw an error if a manifest is missing when loading nodes.
40
40
  */
41
41
  throwOnMissingManifest?: boolean;
42
+ /**
43
+ * Shared intern cache for parsed lockfile specs.
44
+ */
45
+ specCache?: SpecCache;
46
+ /**
47
+ * Already-parsed lockfile data. When set, skips the filesystem read.
48
+ */
49
+ lockfileData?: LockfileData;
42
50
  };
51
+ export declare const loadData: (projectRoot: string, lockfilePath?: string) => LockfileData;
43
52
  export declare const load: (options: LoadOptions) => Graph;
44
53
  export declare const loadHidden: (options: LoadOptions) => Graph;
45
54
  export declare const loadObject: (options: LoadOptions, lockfileData: Omit<LockfileData, "options" | "lockfileVersion"> & Partial<Pick<LockfileData, "options" | "lockfileVersion">>) => Graph;
@@ -12,9 +12,11 @@ import { LOCKFILE_VERSION } from "./types.js";
12
12
  const loadLockfile = (projectRoot, lockfilePath) => JSON.parse(readFileSync(resolve(projectRoot, lockfilePath), {
13
13
  encoding: 'utf8',
14
14
  }));
15
+ export const loadData = (projectRoot, lockfilePath = 'vlt-lock.json') => loadLockfile(projectRoot, lockfilePath);
15
16
  export const load = (options) => {
16
17
  const { projectRoot } = options;
17
- return loadObject(options, loadLockfile(projectRoot, 'vlt-lock.json'));
18
+ return loadObject(options, options.lockfileData ??
19
+ loadLockfile(projectRoot, 'vlt-lock.json'));
18
20
  };
19
21
  export const loadHidden = (options) => {
20
22
  const { projectRoot } = options;
@@ -159,7 +161,14 @@ export const loadObject = (options, lockfileData) => {
159
161
  });
160
162
  graph.optionsChanged = optionsChanged;
161
163
  loadNodes(graph, lockfileData.nodes, mergedOptions, options.actual, options.throwOnMissingManifest);
162
- loadEdges(graph, lockfileData.edges, mergedOptions);
164
+ // Skip interning when modifiers are active — they mutate Spec.overridden
165
+ const specCache = options.modifiers || !options.specCache ?
166
+ undefined
167
+ : {
168
+ specCache: options.specCache,
169
+ prefix: JSON.stringify(lockfileOptions),
170
+ };
171
+ loadEdges(graph, lockfileData.edges, mergedOptions, specCache);
163
172
  // hydrate missing node-level registry data
164
173
  for (const node of graph.nodes.values()) {
165
174
  const [firstEdge] = node.edgesIn;
@@ -25,6 +25,12 @@ export type LockfileData = {
25
25
  nodes: Record<DepID, LockfileNode>;
26
26
  edges: LockfileEdges;
27
27
  };
28
+ /**
29
+ * Per-install intern cache for parsed lockfile specs. Shared across
30
+ * hidden + main lockfile loads so identical (name, bareSpec) pairs
31
+ * reuse one Spec instance.
32
+ */
33
+ export type SpecCache = Map<string, Spec>;
28
34
  /**
29
35
  * Current lockfile format version.
30
36
  * Only lockfiles with matching version are considered valid.
package/dist/node.d.ts CHANGED
@@ -76,6 +76,11 @@ export declare class Node implements NodeLike {
76
76
  * The manifest integrity value.
77
77
  */
78
78
  integrity?: Integrity;
79
+ /**
80
+ * True when `integrity` and `resolved` came from a lockfile, i.e. they were
81
+ * verified by the install that wrote them.
82
+ */
83
+ resolvedFromLockfile: boolean;
79
84
  /**
80
85
  * The manifest this node represents in the graph.
81
86
  */
package/dist/node.js CHANGED
@@ -101,6 +101,11 @@ export class Node {
101
101
  * The manifest integrity value.
102
102
  */
103
103
  integrity;
104
+ /**
105
+ * True when `integrity` and `resolved` came from a lockfile, i.e. they were
106
+ * verified by the install that wrote them.
107
+ */
108
+ resolvedFromLockfile = false;
104
109
  /**
105
110
  * The manifest this node represents in the graph.
106
111
  */
@@ -38,15 +38,17 @@ export const extractNode = async (node, scurry, remover, options, packageInfo, d
38
38
  error: new Error('Platform check failed or package deprecated'),
39
39
  };
40
40
  }
41
+ const extractOptions = {
42
+ from,
43
+ integrity,
44
+ resolved,
45
+ fromLockfile: node.resolvedFromLockfile,
46
+ };
41
47
  try {
42
48
  await remover.rm(target);
43
49
  if (removeOptionalFailedNode) {
44
50
  try {
45
- const result = await packageInfo.extract(spec, target, {
46
- from,
47
- integrity,
48
- resolved,
49
- });
51
+ const result = await packageInfo.extract(spec, target, extractOptions);
50
52
  // Store computed integrity for git/remote deps
51
53
  if (result.integrity && !node.integrity) {
52
54
  node.integrity = result.integrity;
@@ -59,11 +61,7 @@ export const extractNode = async (node, scurry, remover, options, packageInfo, d
59
61
  }
60
62
  }
61
63
  else {
62
- const result = await packageInfo.extract(spec, target, {
63
- from,
64
- integrity,
65
- resolved,
66
- });
64
+ const result = await packageInfo.extract(spec, target, extractOptions);
67
65
  // Store computed integrity for git/remote deps
68
66
  if (result.integrity && !node.integrity) {
69
67
  node.integrity = result.integrity;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vltpkg/graph",
3
3
  "description": "A library that helps understanding & expressing what happens on an install",
4
- "version": "1.0.2",
4
+ "version": "1.0.4",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/vltpkg/vltpkg.git",
@@ -13,26 +13,26 @@
13
13
  "url": "http://vlt.sh"
14
14
  },
15
15
  "dependencies": {
16
- "@vltpkg/cmd-shim": "1.0.2",
17
- "@vltpkg/dep-id": "1.0.2",
18
- "@vltpkg/dss-breadcrumb": "1.0.2",
19
- "@vltpkg/error-cause": "1.0.2",
20
- "@vltpkg/fast-split": "1.0.2",
21
- "@vltpkg/graph-run": "1.0.2",
22
- "@vltpkg/init": "1.0.2",
23
- "@vltpkg/output": "1.0.2",
24
- "@vltpkg/package-info": "1.0.2",
25
- "@vltpkg/package-json": "1.0.2",
26
- "@vltpkg/pick-manifest": "1.0.2",
27
- "@vltpkg/query": "1.0.2",
28
- "@vltpkg/rollback-remove": "1.0.2",
29
- "@vltpkg/run": "1.0.2",
30
- "@vltpkg/satisfies": "1.0.2",
31
- "@vltpkg/security-archive": "1.0.2",
32
- "@vltpkg/spec": "1.0.2",
33
- "@vltpkg/types": "1.0.2",
34
- "@vltpkg/vlt-json": "1.0.2",
35
- "@vltpkg/workspaces": "1.0.2",
16
+ "@vltpkg/cmd-shim": "1.0.4",
17
+ "@vltpkg/dep-id": "1.0.4",
18
+ "@vltpkg/dss-breadcrumb": "1.0.4",
19
+ "@vltpkg/error-cause": "1.0.4",
20
+ "@vltpkg/fast-split": "1.0.4",
21
+ "@vltpkg/graph-run": "1.0.4",
22
+ "@vltpkg/init": "1.0.4",
23
+ "@vltpkg/output": "1.0.4",
24
+ "@vltpkg/package-info": "1.0.4",
25
+ "@vltpkg/package-json": "1.0.4",
26
+ "@vltpkg/pick-manifest": "1.0.4",
27
+ "@vltpkg/query": "1.0.4",
28
+ "@vltpkg/rollback-remove": "1.0.4",
29
+ "@vltpkg/run": "1.0.4",
30
+ "@vltpkg/satisfies": "1.0.4",
31
+ "@vltpkg/security-archive": "1.0.4",
32
+ "@vltpkg/spec": "1.0.4",
33
+ "@vltpkg/types": "1.0.4",
34
+ "@vltpkg/vlt-json": "1.0.4",
35
+ "@vltpkg/workspaces": "1.0.4",
36
36
  "path-scurry": "^2.0.1",
37
37
  "promise-call-limit": "^3.0.2"
38
38
  },