@vltpkg/graph 1.0.0-rc.3 → 1.0.0-rc.31

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.
Files changed (227) hide show
  1. package/README.md +101 -22
  2. package/dist/{esm/actual → actual}/load.d.ts +8 -1
  3. package/dist/{esm/actual → actual}/load.js +57 -22
  4. package/dist/{esm/browser.d.ts → browser.d.ts} +0 -1
  5. package/dist/{esm/browser.js → browser.js} +0 -1
  6. package/dist/{esm/build.d.ts → build.d.ts} +0 -1
  7. package/dist/{esm/build.js → build.js} +0 -1
  8. package/dist/{esm/dependencies.d.ts → dependencies.d.ts} +0 -1
  9. package/dist/{esm/dependencies.js → dependencies.js} +0 -1
  10. package/dist/{esm/diff.d.ts → diff.d.ts} +4 -1
  11. package/dist/{esm/diff.js → diff.js} +5 -2
  12. package/dist/{esm/edge.d.ts → edge.d.ts} +1 -1
  13. package/dist/{esm/edge.js → edge.js} +4 -1
  14. package/dist/fixup-added-names.d.ts +18 -0
  15. package/dist/fixup-added-names.js +46 -0
  16. package/dist/{esm/graph.d.ts → graph.d.ts} +25 -5
  17. package/dist/{esm/graph.js → graph.js} +92 -43
  18. package/dist/ideal/append-nodes.d.ts +31 -0
  19. package/dist/ideal/append-nodes.js +560 -0
  20. package/dist/{esm/ideal → ideal}/build-ideal-from-starting-graph.d.ts +4 -5
  21. package/dist/ideal/build-ideal-from-starting-graph.js +69 -0
  22. package/dist/{esm/ideal → ideal}/build.d.ts +0 -1
  23. package/dist/ideal/build.js +84 -0
  24. package/dist/{esm/ideal → ideal}/get-importer-specs.d.ts +9 -3
  25. package/dist/{esm/ideal → ideal}/get-importer-specs.js +80 -6
  26. package/dist/ideal/peers.d.ts +160 -0
  27. package/dist/ideal/peers.js +696 -0
  28. package/dist/ideal/refresh-ideal-graph.d.ts +43 -0
  29. package/dist/ideal/refresh-ideal-graph.js +62 -0
  30. package/dist/{esm/ideal → ideal}/remove-satisfied-specs.d.ts +0 -1
  31. package/dist/{esm/ideal → ideal}/remove-satisfied-specs.js +8 -1
  32. package/dist/ideal/sorting.d.ts +45 -0
  33. package/dist/ideal/sorting.js +70 -0
  34. package/dist/ideal/types.d.ts +107 -0
  35. package/dist/ideal/types.js +1 -0
  36. package/dist/{esm/index.d.ts → index.d.ts} +1 -1
  37. package/dist/{esm/index.js → index.js} +1 -1
  38. package/dist/{esm/install.d.ts → install.d.ts} +3 -3
  39. package/dist/{esm/install.js → install.js} +49 -9
  40. package/dist/{esm/lockfile → lockfile}/load-edges.d.ts +0 -1
  41. package/dist/{esm/lockfile → lockfile}/load-edges.js +7 -4
  42. package/dist/{esm/lockfile → lockfile}/load-nodes.d.ts +0 -1
  43. package/dist/{esm/lockfile → lockfile}/load-nodes.js +10 -4
  44. package/dist/{esm/lockfile → lockfile}/load.d.ts +0 -5
  45. package/dist/{esm/lockfile → lockfile}/load.js +31 -33
  46. package/dist/{esm/lockfile → lockfile}/save.d.ts +1 -2
  47. package/dist/{esm/lockfile → lockfile}/save.js +8 -7
  48. package/dist/{esm/lockfile → lockfile}/types.d.ts +7 -1
  49. package/dist/{esm/lockfile → lockfile}/types.js +6 -1
  50. package/dist/{esm/modifiers.d.ts → modifiers.d.ts} +0 -1
  51. package/dist/{esm/modifiers.js → modifiers.js} +0 -1
  52. package/dist/{esm/node.d.ts → node.d.ts} +16 -1
  53. package/dist/{esm/node.js → node.js} +21 -1
  54. package/dist/{esm/non-empty-list.d.ts → non-empty-list.d.ts} +0 -1
  55. package/dist/{esm/non-empty-list.js → non-empty-list.js} +0 -1
  56. package/dist/{esm/reify → reify}/add-edge.d.ts +0 -1
  57. package/dist/{esm/reify → reify}/add-edge.js +10 -4
  58. package/dist/{esm/reify → reify}/add-edges.d.ts +1 -2
  59. package/dist/{esm/reify → reify}/add-edges.js +2 -2
  60. package/dist/{esm/reify → reify}/add-nodes.d.ts +0 -1
  61. package/dist/{esm/reify → reify}/add-nodes.js +0 -1
  62. package/dist/{esm/reify → reify}/bin-chmod.d.ts +0 -1
  63. package/dist/{esm/reify → reify}/bin-chmod.js +0 -1
  64. package/dist/{esm/reify → reify}/build.d.ts +0 -1
  65. package/dist/{esm/reify → reify}/build.js +12 -4
  66. package/dist/{esm/reify → reify}/calculate-save-value.d.ts +0 -1
  67. package/dist/{esm/reify → reify}/calculate-save-value.js +6 -1
  68. package/dist/{esm/reify → reify}/check-needed-build.d.ts +10 -1
  69. package/dist/reify/check-needed-build.js +71 -0
  70. package/dist/{esm/reify → reify}/delete-edge.d.ts +0 -1
  71. package/dist/{esm/reify → reify}/delete-edge.js +0 -1
  72. package/dist/{esm/reify → reify}/delete-edges.d.ts +0 -1
  73. package/dist/{esm/reify → reify}/delete-edges.js +0 -1
  74. package/dist/{esm/reify → reify}/delete-nodes.d.ts +0 -1
  75. package/dist/{esm/reify → reify}/delete-nodes.js +0 -1
  76. package/dist/{esm/reify → reify}/extract-node.d.ts +0 -1
  77. package/dist/{esm/reify → reify}/extract-node.js +10 -3
  78. package/dist/{esm/reify → reify}/index.d.ts +1 -1
  79. package/dist/{esm/reify → reify}/index.js +4 -4
  80. package/dist/{esm/reify → reify}/internal-hoist.d.ts +0 -1
  81. package/dist/{esm/reify → reify}/internal-hoist.js +0 -1
  82. package/dist/{esm/reify → reify}/optional-fail.d.ts +0 -1
  83. package/dist/{esm/reify → reify}/optional-fail.js +0 -1
  84. package/dist/{esm/reify → reify}/rollback.d.ts +0 -1
  85. package/dist/{esm/reify → reify}/rollback.js +0 -1
  86. package/dist/{esm/reify → reify}/update-importers-package-json.d.ts +1 -2
  87. package/dist/{esm/reify → reify}/update-importers-package-json.js +19 -17
  88. package/dist/{esm/remove-optional-subgraph.d.ts → remove-optional-subgraph.d.ts} +0 -1
  89. package/dist/{esm/remove-optional-subgraph.js → remove-optional-subgraph.js} +0 -1
  90. package/dist/{esm/resolve-save-type.d.ts → resolve-save-type.d.ts} +0 -1
  91. package/dist/{esm/resolve-save-type.js → resolve-save-type.js} +0 -1
  92. package/dist/{esm/stringify-node.d.ts → stringify-node.d.ts} +0 -1
  93. package/dist/{esm/stringify-node.js → stringify-node.js} +10 -2
  94. package/dist/{esm/transfer-data → transfer-data}/load.d.ts +0 -1
  95. package/dist/{esm/transfer-data → transfer-data}/load.js +5 -3
  96. package/dist/{esm/uninstall.d.ts → uninstall.d.ts} +0 -1
  97. package/dist/{esm/uninstall.js → uninstall.js} +27 -7
  98. package/dist/{esm/update.d.ts → update.d.ts} +0 -1
  99. package/dist/{esm/update.js → update.js} +11 -1
  100. package/dist/{esm/virtual-root.d.ts → virtual-root.d.ts} +0 -1
  101. package/dist/{esm/virtual-root.js → virtual-root.js} +0 -1
  102. package/dist/{esm/visualization → visualization}/human-readable-output.d.ts +0 -1
  103. package/dist/{esm/visualization → visualization}/human-readable-output.js +7 -3
  104. package/dist/{esm/visualization → visualization}/json-output.d.ts +2 -2
  105. package/dist/{esm/visualization → visualization}/json-output.js +2 -3
  106. package/dist/{esm/visualization → visualization}/mermaid-output.d.ts +2 -2
  107. package/dist/visualization/mermaid-output.js +170 -0
  108. package/dist/{esm/visualization → visualization}/object-like-output.d.ts +0 -1
  109. package/dist/{esm/visualization → visualization}/object-like-output.js +0 -1
  110. package/package.json +51 -63
  111. package/dist/esm/actual/load.d.ts.map +0 -1
  112. package/dist/esm/actual/load.js.map +0 -1
  113. package/dist/esm/browser.d.ts.map +0 -1
  114. package/dist/esm/browser.js.map +0 -1
  115. package/dist/esm/build.d.ts.map +0 -1
  116. package/dist/esm/build.js.map +0 -1
  117. package/dist/esm/dependencies.d.ts.map +0 -1
  118. package/dist/esm/dependencies.js.map +0 -1
  119. package/dist/esm/diff.d.ts.map +0 -1
  120. package/dist/esm/diff.js.map +0 -1
  121. package/dist/esm/edge.d.ts.map +0 -1
  122. package/dist/esm/edge.js.map +0 -1
  123. package/dist/esm/graph.d.ts.map +0 -1
  124. package/dist/esm/graph.js.map +0 -1
  125. package/dist/esm/ideal/add-nodes.d.ts +0 -34
  126. package/dist/esm/ideal/add-nodes.d.ts.map +0 -1
  127. package/dist/esm/ideal/add-nodes.js +0 -39
  128. package/dist/esm/ideal/add-nodes.js.map +0 -1
  129. package/dist/esm/ideal/append-nodes.d.ts +0 -19
  130. package/dist/esm/ideal/append-nodes.d.ts.map +0 -1
  131. package/dist/esm/ideal/append-nodes.js +0 -289
  132. package/dist/esm/ideal/append-nodes.js.map +0 -1
  133. package/dist/esm/ideal/build-ideal-from-starting-graph.d.ts.map +0 -1
  134. package/dist/esm/ideal/build-ideal-from-starting-graph.js +0 -55
  135. package/dist/esm/ideal/build-ideal-from-starting-graph.js.map +0 -1
  136. package/dist/esm/ideal/build.d.ts.map +0 -1
  137. package/dist/esm/ideal/build.js +0 -48
  138. package/dist/esm/ideal/build.js.map +0 -1
  139. package/dist/esm/ideal/get-importer-specs.d.ts.map +0 -1
  140. package/dist/esm/ideal/get-importer-specs.js.map +0 -1
  141. package/dist/esm/ideal/remove-nodes.d.ts +0 -7
  142. package/dist/esm/ideal/remove-nodes.d.ts.map +0 -1
  143. package/dist/esm/ideal/remove-nodes.js +0 -19
  144. package/dist/esm/ideal/remove-nodes.js.map +0 -1
  145. package/dist/esm/ideal/remove-satisfied-specs.d.ts.map +0 -1
  146. package/dist/esm/ideal/remove-satisfied-specs.js.map +0 -1
  147. package/dist/esm/ideal/types.d.ts +0 -35
  148. package/dist/esm/ideal/types.d.ts.map +0 -1
  149. package/dist/esm/ideal/types.js +0 -2
  150. package/dist/esm/ideal/types.js.map +0 -1
  151. package/dist/esm/index.d.ts.map +0 -1
  152. package/dist/esm/index.js.map +0 -1
  153. package/dist/esm/install.d.ts.map +0 -1
  154. package/dist/esm/install.js.map +0 -1
  155. package/dist/esm/lockfile/load-edges.d.ts.map +0 -1
  156. package/dist/esm/lockfile/load-edges.js.map +0 -1
  157. package/dist/esm/lockfile/load-nodes.d.ts.map +0 -1
  158. package/dist/esm/lockfile/load-nodes.js.map +0 -1
  159. package/dist/esm/lockfile/load.d.ts.map +0 -1
  160. package/dist/esm/lockfile/load.js.map +0 -1
  161. package/dist/esm/lockfile/save.d.ts.map +0 -1
  162. package/dist/esm/lockfile/save.js.map +0 -1
  163. package/dist/esm/lockfile/types.d.ts.map +0 -1
  164. package/dist/esm/lockfile/types.js.map +0 -1
  165. package/dist/esm/modifiers.d.ts.map +0 -1
  166. package/dist/esm/modifiers.js.map +0 -1
  167. package/dist/esm/node.d.ts.map +0 -1
  168. package/dist/esm/node.js.map +0 -1
  169. package/dist/esm/non-empty-list.d.ts.map +0 -1
  170. package/dist/esm/non-empty-list.js.map +0 -1
  171. package/dist/esm/package.json +0 -3
  172. package/dist/esm/reify/add-edge.d.ts.map +0 -1
  173. package/dist/esm/reify/add-edge.js.map +0 -1
  174. package/dist/esm/reify/add-edges.d.ts.map +0 -1
  175. package/dist/esm/reify/add-edges.js.map +0 -1
  176. package/dist/esm/reify/add-nodes.d.ts.map +0 -1
  177. package/dist/esm/reify/add-nodes.js.map +0 -1
  178. package/dist/esm/reify/bin-chmod.d.ts.map +0 -1
  179. package/dist/esm/reify/bin-chmod.js.map +0 -1
  180. package/dist/esm/reify/build.d.ts.map +0 -1
  181. package/dist/esm/reify/build.js.map +0 -1
  182. package/dist/esm/reify/calculate-save-value.d.ts.map +0 -1
  183. package/dist/esm/reify/calculate-save-value.js.map +0 -1
  184. package/dist/esm/reify/check-needed-build.d.ts.map +0 -1
  185. package/dist/esm/reify/check-needed-build.js +0 -50
  186. package/dist/esm/reify/check-needed-build.js.map +0 -1
  187. package/dist/esm/reify/delete-edge.d.ts.map +0 -1
  188. package/dist/esm/reify/delete-edge.js.map +0 -1
  189. package/dist/esm/reify/delete-edges.d.ts.map +0 -1
  190. package/dist/esm/reify/delete-edges.js.map +0 -1
  191. package/dist/esm/reify/delete-nodes.d.ts.map +0 -1
  192. package/dist/esm/reify/delete-nodes.js.map +0 -1
  193. package/dist/esm/reify/extract-node.d.ts.map +0 -1
  194. package/dist/esm/reify/extract-node.js.map +0 -1
  195. package/dist/esm/reify/index.d.ts.map +0 -1
  196. package/dist/esm/reify/index.js.map +0 -1
  197. package/dist/esm/reify/internal-hoist.d.ts.map +0 -1
  198. package/dist/esm/reify/internal-hoist.js.map +0 -1
  199. package/dist/esm/reify/optional-fail.d.ts.map +0 -1
  200. package/dist/esm/reify/optional-fail.js.map +0 -1
  201. package/dist/esm/reify/rollback.d.ts.map +0 -1
  202. package/dist/esm/reify/rollback.js.map +0 -1
  203. package/dist/esm/reify/update-importers-package-json.d.ts.map +0 -1
  204. package/dist/esm/reify/update-importers-package-json.js.map +0 -1
  205. package/dist/esm/remove-optional-subgraph.d.ts.map +0 -1
  206. package/dist/esm/remove-optional-subgraph.js.map +0 -1
  207. package/dist/esm/resolve-save-type.d.ts.map +0 -1
  208. package/dist/esm/resolve-save-type.js.map +0 -1
  209. package/dist/esm/stringify-node.d.ts.map +0 -1
  210. package/dist/esm/stringify-node.js.map +0 -1
  211. package/dist/esm/transfer-data/load.d.ts.map +0 -1
  212. package/dist/esm/transfer-data/load.js.map +0 -1
  213. package/dist/esm/uninstall.d.ts.map +0 -1
  214. package/dist/esm/uninstall.js.map +0 -1
  215. package/dist/esm/update.d.ts.map +0 -1
  216. package/dist/esm/update.js.map +0 -1
  217. package/dist/esm/virtual-root.d.ts.map +0 -1
  218. package/dist/esm/virtual-root.js.map +0 -1
  219. package/dist/esm/visualization/human-readable-output.d.ts.map +0 -1
  220. package/dist/esm/visualization/human-readable-output.js.map +0 -1
  221. package/dist/esm/visualization/json-output.d.ts.map +0 -1
  222. package/dist/esm/visualization/json-output.js.map +0 -1
  223. package/dist/esm/visualization/mermaid-output.d.ts.map +0 -1
  224. package/dist/esm/visualization/mermaid-output.js +0 -123
  225. package/dist/esm/visualization/mermaid-output.js.map +0 -1
  226. package/dist/esm/visualization/object-like-output.d.ts.map +0 -1
  227. package/dist/esm/visualization/object-like-output.js.map +0 -1
@@ -0,0 +1,84 @@
1
+ import { error } from '@vltpkg/error-cause';
2
+ import { graphStep } from '@vltpkg/output';
3
+ import { load as loadActual } from "../actual/load.js";
4
+ import { load as loadVirtual } from "../lockfile/load.js";
5
+ import { buildIdealFromStartingGraph } from "./build-ideal-from-starting-graph.js";
6
+ import { splitDepID } from '@vltpkg/dep-id';
7
+ const getMap = (m) => m ?? new Map();
8
+ /**
9
+ * Validates that a file dependency node exists in the graph after
10
+ * a successful graph build. This helps providing an actionable error
11
+ * message in case the current working directory is a linked nested
12
+ * folder that hasn't yet been added as a dependency to an importer
13
+ * in the project.
14
+ */
15
+ const validateFileDepNode = (graph, id) => {
16
+ const [type, path] = splitDepID(id);
17
+ if (type === 'file' && !graph.nodes.get(id)) {
18
+ throw error('The current working dir is not a dependency of this project', { path });
19
+ }
20
+ };
21
+ /**
22
+ * Builds an ideal {@link Graph} representing the dependencies that
23
+ * should be present in order to fulfill the requirements defined
24
+ * by the `package.json` and `vlt-lock.json` files using either the
25
+ * virtual or actual graph as a starting point. Also add / remove any
26
+ * dependencies listed in the `add` and `remove` properties.
27
+ */
28
+ export const build = async (options) => {
29
+ const done = graphStep('build');
30
+ // Creates the shared instances that are going to be used
31
+ // in both the loader methods and the build graph
32
+ const { packageInfo, packageJson, scurry, monorepo } = options;
33
+ const mainManifest = options.mainManifest ?? packageJson.read(options.projectRoot);
34
+ let graph;
35
+ try {
36
+ graph = loadVirtual({
37
+ ...options,
38
+ mainManifest,
39
+ monorepo,
40
+ });
41
+ }
42
+ catch (err) {
43
+ // Check if this is a lockfile version mismatch
44
+ const cause = err.cause;
45
+ if (cause?.code === 'ELOCKFILEVERSION') {
46
+ // If lockfile is from a different vlt version, hard fail
47
+ if (typeof cause.found === 'number' &&
48
+ typeof cause.wanted === 'number') {
49
+ throw err;
50
+ }
51
+ // TODO: add warning to CLI layer via @vltpkg/output when available
52
+ }
53
+ graph = loadActual({
54
+ ...options,
55
+ mainManifest,
56
+ monorepo,
57
+ });
58
+ }
59
+ const res = await buildIdealFromStartingGraph({
60
+ ...options,
61
+ scurry,
62
+ add: getMap(options.add),
63
+ graph,
64
+ packageInfo,
65
+ remove: getMap(options.remove),
66
+ actual: options.actual,
67
+ });
68
+ done();
69
+ // when adding or removing a new dependency from a file dep,
70
+ // validate the receiver node was present in the graph
71
+ if (options.add) {
72
+ for (const [addKey, value] of options.add.entries()) {
73
+ if (value.size)
74
+ validateFileDepNode(res, addKey);
75
+ }
76
+ }
77
+ if (options.remove) {
78
+ for (const [removeKey, value] of options.remove.entries()) {
79
+ if (value.size)
80
+ validateFileDepNode(res, removeKey);
81
+ }
82
+ }
83
+ return res;
84
+ };
@@ -1,7 +1,12 @@
1
1
  import type { AddImportersDependenciesMap, RemoveImportersDependenciesMap } from '../dependencies.ts';
2
- import type { BuildIdealAddOptions, BuildIdealFromGraphOptions, BuildIdealRemoveOptions } from './types.ts';
2
+ import type { BuildIdealAddOptions, BuildIdealFromGraphOptions, BuildIdealRemoveOptions, TransientAddMap, TransientRemoveMap } from './types.ts';
3
3
  import type { SpecOptions } from '@vltpkg/spec';
4
- export type GetImporterSpecsOptions = BuildIdealAddOptions & BuildIdealFromGraphOptions & BuildIdealRemoveOptions & SpecOptions;
4
+ import type { PackageJson } from '@vltpkg/package-json';
5
+ import type { PathScurry } from 'path-scurry';
6
+ export type GetImporterSpecsOptions = BuildIdealAddOptions & BuildIdealFromGraphOptions & BuildIdealRemoveOptions & SpecOptions & {
7
+ scurry: PathScurry;
8
+ packageJson: PackageJson;
9
+ };
5
10
  /**
6
11
  * Given a {@link Graph} and a list of {@link Dependency}, merges the
7
12
  * dependencies info found in the graph importers and returns the add & remove
@@ -10,5 +15,6 @@ export type GetImporterSpecsOptions = BuildIdealAddOptions & BuildIdealFromGraph
10
15
  export declare const getImporterSpecs: (options: GetImporterSpecsOptions) => {
11
16
  add: AddImportersDependenciesMap;
12
17
  remove: RemoveImportersDependenciesMap;
18
+ transientAdd: TransientAddMap;
19
+ transientRemove: TransientRemoveMap;
13
20
  };
14
- //# sourceMappingURL=get-importer-specs.d.ts.map
@@ -1,4 +1,3 @@
1
- import { error } from '@vltpkg/error-cause';
2
1
  import { longDependencyTypes } from '@vltpkg/types';
3
2
  import { shorten, asDependency } from "../dependencies.js";
4
3
  import { removeSatisfiedSpecs } from "./remove-satisfied-specs.js";
@@ -47,23 +46,85 @@ export const getImporterSpecs = (options) => {
47
46
  const deps = Object.entries(importer.manifest?.[depType] ?? {});
48
47
  for (const [depName, depSpec] of deps) {
49
48
  const edge = importer.edgesOut.get(depName);
49
+ // skip if the edge exists and already uses the same spec
50
+ if (edge?.to && depSpec === edge.spec.bareSpec)
51
+ continue;
50
52
  const dependency = asDependency({
51
53
  spec: Spec.parse(depName, depSpec, options),
52
54
  type: shorten(depType, depName, importer.manifest),
53
55
  });
54
- if (!edge?.to) {
55
- addDeps.set(depName, dependency);
56
- }
56
+ addDeps.set(depName, dependency);
57
57
  }
58
58
  }
59
59
  addResult.set(importer.id, addDeps);
60
60
  removeResult.set(importer.id, removeDeps);
61
61
  }
62
+ // Maps to store dependencies targeting non-importer nodes (e.g., nested folders)
63
+ // These will be injected when the target node is placed in the graph
64
+ const transientAdd = new Map();
65
+ const transientRemove = new Map();
66
+ // Traverse all nodes in the graph to find file type dependencies that are directories
67
+ // and populate transientAdd/transientRemove with their manifest dependencies
68
+ // Only process when scurry and packageJson are available
69
+ for (const node of graph.nodes.values()) {
70
+ // Skip importers as they're already handled above and also skip
71
+ // any non-file type dependencies
72
+ if (graph.importers.has(node) || !node.id.startsWith('file'))
73
+ continue;
74
+ // check if this is a file type dependency that is a directory
75
+ const nodePath = options.scurry.cwd.resolve(node.location);
76
+ const stat = nodePath.lstatSync();
77
+ if (stat?.isDirectory()) {
78
+ // load the manifest for this directory (throw if it does not exist)
79
+ const manifest = options.packageJson.read(nodePath.fullpath());
80
+ // should always set the manifest to the read manifest
81
+ node.manifest = manifest;
82
+ // create a map of dependencies from the manifest
83
+ const addDeps = new Map();
84
+ // check for edges not in manifest (should be removed)
85
+ const removeDeps = new Set();
86
+ for (const edge of node.edgesOut.values()) {
87
+ if (!hasDepName(node, edge) &&
88
+ !add.get(node.id)?.has(edge.name)) {
89
+ removeDeps.add(edge.name);
90
+ }
91
+ }
92
+ // iterate over manifest dependencies to add them if
93
+ // they're missing from the graph
94
+ for (const depType of longDependencyTypes) {
95
+ const deps = Object.entries(manifest[depType] ?? {});
96
+ for (const [depName, depSpec] of deps) {
97
+ const edge = node.edgesOut.get(depName);
98
+ // skip if the edge exists and already uses the same spec
99
+ if (edge?.to && depSpec === edge.spec.bareSpec)
100
+ continue;
101
+ // add the dependency to the addDeps map
102
+ const dependency = asDependency({
103
+ spec: Spec.parse(depName, depSpec, options),
104
+ type: shorten(depType, depName, manifest),
105
+ });
106
+ addDeps.set(depName, dependency);
107
+ }
108
+ }
109
+ // store in transientAdd if there are any dependencies
110
+ if (addDeps.size > 0) {
111
+ transientAdd.set(node.id, addDeps);
112
+ }
113
+ // store in transientRemove if there are any to remove
114
+ if (removeDeps.size > 0) {
115
+ transientRemove.set(node.id, removeDeps);
116
+ }
117
+ }
118
+ }
62
119
  // merges any provided specs to add to the current found results
63
120
  for (const [id, addDeps] of add.entries()) {
64
121
  const deps = addResult.get(id);
65
122
  if (!deps) {
66
- throw error('Not an importer', { found: id });
123
+ // Not an importer - only store file-type deps for later injection
124
+ if (id.startsWith('file')) {
125
+ transientAdd.set(id, addDeps);
126
+ }
127
+ continue;
67
128
  }
68
129
  for (const [name, dep] of addDeps.entries()) {
69
130
  deps.set(name, dep);
@@ -78,6 +139,18 @@ export const getImporterSpecs = (options) => {
78
139
  importerRemoveItem.add(depName);
79
140
  }
80
141
  }
142
+ else if (key.startsWith('file')) {
143
+ // Not an importer - only store file-type deps in transientRemove
144
+ const existing = transientRemove.get(key);
145
+ if (existing) {
146
+ for (const depName of removeSet) {
147
+ existing.add(depName);
148
+ }
149
+ }
150
+ else {
151
+ transientRemove.set(key, new Set(removeSet));
152
+ }
153
+ }
81
154
  }
82
155
  // Removes any references to an importer that no longer has specs
83
156
  for (const [key, removeItem] of removeResult) {
@@ -101,6 +174,7 @@ export const getImporterSpecs = (options) => {
101
174
  return {
102
175
  add: addResult,
103
176
  remove: removeResult,
177
+ transientAdd,
178
+ transientRemove,
104
179
  };
105
180
  };
106
- //# sourceMappingURL=get-importer-specs.js.map
@@ -0,0 +1,160 @@
1
+ import { Spec } from '@vltpkg/spec';
2
+ import type { PeerContext, PeerContextEntry, PeerContextEntryInput, ProcessPlacementResult } from './types.ts';
3
+ import type { SpecOptions } from '@vltpkg/spec';
4
+ import type { DependencySaveType, Manifest } from '@vltpkg/types';
5
+ import type { Monorepo } from '@vltpkg/workspaces';
6
+ import type { Dependency } from '../dependencies.ts';
7
+ import type { Graph } from '../graph.ts';
8
+ import type { Node } from '../node.ts';
9
+ /**
10
+ * Result of checking if an existing node's peer edges are compatible
11
+ * with a new parent's context. The `forkEntry` property is optional
12
+ * and will only be present if the node's peer edges are incompatible.
13
+ */
14
+ type PeerEdgeCompatResult = {
15
+ compatible: boolean;
16
+ /** When incompatible, entry to add to forked context (target always present) */
17
+ forkEntry?: PeerContextEntryInput & {
18
+ target: Node;
19
+ };
20
+ };
21
+ /**
22
+ * Check if an existing node's peer edges would still resolve to the same
23
+ * targets from a new parent's context. Returns incompatible info if any
24
+ * peer would resolve differently, meaning the node should NOT be reused.
25
+ *
26
+ * This is crucial for avoiding incorrect node reuse that would break peer
27
+ * dependency contracts. Three sources of conflict are checked:
28
+ *
29
+ * 1. **Peer context entries**: The global peer context may have resolved a
30
+ * different version of a peer dependency than what the existing node expects.
31
+ *
32
+ * 2. **Already-placed siblings**: The parent node may already have an edge to
33
+ * a different version of the peer dependency.
34
+ *
35
+ * 3. **Not-yet-placed siblings**: The parent's manifest declares a dependency
36
+ * on the same package, and there's a graph node that would satisfy it but
37
+ * differs from what the existing node expects.
38
+ */
39
+ export declare const checkPeerEdgesCompatible: (existingNode: Node, fromNode: Node, peerContext: PeerContext, graph: Graph) => PeerEdgeCompatResult;
40
+ /**
41
+ * Retrieve a unique hash value for a given peer context set.
42
+ */
43
+ export declare const retrievePeerContextHash: (peerContext: PeerContext | undefined) => string | undefined;
44
+ /**
45
+ * Checks if a given spec is compatible with the specs already
46
+ * assigned to a peer context entry.
47
+ *
48
+ * Returns true if INCOMPATIBLE, false if compatible.
49
+ *
50
+ * Compatibility rules:
51
+ * - **Registry specs**: Uses semver range intersection. `^18.0.0` and `^18.2.0`
52
+ * intersect (compatible), but `^18.0.0` and `^19.0.0` don't (incompatible).
53
+ * - **Non-registry specs** (git, file, etc.): Requires exact bareSpec match.
54
+ * `github:foo/bar#v1` only matches itself.
55
+ *
56
+ * This is used to determine when peer context forking is needed - if specs
57
+ * are incompatible, a new peer context must be created.
58
+ */
59
+ export declare const incompatibleSpecs: (spec: Spec, entry: PeerContextEntry) => boolean;
60
+ /**
61
+ * Sort peer context entry inputs for deterministic processing.
62
+ * Orders: non-peer dependencies first, then peer dependencies, alphabetically by name.
63
+ */
64
+ export declare const getOrderedPeerContextEntries: (entries: PeerContextEntryInput[]) => PeerContextEntryInput[];
65
+ export declare const checkEntriesToPeerContext: (peerContext: PeerContext, entries: PeerContextEntryInput[]) => boolean;
66
+ /**
67
+ * Add or update dependencies in a given peer context making sure to check
68
+ * for compatibility with existing dependencies already resolved by a given
69
+ * peer context set. Extra info such as a target or dependent nodes is
70
+ * optional.
71
+ *
72
+ * Returns true if forking is needed, false otherwise.
73
+ */
74
+ export declare const addEntriesToPeerContext: (peerContext: PeerContext, entries: PeerContextEntryInput[], fromNode: Node, monorepo?: Monorepo) => boolean;
75
+ /**
76
+ * Create and returns a forked copy of a given peer context set.
77
+ */
78
+ export declare const forkPeerContext: (graph: Graph, peerContext: PeerContext, entries: PeerContextEntryInput[]) => PeerContext;
79
+ /**
80
+ * Starts the peer dependency placement process
81
+ * for a given node being processed and placed.
82
+ */
83
+ export declare const startPeerPlacement: (peerContext: PeerContext, manifest: Manifest, fromNode: Node, options: SpecOptions) => {
84
+ peerSetHash: string | undefined;
85
+ queuedEntries: PeerContextEntryInput[];
86
+ };
87
+ /**
88
+ * Ends the peer dependency placement process, returning the functions that
89
+ * are going to be used to update the peer context set, forking when needed
90
+ * and resolving peer dependencies if possible.
91
+ *
92
+ * Returns two deferred functions:
93
+ * - `putEntries()`: Adds entries to peer context; returns fork entries if conflict
94
+ * - `resolvePeerDeps()`: Resolves peer deps from context/siblings or adds to nextDeps
95
+ *
96
+ * These are deferred (not executed immediately) so that all siblings at a level
97
+ * can be processed before peer context updates, enabling context reuse optimization.
98
+ */
99
+ export declare const endPeerPlacement: (peerContext: PeerContext, nextDeps: Dependency[], nextPeerDeps: Map<string, Dependency> & {
100
+ id?: number;
101
+ }, graph: Graph, spec: Spec, fromNode: Node, node: Node, type: DependencySaveType, queuedEntries: PeerContextEntryInput[]) => {
102
+ /**
103
+ * Add the new entries to the current peer context set.
104
+ *
105
+ * Two sets of entries are checked:
106
+ * - `prevEntries`: Parent's queued entries + self-reference
107
+ * - `nextEntries`: This node's deps + peer deps (with node as dependent)
108
+ *
109
+ * If either conflicts with the current context, returns ALL entries to be
110
+ * added to a forked context (prevEntries last for priority).
111
+ *
112
+ * Returns `undefined` if no fork needed (entries added directly to context).
113
+ */
114
+ putEntries: () => (PeerContextEntryInput | {
115
+ dependent: Node;
116
+ spec: import("@vltpkg/spec/browser").Spec;
117
+ type: DependencySaveType;
118
+ } | {
119
+ spec: Spec;
120
+ target: Node;
121
+ type: DependencySaveType;
122
+ })[] | undefined;
123
+ /**
124
+ * Try to resolve peer dependencies using already seen target
125
+ * values from the current peer context set.
126
+ *
127
+ * Resolution priority (highest to lowest):
128
+ * 1. Sibling deps from parent (workspace direct deps take priority)
129
+ * 2. Peer-edge closure of sibling targets (handles peer cycles)
130
+ * 3. Global peer context set entries
131
+ * 4. Add to nextDeps for normal resolution (or create dangling edge for optional)
132
+ */
133
+ resolvePeerDeps: () => void;
134
+ };
135
+ /**
136
+ * Given an array of processed results for the current level dependencies
137
+ * being placed in the currently building ideal graph, traverse its direct
138
+ * dependencies and track peer dependencies in their appropriate peer context
139
+ * sets, forking as needed and resolving peer dependencies using suitable
140
+ * nodes already present in the graph if possible.
141
+ *
142
+ * This is the core peer context management algorithm, executed after each
143
+ * BFS level. It runs in three phases:
144
+ *
145
+ * **Phase 1: Collect fork requirements**
146
+ * Call `putEntries()` on each child dep to add entries to peer context.
147
+ * Collect which children need forked contexts (due to conflicts).
148
+ *
149
+ * **Phase 2: Fork or reuse contexts**
150
+ * For children needing forks, try to reuse a sibling's forked context if
151
+ * compatible. This optimization reduces the number of peer contexts created.
152
+ *
153
+ * **Phase 3: Resolve peer deps**
154
+ * With contexts finalized, call `resolvePeerDeps()` to create edges for
155
+ * peers that can be satisfied from context/siblings, or add them to nextDeps.
156
+ *
157
+ * All operations are sorted by `node.id` for deterministic, reproducible builds.
158
+ */
159
+ export declare const postPlacementPeerCheck: (graph: Graph, sortedLevelResults: ProcessPlacementResult[]) => void;
160
+ export {};