submodule-version 2.3.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +103 -20
  2. package/dist/runner/handleErrors/handleErrors.d.ts +1 -1
  3. package/dist/runner/handleErrors/handleErrors.js +3 -10
  4. package/dist/runner/handleErrors/handleErrors.js.map +1 -1
  5. package/dist/runner/index.js +0 -0
  6. package/dist/runner/runner.d.ts +4 -0
  7. package/dist/runner/runner.js +91 -37
  8. package/dist/runner/runner.js.map +1 -1
  9. package/dist/src/PubGrub/PubGrub.d.ts +28 -0
  10. package/dist/src/PubGrub/PubGrub.js +366 -0
  11. package/dist/src/PubGrub/PubGrub.js.map +1 -0
  12. package/dist/src/PubGrub/index.d.ts +2 -0
  13. package/dist/src/PubGrub/index.js +7 -0
  14. package/dist/src/PubGrub/index.js.map +1 -0
  15. package/dist/src/PubGrub/types.d.ts +56 -0
  16. package/dist/src/PubGrub/types.js +3 -0
  17. package/dist/src/PubGrub/types.js.map +1 -0
  18. package/dist/src/entryStore.d.ts +16 -0
  19. package/dist/src/entryStore.js +152 -0
  20. package/dist/src/entryStore.js.map +1 -0
  21. package/dist/src/errors.d.ts +28 -0
  22. package/dist/src/errors.js +30 -0
  23. package/dist/src/errors.js.map +1 -0
  24. package/dist/src/git/api/GithubError.d.ts +4 -0
  25. package/dist/src/git/api/GithubError.js +11 -0
  26. package/dist/src/git/api/GithubError.js.map +1 -0
  27. package/dist/src/git/api/api.d.ts +8 -0
  28. package/dist/src/git/api/api.js +133 -0
  29. package/dist/src/git/api/api.js.map +1 -0
  30. package/dist/src/git/api/index.d.ts +3 -0
  31. package/dist/src/git/api/index.js +8 -0
  32. package/dist/src/git/api/index.js.map +1 -0
  33. package/dist/src/git/git.d.ts +30 -23
  34. package/dist/src/git/git.js +8 -258
  35. package/dist/src/git/git.js.map +1 -1
  36. package/dist/src/git/index.d.ts +1 -0
  37. package/dist/src/git/index.js +3 -1
  38. package/dist/src/git/index.js.map +1 -1
  39. package/dist/src/git/local/index.d.ts +1 -0
  40. package/dist/src/git/local/index.js +6 -0
  41. package/dist/src/git/local/index.js.map +1 -0
  42. package/dist/src/git/local/local.d.ts +26 -0
  43. package/dist/src/git/local/local.js +288 -0
  44. package/dist/src/git/local/local.js.map +1 -0
  45. package/dist/src/index.d.ts +7 -0
  46. package/dist/src/index.js +16 -1
  47. package/dist/src/index.js.map +1 -1
  48. package/dist/src/printError.d.ts +1 -0
  49. package/dist/src/printError.js +158 -0
  50. package/dist/src/printError.js.map +1 -0
  51. package/dist/src/sv.d.ts +18 -8
  52. package/dist/src/sv.js +235 -131
  53. package/dist/src/sv.js.map +1 -1
  54. package/dist/src/versionUtil/index.d.ts +2 -0
  55. package/dist/src/versionUtil/index.js +6 -0
  56. package/dist/src/versionUtil/index.js.map +1 -0
  57. package/dist/src/versionUtil/versionUtil.d.ts +16 -0
  58. package/dist/src/versionUtil/versionUtil.js +70 -0
  59. package/dist/src/versionUtil/versionUtil.js.map +1 -0
  60. package/package.json +4 -2
  61. package/runner/handleErrors/handleErrors.ts +3 -10
  62. package/runner/runner.ts +128 -46
  63. package/src/PubGrub/PubGrub.ts +578 -0
  64. package/src/PubGrub/__tests__/PubGrub.test.ts +351 -0
  65. package/src/PubGrub/index.ts +12 -0
  66. package/src/PubGrub/types.ts +72 -0
  67. package/src/__tests__/entryStore.test.ts +248 -0
  68. package/src/__tests__/printError.test.ts +148 -0
  69. package/src/__tests__/sv.test.ts +441 -0
  70. package/src/entryStore.ts +212 -0
  71. package/src/errors.ts +39 -0
  72. package/src/git/api/GithubError.ts +5 -0
  73. package/src/git/api/api.ts +180 -0
  74. package/src/git/api/index.ts +3 -0
  75. package/src/git/git.ts +8 -396
  76. package/src/git/index.ts +1 -0
  77. package/src/git/local/index.ts +1 -0
  78. package/src/git/local/local.ts +430 -0
  79. package/src/index.ts +17 -0
  80. package/src/printError.ts +189 -0
  81. package/src/sv.ts +321 -134
  82. package/src/versionUtil/__tests__/versionUtil.test.ts +62 -0
  83. package/src/versionUtil/index.ts +2 -0
  84. package/src/versionUtil/versionUtil.ts +103 -0
  85. package/tsconfig.json +1 -1
  86. package/dist/runner/handleErrors/handleBuildGraphError.d.ts +0 -1
  87. package/dist/runner/handleErrors/handleBuildGraphError.js +0 -42
  88. package/dist/runner/handleErrors/handleBuildGraphError.js.map +0 -1
  89. package/dist/runner/handleErrors/handleGitError.d.ts +0 -1
  90. package/dist/runner/handleErrors/handleGitError.js +0 -27
  91. package/dist/runner/handleErrors/handleGitError.js.map +0 -1
  92. package/dist/runner/handleErrors/handleInstallError.d.ts +0 -1
  93. package/dist/runner/handleErrors/handleInstallError.js +0 -30
  94. package/dist/runner/handleErrors/handleInstallError.js.map +0 -1
  95. package/dist/src/__tests__/versionUtil.test.d.ts +0 -1
  96. package/dist/src/__tests__/versionUtil.test.js +0 -80
  97. package/dist/src/__tests__/versionUtil.test.js.map +0 -1
  98. package/dist/src/buildGraph/GraphError.d.ts +0 -11
  99. package/dist/src/buildGraph/GraphError.js +0 -11
  100. package/dist/src/buildGraph/GraphError.js.map +0 -1
  101. package/dist/src/buildGraph/buildGraph.d.ts +0 -8
  102. package/dist/src/buildGraph/buildGraph.js +0 -100
  103. package/dist/src/buildGraph/buildGraph.js.map +0 -1
  104. package/dist/src/buildGraph/index.d.ts +0 -2
  105. package/dist/src/buildGraph/index.js +0 -8
  106. package/dist/src/buildGraph/index.js.map +0 -1
  107. package/dist/src/git/__tests__/parseGitUrl.test.d.ts +0 -1
  108. package/dist/src/git/__tests__/parseGitUrl.test.js +0 -30
  109. package/dist/src/git/__tests__/parseGitUrl.test.js.map +0 -1
  110. package/dist/src/versionUtil.d.ts +0 -13
  111. package/dist/src/versionUtil.js +0 -79
  112. package/dist/src/versionUtil.js.map +0 -1
  113. package/runner/handleErrors/handleBuildGraphError.ts +0 -49
  114. package/runner/handleErrors/handleGitError.ts +0 -28
  115. package/runner/handleErrors/handleInstallError.ts +0 -26
  116. package/src/__tests__/versionUtil.test.ts +0 -96
  117. package/src/buildGraph/GraphError.ts +0 -12
  118. package/src/buildGraph/buildGraph.ts +0 -150
  119. package/src/buildGraph/index.ts +0 -2
  120. package/src/versionUtil.ts +0 -97
@@ -0,0 +1,578 @@
1
+ import { entryStore } from '../entryStore';
2
+ import { SVError } from '../errors';
3
+ import { versionUtil } from '../versionUtil';
4
+ import {
5
+ ICircularConflict,
6
+ IEntrySource,
7
+ IIncompatibility,
8
+ IPackageEntry,
9
+ IRequirement,
10
+ ITerm,
11
+ IVersionConflict,
12
+ TConflict,
13
+ TDependencies,
14
+ TPubGrubResult,
15
+ } from './types';
16
+
17
+ interface ISolverState {
18
+ selected: Map<string, string>;
19
+ requirements: Map<string, IRequirement[]>;
20
+ edges: Map<string, string[]>;
21
+ }
22
+
23
+ type TSearchResult = {
24
+ ok: true;
25
+ state: ISolverState;
26
+ } | {
27
+ ok: false;
28
+ conflict: TConflict;
29
+ };
30
+
31
+ type TTermState = 'satisfied' | 'contradicted' | 'inconclusive';
32
+
33
+ const ROOT = '<root>';
34
+ const ROOT_VERSION = '1.0.0';
35
+
36
+ const cloneState = (state: ISolverState): ISolverState => ({
37
+ selected: new Map(state.selected),
38
+ requirements: new Map(
39
+ [...state.requirements].map(([name, requirements]) => [
40
+ name,
41
+ requirements.map(requirement => ({
42
+ ...requirement,
43
+ chain: [...requirement.chain],
44
+ })),
45
+ ]),
46
+ ),
47
+ edges: new Map(
48
+ [...state.edges].map(([name, dependencies]) => [
49
+ name,
50
+ [...dependencies],
51
+ ]),
52
+ ),
53
+ });
54
+
55
+ const findPath = (
56
+ edges: Map<string, string[]>,
57
+ from: string,
58
+ target: string,
59
+ visited: Set<string> = new Set(),
60
+ ): string[] | null => {
61
+ if (from === target) return [from];
62
+ if (visited.has(from)) return null;
63
+
64
+ visited.add(from);
65
+
66
+ for (const next of edges.get(from) || []) {
67
+ const path = findPath(edges, next, target, visited);
68
+
69
+ if (path) return [from, ...path];
70
+ }
71
+
72
+ return null;
73
+ };
74
+
75
+ export class PubGrub {
76
+ private entries = new Map<string, IPackageEntry>();
77
+
78
+ private incompatibilities: IIncompatibility[] = [];
79
+
80
+ private incompatibilityKeys = new Set<string>();
81
+
82
+ constructor (private source: IEntrySource = entryStore) {}
83
+
84
+ public resolve = async (
85
+ rootDependencies: TDependencies,
86
+ ): Promise<TPubGrubResult> => {
87
+ this.entries = new Map();
88
+ this.incompatibilities = [];
89
+ this.incompatibilityKeys = new Set();
90
+
91
+ const state: ISolverState = {
92
+ selected: new Map([[ROOT, ROOT_VERSION]]),
93
+ requirements: new Map(),
94
+ edges: new Map(),
95
+ };
96
+
97
+ const roots = await Promise.all(
98
+ Object.entries(rootDependencies).map(async ([depPath, range]) => ({
99
+ entry: await this.load(depPath),
100
+ range: range || '*',
101
+ })),
102
+ );
103
+
104
+ roots.forEach(({ entry, range }) => {
105
+ this.assertConstraint(entry.name, range, ROOT);
106
+
107
+ const requirement = this.requirement(
108
+ entry.name,
109
+ range,
110
+ ROOT,
111
+ [ROOT, entry.name],
112
+ );
113
+
114
+ this.addIncompatibility({
115
+ terms: [
116
+ { module: ROOT, range: ROOT_VERSION, positive: true },
117
+ { module: entry.name, range, positive: false },
118
+ ],
119
+ cause: { type: 'root', requirement },
120
+ });
121
+ });
122
+
123
+ const result = await this.search(state);
124
+
125
+ if (!result.ok) throw this.toError(result.conflict);
126
+
127
+ return Object.fromEntries(
128
+ [...result.state.selected]
129
+ .filter(([name]) => name !== ROOT)
130
+ .map(([name, version]) => {
131
+ const entry = this.entries.get(name)!;
132
+
133
+ return [name, {
134
+ ...entry,
135
+ version,
136
+ dependencies: { ...entry.versions[version] },
137
+ }];
138
+ }),
139
+ );
140
+ };
141
+
142
+ private search = async (state: ISolverState): Promise<TSearchResult> => {
143
+ const propagatedConflict = this.propagate(state);
144
+
145
+ if (propagatedConflict) {
146
+ this.learn(state, propagatedConflict);
147
+
148
+ return { ok: false, conflict: propagatedConflict };
149
+ }
150
+
151
+ const unresolved = [...state.requirements]
152
+ .filter(([name, requirements]) => (
153
+ name !== ROOT
154
+ && !state.selected.has(name)
155
+ && requirements.some(requirement => requirement.positive)
156
+ ))
157
+ .map(([name]) => name);
158
+
159
+ if (!unresolved.length) return { ok: true, state };
160
+
161
+ const choices = unresolved.map(name => {
162
+ const entry = this.entries.get(name)!;
163
+ const requirements = state.requirements.get(name) || [];
164
+ const versions = this.candidates(name, state);
165
+
166
+ return { name, entry, requirements, versions };
167
+ });
168
+
169
+ choices.sort((a, b) => (
170
+ a.versions.length - b.versions.length || a.name.localeCompare(b.name)
171
+ ));
172
+
173
+ const choice = choices[0];
174
+
175
+ if (!choice.versions.length) {
176
+ const conflict = this.versionConflict(
177
+ choice.name,
178
+ choice.requirements,
179
+ Object.keys(choice.entry.versions),
180
+ );
181
+
182
+ this.learn(state, conflict);
183
+
184
+ return { ok: false, conflict };
185
+ }
186
+
187
+ let lastConflict: TConflict | undefined;
188
+
189
+ for (const version of choice.versions) {
190
+ const next = cloneState(state);
191
+
192
+ next.selected.set(choice.name, version);
193
+
194
+ const dependencyConflict = await this.addDependencies(
195
+ next,
196
+ choice.entry,
197
+ version,
198
+ );
199
+
200
+ const result = dependencyConflict
201
+ ? { ok: false as const, conflict: dependencyConflict }
202
+ : await this.search(next);
203
+
204
+ if (result.ok) return result;
205
+
206
+ this.learn(next, result.conflict);
207
+ lastConflict = result.conflict;
208
+
209
+ /* Решение не участвовало в причине: откатываемся через этот уровень. */
210
+ if (!result.conflict.packages.has(choice.name)) return result;
211
+ }
212
+
213
+ return {
214
+ ok: false,
215
+ conflict: lastConflict || this.versionConflict(
216
+ choice.name,
217
+ choice.requirements,
218
+ Object.keys(choice.entry.versions),
219
+ ),
220
+ };
221
+ };
222
+
223
+ /*
224
+ * Unit propagation: если incompatibility почти удовлетворена, добавляем
225
+ * отрицание единственного неопределённого term в partial solution.
226
+ */
227
+ private propagate = (state: ISolverState): TConflict | null => {
228
+ let changed = true;
229
+
230
+ while (changed) {
231
+ changed = false;
232
+
233
+ for (const incompatibility of this.incompatibilities) {
234
+ const relations = incompatibility.terms.map(term => (
235
+ this.relation(term, state)
236
+ ));
237
+
238
+ if (relations.includes('contradicted')) continue;
239
+
240
+ const inconclusive = relations
241
+ .map((relation, index) => ({ relation, index }))
242
+ .filter(({ relation }) => relation === 'inconclusive');
243
+
244
+ if (!inconclusive.length) {
245
+ return this.conflictFrom(incompatibility, state);
246
+ }
247
+
248
+ if (inconclusive.length !== 1) continue;
249
+
250
+ const term = incompatibility.terms[inconclusive[0].index];
251
+
252
+ const requirement = this.derivedRequirement(
253
+ term,
254
+ incompatibility,
255
+ );
256
+
257
+ changed = this.addRequirement(
258
+ state,
259
+ term.module,
260
+ requirement,
261
+ ) || changed;
262
+ }
263
+
264
+ const selectedConflict = this.findSelectedConflict(state);
265
+
266
+ if (selectedConflict) return selectedConflict;
267
+ }
268
+
269
+ return null;
270
+ };
271
+
272
+ private relation = (term: ITerm, state: ISolverState): TTermState => {
273
+ const selected = state.selected.get(term.module);
274
+
275
+ if (selected) {
276
+ const matches = versionUtil.satisfies(selected, term.range);
277
+ const satisfied = term.positive ? matches : !matches;
278
+
279
+ return satisfied ? 'satisfied' : 'contradicted';
280
+ }
281
+
282
+ const requirements = state.requirements.get(term.module) || [];
283
+
284
+ if (!requirements.length) return 'inconclusive';
285
+
286
+ const candidates = this.candidates(term.module, state);
287
+
288
+ if (!candidates.length) return 'inconclusive';
289
+
290
+ const matching = candidates.filter(version => (
291
+ versionUtil.satisfies(version, term.range)
292
+ ));
293
+
294
+ if (term.positive) {
295
+ if (matching.length === candidates.length) return 'satisfied';
296
+ if (!matching.length) return 'contradicted';
297
+ } else {
298
+ if (!matching.length) return 'satisfied';
299
+ if (matching.length === candidates.length) return 'contradicted';
300
+ }
301
+
302
+ return 'inconclusive';
303
+ };
304
+
305
+ private candidates = (name: string, state: ISolverState): string[] => {
306
+ const versions = Object.keys(this.entries.get(name)?.versions || {});
307
+ const requirements = state.requirements.get(name) || [];
308
+
309
+ return versionUtil.sort(versions).filter(version => (
310
+ requirements.every(requirement => {
311
+ const matches = versionUtil.satisfies(version, requirement.range);
312
+
313
+ return requirement.positive ? matches : !matches;
314
+ })
315
+ ));
316
+ };
317
+
318
+ private addDependencies = async (
319
+ state: ISolverState,
320
+ entry: IPackageEntry,
321
+ version: string,
322
+ ): Promise<ICircularConflict | null> => {
323
+ const dependencies = entry.versions[version] || {};
324
+ const dependencyNames = Object.keys(dependencies);
325
+
326
+ Object.entries(dependencies).forEach(([name, range]) => {
327
+ this.assertConstraint(name, range, `${entry.name}@${version}`);
328
+ });
329
+
330
+ await Promise.all(dependencyNames.map(name => this.load(name)));
331
+ state.edges.set(entry.name, dependencyNames);
332
+
333
+ for (const [name, range] of Object.entries(dependencies)) {
334
+ const path = findPath(state.edges, name, entry.name);
335
+
336
+ if (path) {
337
+ const chain = [entry.name, ...path];
338
+
339
+ return {
340
+ type: 'circular',
341
+ name: entry.name,
342
+ chain,
343
+ packages: new Set(chain),
344
+ };
345
+ }
346
+
347
+ const parentRequirement = [...(state.requirements.get(entry.name) || [])]
348
+ .sort((a, b) => a.chain.length - b.chain.length)[0];
349
+
350
+ const requirement = this.requirement(
351
+ name,
352
+ range,
353
+ `${entry.name}@${version}`,
354
+ [...(parentRequirement?.chain || [ROOT, entry.name]), name],
355
+ );
356
+
357
+ this.addIncompatibility({
358
+ terms: [
359
+ { module: entry.name, range: version, positive: true },
360
+ { module: name, range, positive: false },
361
+ ],
362
+ cause: { type: 'dependency', requirement },
363
+ });
364
+ }
365
+
366
+ return null;
367
+ };
368
+
369
+ private load = async (depPath: string): Promise<IPackageEntry> => {
370
+ const known = this.entries.get(depPath);
371
+
372
+ if (known) return known;
373
+
374
+ const entry = await this.source.fetch(depPath);
375
+ const duplicate = this.entries.get(entry.name);
376
+
377
+ if (duplicate && duplicate.url !== entry.url) {
378
+ throw new SVError('ADDON_NOT_FOUND', {
379
+ name: entry.name,
380
+ urls: [duplicate.url, entry.url],
381
+ });
382
+ }
383
+
384
+ this.entries.set(entry.name, entry);
385
+
386
+ return entry;
387
+ };
388
+
389
+ private addIncompatibility = (incompatibility: IIncompatibility): void => {
390
+ const key = incompatibility.terms
391
+ .map(term => `${term.module}:${term.positive}:${term.range}`)
392
+ .sort()
393
+ .join('|');
394
+
395
+ if (this.incompatibilityKeys.has(key)) return;
396
+
397
+ this.incompatibilityKeys.add(key);
398
+ this.incompatibilities.push(incompatibility);
399
+ };
400
+
401
+ private learn = (state: ISolverState, conflict: TConflict): void => {
402
+ const terms = [...state.selected]
403
+ .filter(([name]) => name !== ROOT && conflict.packages.has(name))
404
+ .map(([module, range]) => ({ module, range, positive: true }));
405
+
406
+ if (!terms.length) return;
407
+
408
+ this.addIncompatibility({
409
+ terms,
410
+ cause: { type: 'learned', conflict },
411
+ });
412
+ };
413
+
414
+ private conflictFrom = (
415
+ incompatibility: IIncompatibility,
416
+ state: ISolverState,
417
+ ): TConflict => {
418
+ if (incompatibility.cause.type === 'learned') {
419
+ return incompatibility.cause.conflict;
420
+ }
421
+
422
+ const { requirement } = incompatibility.cause;
423
+ const name = requirement.chain[requirement.chain.length - 1];
424
+
425
+ this.addRequirement(state, name, requirement);
426
+
427
+ return this.versionConflict(
428
+ name,
429
+ state.requirements.get(name) || [],
430
+ Object.keys(this.entries.get(name)?.versions || {}),
431
+ );
432
+ };
433
+
434
+ // eslint-disable-next-line class-methods-use-this
435
+ private derivedRequirement = (
436
+ term: ITerm,
437
+ incompatibility: IIncompatibility,
438
+ ): IRequirement => {
439
+ if (incompatibility.cause.type !== 'learned') {
440
+ const source = incompatibility.cause.requirement;
441
+
442
+ return { ...source, positive: !term.positive };
443
+ }
444
+
445
+ return {
446
+ range: term.range,
447
+ positive: !term.positive,
448
+ requiredBy: '<conflict>',
449
+ chain: [ROOT, term.module],
450
+ };
451
+ };
452
+
453
+ // eslint-disable-next-line class-methods-use-this
454
+ private requirement = (
455
+ name: string,
456
+ range: string,
457
+ requiredBy: string,
458
+ chain: string[],
459
+ ): IRequirement => ({
460
+ range,
461
+ positive: true,
462
+ requiredBy,
463
+ chain: chain[chain.length - 1] === name ? chain : [...chain, name],
464
+ });
465
+
466
+ // eslint-disable-next-line class-methods-use-this
467
+ private addRequirement = (
468
+ state: ISolverState,
469
+ name: string,
470
+ requirement: IRequirement,
471
+ ): boolean => {
472
+ const requirements = state.requirements.get(name) || [];
473
+
474
+ const exists = requirements.some(current => (
475
+ current.range === requirement.range
476
+ && current.positive === requirement.positive
477
+ && current.requiredBy === requirement.requiredBy
478
+ ));
479
+
480
+ if (exists) return false;
481
+
482
+ requirements.push(requirement);
483
+ state.requirements.set(name, requirements);
484
+
485
+ return true;
486
+ };
487
+
488
+ private findSelectedConflict = (
489
+ state: ISolverState,
490
+ ): IVersionConflict | null => {
491
+ for (const [name, version] of state.selected) {
492
+ if (name === ROOT) continue;
493
+
494
+ const requirements = state.requirements.get(name) || [];
495
+
496
+ const valid = requirements.every(requirement => {
497
+ const matches = versionUtil.satisfies(version, requirement.range);
498
+
499
+ return requirement.positive ? matches : !matches;
500
+ });
501
+
502
+ if (valid) continue;
503
+
504
+ return this.versionConflict(
505
+ name,
506
+ requirements,
507
+ Object.keys(this.entries.get(name)?.versions || {}),
508
+ );
509
+ }
510
+
511
+ return null;
512
+ };
513
+
514
+ // eslint-disable-next-line class-methods-use-this
515
+ private versionConflict = (
516
+ name: string,
517
+ requirements: IRequirement[],
518
+ versions: string[],
519
+ ): IVersionConflict => ({
520
+ type: 'version',
521
+ name,
522
+ requirements,
523
+ versions: versionUtil.sort(versions),
524
+ packages: new Set([
525
+ name,
526
+ ...requirements.flatMap(requirement => requirement.chain),
527
+ ].filter(packageName => packageName !== ROOT)),
528
+ });
529
+
530
+ // eslint-disable-next-line class-methods-use-this
531
+ private assertConstraint = (
532
+ name: string,
533
+ range: string,
534
+ parent: string,
535
+ ): void => {
536
+ if (versionUtil.validate(range, true)) return;
537
+
538
+ throw new SVError('UNKNOWN_VERSION', {
539
+ name,
540
+ parent,
541
+ version: range,
542
+ });
543
+ };
544
+
545
+ // eslint-disable-next-line class-methods-use-this
546
+ private toError = (conflict: TConflict): SVError => {
547
+ if (conflict.type === 'circular') {
548
+ return new SVError('CIRCULAR_DEPENDENCY', {
549
+ name: conflict.name,
550
+ chain: conflict.chain,
551
+ });
552
+ }
553
+
554
+ const parents = Object.fromEntries(
555
+ conflict.requirements.map(requirement => [
556
+ requirement.requiredBy,
557
+ `${requirement.positive ? '' : 'not '}${requirement.range}`,
558
+ ]),
559
+ );
560
+
561
+ const chain = [...conflict.requirements]
562
+ .sort((a, b) => a.chain.length - b.chain.length)[0]?.chain;
563
+
564
+ return new SVError('VERSION_CONFLICT', {
565
+ name: conflict.name,
566
+ parents,
567
+ constraints: conflict.requirements,
568
+ versions: conflict.versions,
569
+ chain,
570
+ });
571
+ };
572
+ }
573
+
574
+ export const pubGrub = {
575
+ resolve: (rootDependencies: TDependencies): Promise<TPubGrubResult> => (
576
+ new PubGrub().resolve(rootDependencies)
577
+ ),
578
+ };