pnpm-dedup 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +218 -0
  3. package/dist/applyClusterFixes.js +252 -0
  4. package/dist/applyClusterFixes.js.map +1 -0
  5. package/dist/applyClusterFixes.test.js +474 -0
  6. package/dist/applyClusterFixes.test.js.map +1 -0
  7. package/dist/bin/pnpm-dedupe.js +35 -0
  8. package/dist/bin/pnpm-dedupe.js.map +1 -0
  9. package/dist/bin/pnpm-why-duplicate.js +23 -0
  10. package/dist/bin/pnpm-why-duplicate.js.map +1 -0
  11. package/dist/bin/pnpm-why-duplicate.test.js +57 -0
  12. package/dist/bin/pnpm-why-duplicate.test.js.map +1 -0
  13. package/dist/dedupe.js +86 -0
  14. package/dist/dedupe.js.map +1 -0
  15. package/dist/dedupeCheckConsistency.test.js +149 -0
  16. package/dist/dedupeCheckConsistency.test.js.map +1 -0
  17. package/dist/dedupeEndToEnd.test.js +121 -0
  18. package/dist/dedupeEndToEnd.test.js.map +1 -0
  19. package/dist/dedupePeers.test.js +105 -0
  20. package/dist/dedupePeers.test.js.map +1 -0
  21. package/dist/definitions/applyClusterFixes.d.ts +24 -0
  22. package/dist/definitions/applyClusterFixes.d.ts.map +1 -0
  23. package/dist/definitions/applyClusterFixes.test.d.ts +2 -0
  24. package/dist/definitions/applyClusterFixes.test.d.ts.map +1 -0
  25. package/dist/definitions/bin/pnpm-dedupe.d.ts +3 -0
  26. package/dist/definitions/bin/pnpm-dedupe.d.ts.map +1 -0
  27. package/dist/definitions/bin/pnpm-why-duplicate.d.ts +3 -0
  28. package/dist/definitions/bin/pnpm-why-duplicate.d.ts.map +1 -0
  29. package/dist/definitions/bin/pnpm-why-duplicate.test.d.ts +2 -0
  30. package/dist/definitions/bin/pnpm-why-duplicate.test.d.ts.map +1 -0
  31. package/dist/definitions/dedupe.d.ts +15 -0
  32. package/dist/definitions/dedupe.d.ts.map +1 -0
  33. package/dist/definitions/dedupeCheckConsistency.test.d.ts +2 -0
  34. package/dist/definitions/dedupeCheckConsistency.test.d.ts.map +1 -0
  35. package/dist/definitions/dedupeEndToEnd.test.d.ts +2 -0
  36. package/dist/definitions/dedupeEndToEnd.test.d.ts.map +1 -0
  37. package/dist/definitions/dedupePeers.test.d.ts +2 -0
  38. package/dist/definitions/dedupePeers.test.d.ts.map +1 -0
  39. package/dist/definitions/displayMany.d.ts +16 -0
  40. package/dist/definitions/displayMany.d.ts.map +1 -0
  41. package/dist/definitions/displayMany.test.d.ts +2 -0
  42. package/dist/definitions/displayMany.test.d.ts.map +1 -0
  43. package/dist/definitions/helpers/buildIdentifiedFixesMap.test.d.ts +2 -0
  44. package/dist/definitions/helpers/buildIdentifiedFixesMap.test.d.ts.map +1 -0
  45. package/dist/definitions/helpers/buildPnpmPackagesMap.d.ts +13 -0
  46. package/dist/definitions/helpers/buildPnpmPackagesMap.d.ts.map +1 -0
  47. package/dist/definitions/helpers/buildPnpmPackagesMap.test.d.ts +2 -0
  48. package/dist/definitions/helpers/buildPnpmPackagesMap.test.d.ts.map +1 -0
  49. package/dist/definitions/helpers/collectDependentRanges.d.ts +22 -0
  50. package/dist/definitions/helpers/collectDependentRanges.d.ts.map +1 -0
  51. package/dist/definitions/helpers/collectDependentRanges.test.d.ts +2 -0
  52. package/dist/definitions/helpers/collectDependentRanges.test.d.ts.map +1 -0
  53. package/dist/definitions/helpers/collectPnpmDependents.d.ts +8 -0
  54. package/dist/definitions/helpers/collectPnpmDependents.d.ts.map +1 -0
  55. package/dist/definitions/helpers/collectPnpmDependents.test.d.ts +2 -0
  56. package/dist/definitions/helpers/collectPnpmDependents.test.d.ts.map +1 -0
  57. package/dist/definitions/helpers/duplicateSnapshot.d.ts +3 -0
  58. package/dist/definitions/helpers/duplicateSnapshot.d.ts.map +1 -0
  59. package/dist/definitions/helpers/parsePnpmLockPackages.d.ts +44 -0
  60. package/dist/definitions/helpers/parsePnpmLockPackages.d.ts.map +1 -0
  61. package/dist/definitions/helpers/parsePnpmLockPackages.test.d.ts +2 -0
  62. package/dist/definitions/helpers/parsePnpmLockPackages.test.d.ts.map +1 -0
  63. package/dist/definitions/helpers/pnpmProtocol.d.ts +2 -0
  64. package/dist/definitions/helpers/pnpmProtocol.d.ts.map +1 -0
  65. package/dist/definitions/helpers/pnpmVersion.d.ts +11 -0
  66. package/dist/definitions/helpers/pnpmVersion.d.ts.map +1 -0
  67. package/dist/definitions/helpers/pnpmWorkspaceYaml.d.ts +23 -0
  68. package/dist/definitions/helpers/pnpmWorkspaceYaml.d.ts.map +1 -0
  69. package/dist/definitions/helpers/pnpmWorkspaceYaml.test.d.ts +2 -0
  70. package/dist/definitions/helpers/pnpmWorkspaceYaml.test.d.ts.map +1 -0
  71. package/dist/definitions/helpers/projectDir.d.ts +3 -0
  72. package/dist/definitions/helpers/projectDir.d.ts.map +1 -0
  73. package/dist/definitions/helpers/readInstalledManifest.d.ts +32 -0
  74. package/dist/definitions/helpers/readInstalledManifest.d.ts.map +1 -0
  75. package/dist/definitions/helpers/readInstalledManifest.test.d.ts +2 -0
  76. package/dist/definitions/helpers/readInstalledManifest.test.d.ts.map +1 -0
  77. package/dist/definitions/helpers/runPnpm.d.ts +11 -0
  78. package/dist/definitions/helpers/runPnpm.d.ts.map +1 -0
  79. package/dist/definitions/helpers/tempProjects.d.ts +8 -0
  80. package/dist/definitions/helpers/tempProjects.d.ts.map +1 -0
  81. package/dist/definitions/helpers/toLockstepGraph.d.ts +12 -0
  82. package/dist/definitions/helpers/toLockstepGraph.d.ts.map +1 -0
  83. package/dist/definitions/helpers/toLockstepGraph.test.d.ts +2 -0
  84. package/dist/definitions/helpers/toLockstepGraph.test.d.ts.map +1 -0
  85. package/dist/definitions/helpers/versionsSnapshot.d.ts +3 -0
  86. package/dist/definitions/helpers/versionsSnapshot.d.ts.map +1 -0
  87. package/dist/definitions/identifyClusterFixes.d.ts +7 -0
  88. package/dist/definitions/identifyClusterFixes.d.ts.map +1 -0
  89. package/dist/definitions/identifyClusterFixes.test.d.ts +2 -0
  90. package/dist/definitions/identifyClusterFixes.test.d.ts.map +1 -0
  91. package/dist/definitions/identifyResolutionFixes.test.d.ts +2 -0
  92. package/dist/definitions/identifyResolutionFixes.test.d.ts.map +1 -0
  93. package/dist/definitions/index.d.ts +25 -0
  94. package/dist/definitions/index.d.ts.map +1 -0
  95. package/dist/definitions/pnpmLockTypes.d.ts +42 -0
  96. package/dist/definitions/pnpmLockTypes.d.ts.map +1 -0
  97. package/dist/definitions/readPnpmLock.d.ts +3 -0
  98. package/dist/definitions/readPnpmLock.d.ts.map +1 -0
  99. package/dist/displayMany.js +56 -0
  100. package/dist/displayMany.js.map +1 -0
  101. package/dist/displayMany.test.js +146 -0
  102. package/dist/displayMany.test.js.map +1 -0
  103. package/dist/helpers/buildIdentifiedFixesMap.test.js +27 -0
  104. package/dist/helpers/buildIdentifiedFixesMap.test.js.map +1 -0
  105. package/dist/helpers/buildPnpmPackagesMap.js +23 -0
  106. package/dist/helpers/buildPnpmPackagesMap.js.map +1 -0
  107. package/dist/helpers/buildPnpmPackagesMap.test.js +68 -0
  108. package/dist/helpers/buildPnpmPackagesMap.test.js.map +1 -0
  109. package/dist/helpers/collectDependentRanges.js +118 -0
  110. package/dist/helpers/collectDependentRanges.js.map +1 -0
  111. package/dist/helpers/collectDependentRanges.test.js +211 -0
  112. package/dist/helpers/collectDependentRanges.test.js.map +1 -0
  113. package/dist/helpers/collectPnpmDependents.js +55 -0
  114. package/dist/helpers/collectPnpmDependents.js.map +1 -0
  115. package/dist/helpers/collectPnpmDependents.test.js +78 -0
  116. package/dist/helpers/collectPnpmDependents.test.js.map +1 -0
  117. package/dist/helpers/duplicateSnapshot.js +5 -0
  118. package/dist/helpers/duplicateSnapshot.js.map +1 -0
  119. package/dist/helpers/parsePnpmLockPackages.js +62 -0
  120. package/dist/helpers/parsePnpmLockPackages.js.map +1 -0
  121. package/dist/helpers/parsePnpmLockPackages.test.js +62 -0
  122. package/dist/helpers/parsePnpmLockPackages.test.js.map +1 -0
  123. package/dist/helpers/pnpmProtocol.js +2 -0
  124. package/dist/helpers/pnpmProtocol.js.map +1 -0
  125. package/dist/helpers/pnpmVersion.js +23 -0
  126. package/dist/helpers/pnpmVersion.js.map +1 -0
  127. package/dist/helpers/pnpmWorkspaceYaml.js +48 -0
  128. package/dist/helpers/pnpmWorkspaceYaml.js.map +1 -0
  129. package/dist/helpers/pnpmWorkspaceYaml.test.js +91 -0
  130. package/dist/helpers/pnpmWorkspaceYaml.test.js.map +1 -0
  131. package/dist/helpers/projectDir.js +10 -0
  132. package/dist/helpers/projectDir.js.map +1 -0
  133. package/dist/helpers/readInstalledManifest.js +139 -0
  134. package/dist/helpers/readInstalledManifest.js.map +1 -0
  135. package/dist/helpers/readInstalledManifest.test.js +83 -0
  136. package/dist/helpers/readInstalledManifest.test.js.map +1 -0
  137. package/dist/helpers/runPnpm.js +16 -0
  138. package/dist/helpers/runPnpm.js.map +1 -0
  139. package/dist/helpers/tempProjects.js +85 -0
  140. package/dist/helpers/tempProjects.js.map +1 -0
  141. package/dist/helpers/toLockstepGraph.js +34 -0
  142. package/dist/helpers/toLockstepGraph.js.map +1 -0
  143. package/dist/helpers/toLockstepGraph.test.js +81 -0
  144. package/dist/helpers/toLockstepGraph.test.js.map +1 -0
  145. package/dist/helpers/versionsSnapshot.js +5 -0
  146. package/dist/helpers/versionsSnapshot.js.map +1 -0
  147. package/dist/identifyClusterFixes.js +53 -0
  148. package/dist/identifyClusterFixes.js.map +1 -0
  149. package/dist/identifyClusterFixes.test.js +307 -0
  150. package/dist/identifyClusterFixes.test.js.map +1 -0
  151. package/dist/identifyResolutionFixes.test.js +49 -0
  152. package/dist/identifyResolutionFixes.test.js.map +1 -0
  153. package/dist/index.js +86 -0
  154. package/dist/index.js.map +1 -0
  155. package/dist/pnpmLockTypes.js +7 -0
  156. package/dist/pnpmLockTypes.js.map +1 -0
  157. package/dist/readPnpmLock.js +6 -0
  158. package/dist/readPnpmLock.js.map +1 -0
  159. package/package.json +71 -0
  160. package/src/applyClusterFixes.test.ts +560 -0
  161. package/src/applyClusterFixes.ts +466 -0
  162. package/src/bin/pnpm-dedupe.ts +44 -0
  163. package/src/bin/pnpm-why-duplicate.test.ts +79 -0
  164. package/src/bin/pnpm-why-duplicate.ts +34 -0
  165. package/src/dedupe.ts +124 -0
  166. package/src/dedupeCheckConsistency.test.ts +218 -0
  167. package/src/dedupeEndToEnd.test.ts +168 -0
  168. package/src/dedupePeers.test.ts +175 -0
  169. package/src/displayMany.test.ts +208 -0
  170. package/src/displayMany.ts +104 -0
  171. package/src/helpers/buildIdentifiedFixesMap.test.ts +33 -0
  172. package/src/helpers/buildPnpmPackagesMap.test.ts +119 -0
  173. package/src/helpers/buildPnpmPackagesMap.ts +53 -0
  174. package/src/helpers/collectDependentRanges.test.ts +311 -0
  175. package/src/helpers/collectDependentRanges.ts +185 -0
  176. package/src/helpers/collectPnpmDependents.test.ts +112 -0
  177. package/src/helpers/collectPnpmDependents.ts +79 -0
  178. package/src/helpers/duplicateSnapshot.ts +18 -0
  179. package/src/helpers/parsePnpmLockPackages.test.ts +93 -0
  180. package/src/helpers/parsePnpmLockPackages.ts +99 -0
  181. package/src/helpers/pnpmProtocol.ts +12 -0
  182. package/src/helpers/pnpmVersion.ts +28 -0
  183. package/src/helpers/pnpmWorkspaceYaml.test.ts +140 -0
  184. package/src/helpers/pnpmWorkspaceYaml.ts +77 -0
  185. package/src/helpers/projectDir.ts +14 -0
  186. package/src/helpers/readInstalledManifest.test.ts +124 -0
  187. package/src/helpers/readInstalledManifest.ts +202 -0
  188. package/src/helpers/runPnpm.ts +34 -0
  189. package/src/helpers/tempProjects.ts +94 -0
  190. package/src/helpers/toLockstepGraph.test.ts +98 -0
  191. package/src/helpers/toLockstepGraph.ts +52 -0
  192. package/src/helpers/versionsSnapshot.ts +9 -0
  193. package/src/identifyClusterFixes.test.ts +430 -0
  194. package/src/identifyClusterFixes.ts +114 -0
  195. package/src/identifyResolutionFixes.test.ts +82 -0
  196. package/src/index.ts +159 -0
  197. package/src/pnpmLockTypes.ts +44 -0
  198. package/src/readPnpmLock.ts +7 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Christophe Hurpeau <christophe@hurpeau.com> (https://christophe.hurpeau.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,218 @@
1
+ <h1 align="center">
2
+ pnpm-dedup
3
+ </h1>
4
+
5
+ <p align="center">
6
+ List duplicates and dedupe pnpm lock file
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://npmjs.org/package/pnpm-dedup"><img src="https://img.shields.io/npm/v/pnpm-dedup.svg?style=flat-square" alt="npm version"></a>
11
+ <a href="https://npmjs.org/package/pnpm-dedup"><img src="https://img.shields.io/npm/dw/pnpm-dedup.svg?style=flat-square" alt="npm downloads"></a>
12
+ <a href="https://npmjs.org/package/pnpm-dedup"><img src="https://img.shields.io/node/v/pnpm-dedup.svg?style=flat-square" alt="node version"></a>
13
+ <a href="https://npmjs.org/package/pnpm-dedup"><img src="https://img.shields.io/npm/types/pnpm-dedup.svg?style=flat-square" alt="types"></a>
14
+ </p>
15
+
16
+ ## Usage
17
+
18
+ Nothing needs to be installed: `pnpm dlx` fetches the package, and
19
+ `--package pnpm-dedup` is what tells it which one, since `pnpm dlx` otherwise
20
+ fetches the package whose name matches the command. The bins are
21
+ `pnpm-why-duplicate` and `pnpm-dedupe`, plus `pnpm-dedup` as an alias of the
22
+ latter — that one does match the package name, so `pnpm dlx pnpm-dedup` is a
23
+ shorter way to run the dedupe.
24
+
25
+ Installing it (`pnpm add -D pnpm-dedup`) is only worth it to pin a version — for
26
+ a CI gate, say. `pnpm exec <bin>` then runs the local one.
27
+
28
+ Both bins may be run from any subdirectory of the project: they walk up to the
29
+ nearest `pnpm-lock.yaml` and operate on that project, naming the lockfile they
30
+ found on stderr when it is not the working directory.
31
+
32
+ ### List duplicates, or explain one
33
+
34
+ ```
35
+ pnpm dlx --package pnpm-dedup pnpm-why-duplicate # one line per duplicated package
36
+ pnpm dlx --package pnpm-dedup pnpm-why-duplicate --details # every dependent, and the lockstep clusters
37
+ pnpm dlx --package pnpm-dedup pnpm-why-duplicate <dependencyName> # explains the matching packages, detailed
38
+ pnpm dlx --package pnpm-dedup pnpm-why-duplicate <dependencyName> --all
39
+ ```
40
+
41
+ `<dependencyName>` is a glob, so `@babel/*` works. `--all` (`-a`) keeps packages
42
+ that are not duplicated.
43
+
44
+ A named package that is not duplicated still gets its dependents listed —
45
+ "nothing found" is not an answer to "why is this duplicated". `--all` is only
46
+ needed to keep single-version matches next to duplicated ones.
47
+
48
+ By default the report is one line per duplicated package — the versions it
49
+ resolved to, and what it can be deduped to:
50
+
51
+ ```
52
+ - @eslint/plugin-kit resolved to 2 versions (0.7.1, 0.6.1)
53
+ - picomatch resolved to 3 versions (4.0.4, 4.0.3, 2.3.1), can be deduped to 2 versions
54
+ ```
55
+
56
+ `can be deduped to N versions` always means N remain, never N merged away.
57
+
58
+ `--details` (`-d`), implied when a package is named, files every dependent under
59
+ the version it resolved to, so the range that keeps each copy alive is read off
60
+ the version it sits under:
61
+
62
+ ```
63
+ @eslint/plugin-kit — 2 versions
64
+ 0.7.1
65
+ - check-package-dependencies requires "^0.7.1"
66
+ - eslint requires "^0.7.1"
67
+ 0.6.1
68
+ - @eslint/json requires "^0.6.1"
69
+ ```
70
+
71
+ After the packages come the lockstep clusters — families published together,
72
+ whose duplicates only go away when the whole family moves. A package a cluster
73
+ converges carries the family's target and a `(cluster N)` reference.
74
+
75
+ The dedupable count is a floor: merging a package collapses its own dependency
76
+ edges too, so a run can remove copies no per-package fix predicted.
77
+
78
+ ### Dedupe
79
+
80
+ ```
81
+ pnpm dlx --package pnpm-dedup pnpm-dedupe # apply
82
+ pnpm dlx --package pnpm-dedup pnpm-dedupe --dry-run # print the plan, change nothing, exit 0
83
+ pnpm dlx --package pnpm-dedup pnpm-dedupe --check # print the plan, change nothing, exit 1 if anything would change
84
+ ```
85
+
86
+ `--check` is the CI gate; `--dry-run` (`-n`) is the same output without the
87
+ failing exit code. `--check` also exits 1 when `pnpm dedupe --check` itself
88
+ cannot confirm the lockfile is settled, so a probe that fails for environmental
89
+ reasons fails the gate rather than passing silently.
90
+
91
+ `--no-convergence-overrides` writes plain overrides instead of convergence ones,
92
+ which pnpm applies to every requester whatever range it declares. Convergence
93
+ overrides need pnpm >= 11.13.0; without them the cluster pass is skipped.
94
+
95
+ A run that changes something ends with what it merged, one line per package:
96
+
97
+ ```
98
+ Deduped 2 packages, 2 copies merged:
99
+ barcode-detector: 2 versions (3.0.3, 3.2.2) -> 1 version (3.0.3)
100
+ zxing-wasm: 2 versions (2.2.4, 3.1.3) -> 1 version (2.2.4)
101
+ No duplicate left.
102
+ ```
103
+
104
+ The cluster pass and `pnpm dedupe` are covered by that one summary. Both sides
105
+ of a line are counted and named, so a family converging onto an older release
106
+ reads as two copies becoming one rather than as a downgrade; a package only
107
+ partly collapsed lists the versions it still has. The last line counts the
108
+ duplicates left, as `pnpm-why-duplicate` counts them.
109
+
110
+ ### Progressive deduplication
111
+
112
+ Deduping the whole lockfile is the default: a run told nothing takes everything
113
+ it can. In case you only need to dedupe part of it, these flags bound a run to
114
+ the packages you name, and work on both commands:
115
+
116
+ ```
117
+ pnpm dlx --package pnpm-dedup pnpm-dedupe --dry-run --scopes @babel # one scope
118
+ pnpm dlx --package pnpm-dedup pnpm-dedupe --dry-run --packages 'lodash,@babel/**' # names or globs
119
+ pnpm dlx --package pnpm-dedup pnpm-dedupe --dry-run --exclude lodash # everything but these
120
+ pnpm dlx --package pnpm-dedup pnpm-dedupe --dry-run --exclude-scopes @types
121
+ ```
122
+
123
+ Each flag takes a comma list or is repeated (`--packages a --packages b`).
124
+ Selecting nothing selects everything. `--packages`/`--scopes` say what may be
125
+ touched; `--exclude`/`--exclude-scopes` win over them, so a scope can be taken
126
+ minus a few of its packages. Patterns are globs whose `*` stops at `/`, so
127
+ `@babel/*` matches `@babel/core` and a bare `*` matches only unscoped names.
128
+
129
+ A lockstep cluster moves as one, so a fix is kept only when the filter selects
130
+ every member of its family — otherwise it is skipped and the plan names the
131
+ members that blocked it. Widen the filter to take the family, or leave it for
132
+ another run.
133
+
134
+ A filtered run does not end with `pnpm dedupe`, which knows nothing of the
135
+ filter and would merge everything it can. It says so instead, so you can run it
136
+ yourself when you are ready. Applying still needs a real resolution — an
137
+ override only takes effect through one — so a filtered run that changes
138
+ something has already let pnpm merge what it could reach on its own; a
139
+ `--dry-run` or `--check` runs nothing at all.
140
+
141
+ Colors follow the terminal: `NO_COLOR` and a piped stdout turn them off,
142
+ `FORCE_COLOR` turns them on.
143
+
144
+ ## Two passes, one of them pnpm's
145
+
146
+ `pnpm dedupe` merges what it can resolve to a single version on its own, but it
147
+ never widens a workspace range and never repoints an edge that resolved past a
148
+ version the family already carries. That gap is what the cluster pass covers, so
149
+ it runs first and `pnpm dedupe` finishes the residuals. Unlike the bun and yarn
150
+ tools, there is no lockfile-rewriting pass here: `pnpm-lock.yaml` is only ever
151
+ written by pnpm itself.
152
+
153
+ The cluster pass edits workspace `package.json` ranges and adds overrides to
154
+ `pnpm-workspace.yaml`, then re-resolves without them: overrides are scaffolding,
155
+ and a fix that holds on its own has them removed again.
156
+
157
+ When a fix does _not_ hold — `pnpm dedupe` resolves the duplicate straight back
158
+ once the override is gone — the override stays, with a comment above it saying
159
+ so and pointing at the issue tracker, and the run reports how many were left.
160
+ That is the one case where this tool leaves something standing in your
161
+ configuration; it is a workaround, and the cluster is worth reporting.
162
+
163
+ Convergence overrides (`"pkg@": "1.2.3"`, an empty range selector) are what make
164
+ this safe: they rewrite an edge only when the declared range accepts that exact
165
+ version, so a third-party range that legitimately pins elsewhere is left alone.
166
+ `--no-convergence-overrides` falls back to plain overrides, which force every
167
+ requester regardless.
168
+
169
+ ## Peer dependencies
170
+
171
+ pnpm resolves peers before writing the lockfile, folds each into the snapshot's
172
+ `dependencies` and encodes the context in the peer-suffixed key. So the _edge_
173
+ survives — and an edge that exists means the peer really was provided — but the
174
+ _range_ does not: the snapshot stores the resolved version.
175
+
176
+ Left at that, 17 eslint plugins declaring `eslint: "^8.57.0 || ^9.0.0"` would all
177
+ read as exact pins of whatever got installed, and nothing could be merged. The
178
+ range is therefore recovered from the installed manifest first, then from the
179
+ lockfile's own `packages:` entry, so it survives with no `node_modules` present.
180
+ Such a dependent is marked with a dim `(peer)` after its range in the report.
181
+
182
+ A peer range never explains why a second version exists — pnpm resolves peers
183
+ against what is in the tree and warns instead of backtracking, and the several
184
+ peer-suffixed keys of one version are several _installations_, folded back into a
185
+ single resolution here. It is an extra constraint on a package duplicated for
186
+ other reasons, which is why recovering it changes the fixes.
187
+
188
+ ## Notes
189
+
190
+ - Requires pnpm on `PATH`. Cluster fixes additionally need pnpm >= 11.13.0; below
191
+ that the pass is skipped with a message, and `pnpm dedupe` still runs.
192
+ - `--check` shells out to `pnpm dedupe --check`, so it fails the gate when that
193
+ probe cannot confirm the lockfile is settled — an environmental failure fails
194
+ loudly rather than passing silently.
195
+ - Non-npm resolutions (`link:`, git, tarball…) are parsed and reported but never
196
+ merged: only semver-comparable npm versions are.
197
+ - Node only (`>=22.18.0`), no bun runtime dependency, despite the monorepo being
198
+ developed with bun.
199
+
200
+ ## API
201
+
202
+ ```js
203
+ import { dedupe, listDuplicates, whyDuplicate } from "pnpm-dedup/src/index";
204
+
205
+ listDuplicates({ details: true });
206
+ whyDuplicate({ filter: { include: ["@babel/*"] }, all: true, details: true });
207
+ dedupe({
208
+ mode: "dry-run",
209
+ convergenceOverrides: true,
210
+ filter: { includeScopes: ["@babel"] },
211
+ });
212
+ ```
213
+
214
+ `mode` is `"apply"`, `"dry-run"` or `"check"` — the same three the CLI exposes.
215
+ The lockfile parsing and graph building steps are exported too (`readPnpmLock`,
216
+ `parsePnpmLockPackages`, `buildPnpmPackagesMap`, `collectPnpmDependents`,
217
+ `collectDependentRanges`, `toLockstepGraph`), for building something else on
218
+ top; the algorithm itself lives in [pm-dedup-core](../pm-dedup-core).
@@ -0,0 +1,252 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import { join, relative } from "node:path";
3
+ import { applyWorkspaceRangeEdit, captureFiles, createPackageFilter, describeSkippedClusterFix, diffDuplicates, partitionUnconditionalOverrides, planClusterApply, renderApplyPlan, restoreFiles, selectClusterFixes, shouldColorize, } from "pm-dedup-core";
4
+ import { buildPnpmPackagesMap } from "./helpers/buildPnpmPackagesMap.js";
5
+ import { readDuplicateSnapshot } from "./helpers/duplicateSnapshot.js";
6
+ import { parsePnpmLockPackages } from "./helpers/parsePnpmLockPackages.js";
7
+ import { convergenceOverridesMinVersion, readPnpmVersion, supportsConvergenceOverrides, } from "./helpers/pnpmVersion.js";
8
+ import { addOverrides, overrideKey } from "./helpers/pnpmWorkspaceYaml.js";
9
+ import { lockPathOf } from "./helpers/projectDir.js";
10
+ import { createManifestReader } from "./helpers/readInstalledManifest.js";
11
+ import { runPnpm } from "./helpers/runPnpm.js";
12
+ import { identifyClusterFixes } from "./identifyClusterFixes.js";
13
+ import { readPnpmLock } from "./readPnpmLock.js";
14
+ const issuesUrl = "https://github.com/christophehurpeau/pm-tools/issues";
15
+ const defaultReadFixes = (projectDir) => {
16
+ const lock = readPnpmLock(lockPathOf(projectDir));
17
+ return identifyClusterFixes(lock, buildPnpmPackagesMap(parsePnpmLockPackages(lock)), createManifestReader(projectDir));
18
+ };
19
+ const manifestPathOf = (projectDir, importerPath) => join(projectDir, importerPath === "." ? "" : importerPath, "package.json");
20
+ const describeOverride = (override, convergence) => `"${overrideKey(override.packageName, convergence)}": "${override.version}"`;
21
+ /**
22
+ * Why the overrides below them had to stay. Used verbatim as the comment left in
23
+ * `pnpm-workspace.yaml` and as the console explanation, so the file and the run
24
+ * say the same thing. The packages are the entries that follow, so they are not
25
+ * repeated here.
26
+ */
27
+ const stickyOverrideReason = [
28
+ "Added by pnpm-dedup.",
29
+ "Removing these makes `pnpm dedupe` resolve them back to a duplicate, so they",
30
+ "are what holds the deduplicated result. Keeping them is a workaround: no fix",
31
+ "was found that survives on its own. Please report this cluster so it can be",
32
+ `handled without a standing override: ${issuesUrl}`,
33
+ ];
34
+ const reuseKeys = (fixes) => new Set(fixes.flatMap((fix) => fix.reuseFixes.map((reuse) => `${reuse.requesterName}>${reuse.packageName}@${reuse.to}`)));
35
+ export const applyClusterFixes = ({ projectDir, dryRun = false, log = console.log,
36
+ // `pnpm dedupe`, not `pnpm install`: an override is applied by a read-package
37
+ // hook that only runs during a real resolution, and an incremental install
38
+ // reports "Already up to date" without ever re-reading a manifest. `dedupe`
39
+ // re-resolves, and is also what runs right after this, so it is the only
40
+ // honest thing to verify against.
41
+ resolve = () => runPnpm(["dedupe"], { cwd: projectDir }).status, readFixes = defaultReadFixes, readDuplicates = readDuplicateSnapshot, pnpmVersion = () => readPnpmVersion(), convergenceOverrides = true, filter, packageManagerResiduals, color = shouldColorize(), }) => {
42
+ const packageFilter = createPackageFilter(filter);
43
+ const readSelectedFixes = (dir) => selectClusterFixes(readFixes(dir), packageFilter);
44
+ const lockPath = lockPathOf(projectDir);
45
+ const workspaceYamlPath = join(projectDir, "pnpm-workspace.yaml");
46
+ const before = readDuplicates(lockPath);
47
+ const unchanged = (status, plannedChangeCount = 0) => ({
48
+ status,
49
+ before,
50
+ after: before,
51
+ stickyOverrides: [],
52
+ plannedChangeCount,
53
+ });
54
+ const { selected: fixes, skipped: filteredOut } = readSelectedFixes(projectDir);
55
+ const plan = planClusterApply(fixes);
56
+ const skipped = [
57
+ ...filteredOut.map(describeSkippedClusterFix),
58
+ ...plan.unresolvableChanges.map((unresolvable) => `${unresolvable}: no workspace file recorded for it`),
59
+ ...plan.conflicts.map((conflict) => `${conflict.packageName}: keeping ${conflict.kept}, ignoring ${conflict.dropped} asked by another cluster`),
60
+ ];
61
+ // a plain override applies to every requester of the package, so one a
62
+ // third-party range rejects would force that dependent too
63
+ const plannedOverrides = convergenceOverrides
64
+ ? plan.overrides
65
+ : (() => {
66
+ const { safe, rejected } = partitionUnconditionalOverrides(fixes, plan.overrides);
67
+ for (const { override, rejectedBy } of rejected) {
68
+ for (const constraint of rejectedBy) {
69
+ skipped.push(`override ${describeOverride(override, false)}: ${constraint.requesterName ?? "workspace"} requires "${constraint.range}", and a plain override would force it too`);
70
+ }
71
+ }
72
+ return safe;
73
+ })();
74
+ const changeCount = plan.manifestEdits.length + plannedOverrides.length;
75
+ const relativeManifestPath = (importerPath) => relative(projectDir, manifestPathOf(projectDir, importerPath));
76
+ const planFileChanges = () => [
77
+ ...[
78
+ ...new Set(plan.manifestEdits.map((edit) => relativeManifestPath(edit.importerPath))),
79
+ ].map((path) => ({
80
+ path,
81
+ changes: plan.manifestEdits
82
+ .filter((edit) => relativeManifestPath(edit.importerPath) === path)
83
+ .map((edit) => `"${edit.packageName}": "${edit.range}" -> "${edit.to}" (${edit.depType})`),
84
+ })),
85
+ {
86
+ path: relative(projectDir, workspaceYamlPath),
87
+ transient: "removed once the result is verified",
88
+ changes: plannedOverrides.map((override) => `${describeOverride(override, convergenceOverrides)} (${override.reason})`),
89
+ },
90
+ ];
91
+ // A dry run prints the plan through the shared renderer and stops. Anything
92
+ // that rules the plan out has to be decided before that, or the report would
93
+ // promise edits pnpm cannot make.
94
+ const reportPlan = (applicable) => {
95
+ renderApplyPlan({
96
+ fileChanges: applicable ? planFileChanges() : [],
97
+ skipped,
98
+ packageManagerResiduals,
99
+ dedupeCommand: "pnpm-dedupe",
100
+ log,
101
+ color,
102
+ });
103
+ return unchanged("dry-run", applicable ? changeCount : 0);
104
+ };
105
+ if (!dryRun) {
106
+ for (const entry of skipped) {
107
+ log(` Skipped ${entry}`);
108
+ }
109
+ }
110
+ if (changeCount === 0) {
111
+ return dryRun ? reportPlan(false) : unchanged("nothing-to-do");
112
+ }
113
+ if (convergenceOverrides) {
114
+ const version = pnpmVersion();
115
+ if (version === null || !supportsConvergenceOverrides(version)) {
116
+ log(`Cluster fixes need pnpm >= ${convergenceOverridesMinVersion} (convergence overrides); found ${version ?? "no pnpm"}. Skipping them.`);
117
+ // nothing can be applied, so a `--check` run has nothing to fail on
118
+ return dryRun ? reportPlan(false) : unchanged("not-supported");
119
+ }
120
+ }
121
+ if (dryRun) {
122
+ return reportPlan(true);
123
+ }
124
+ const touchedPaths = [
125
+ ...new Set(plan.manifestEdits.map((edit) => manifestPathOf(projectDir, edit.importerPath))),
126
+ workspaceYamlPath,
127
+ lockPath,
128
+ ];
129
+ const originalSnapshot = captureFiles(touchedPaths);
130
+ const revertTo = (snapshot) => {
131
+ restoreFiles(snapshot);
132
+ resolve();
133
+ };
134
+ const editManifests = (edits) => edits.every((edit) => {
135
+ const path = manifestPathOf(projectDir, edit.importerPath);
136
+ const updated = applyWorkspaceRangeEdit(readFileSync(path, "utf8"), edit);
137
+ if (updated === undefined) {
138
+ log(` ${path} no longer declares "${edit.packageName}": "${edit.range}" — the lockfile is out of date`);
139
+ return false;
140
+ }
141
+ log(` ${path}: "${edit.packageName}" ${edit.range} -> ${edit.to} in ${edit.depType}`);
142
+ writeFileSync(path, updated);
143
+ return true;
144
+ });
145
+ // A reuse fix removes no duplicate — both copies keep their dependents — so
146
+ // the duplicate set cannot tell whether it took. The detector can: the fix is
147
+ // gone from its output once the edge points at the anchored version.
148
+ const readState = () => ({
149
+ duplicates: readDuplicates(lockPath),
150
+ reuses: reuseKeys(readSelectedFixes(projectDir).selected),
151
+ });
152
+ // A step keeps its edits as long as it broke nothing: a widened range often
153
+ // deduplicates nothing on its own and only makes the overrides applicable.
154
+ const resolveAndCheck = (label) => {
155
+ if (resolve() !== 0) {
156
+ log(` \`pnpm dedupe\` failed after ${label} — reverting`);
157
+ return null;
158
+ }
159
+ const state = readState();
160
+ const { added } = diffDuplicates(before, state.duplicates);
161
+ if (added.length > 0) {
162
+ log(` ${label} introduced ${added.length} new duplicate(s) — reverting`);
163
+ return null;
164
+ }
165
+ return state;
166
+ };
167
+ if (plan.manifestEdits.length > 0) {
168
+ log("Editing workspace ranges:");
169
+ if (!editManifests(plan.manifestEdits)) {
170
+ revertTo(originalSnapshot);
171
+ return unchanged("reverted", changeCount);
172
+ }
173
+ if (resolveAndCheck("the workspace range edits") === null) {
174
+ revertTo(originalSnapshot);
175
+ return unchanged("reverted", changeCount);
176
+ }
177
+ }
178
+ const withoutOverridesSnapshot = captureFiles(touchedPaths);
179
+ const workspaceYamlBefore = withoutOverridesSnapshot.find((snapshot) => snapshot.path === workspaceYamlPath);
180
+ const remaining = readState();
181
+ // An override is still worth writing while its package is duplicated, or
182
+ // while the edge it repoints is still resolving elsewhere.
183
+ const outstanding = plannedOverrides.filter((override) => [...remaining.duplicates].some((resolution) => resolution.startsWith(`${override.packageName}@`)) ||
184
+ [...remaining.reuses].some((key) => key.endsWith(`>${override.packageName}@${override.version}`)));
185
+ if (outstanding.length === 0) {
186
+ return {
187
+ status: "applied",
188
+ before,
189
+ after: remaining.duplicates,
190
+ stickyOverrides: [],
191
+ plannedChangeCount: changeCount,
192
+ };
193
+ }
194
+ log(`Adding ${convergenceOverrides ? "convergence" : "plain"} overrides to pnpm-workspace.yaml:`);
195
+ for (const override of outstanding) {
196
+ log(` ${describeOverride(override, convergenceOverrides)} (${override.reason})`);
197
+ }
198
+ writeFileSync(workspaceYamlPath, addOverrides(workspaceYamlBefore.content, new Map(outstanding.map((override) => [override.packageName, override.version])), { convergence: convergenceOverrides }));
199
+ const withOverrides = resolveAndCheck("the overrides");
200
+ if (withOverrides === null) {
201
+ revertTo(withoutOverridesSnapshot);
202
+ return unchanged("reverted", changeCount);
203
+ }
204
+ // Overrides are scaffolding first: if pnpm holds the result without them they
205
+ // are removed, and if it does not they go back with the reason recorded.
206
+ log("Removing the overrides and re-resolving to check the result holds:");
207
+ restoreFiles([workspaceYamlBefore]);
208
+ if (resolve() !== 0) {
209
+ log(" `pnpm dedupe` failed without the overrides — reverting");
210
+ revertTo(withoutOverridesSnapshot);
211
+ return unchanged("reverted", changeCount);
212
+ }
213
+ const after = readState();
214
+ const returnedDuplicates = diffDuplicates(withOverrides.duplicates, after.duplicates).added;
215
+ const returnedReuses = [...after.reuses].filter((key) => !withOverrides.reuses.has(key));
216
+ if (returnedDuplicates.length > 0 || returnedReuses.length > 0) {
217
+ writeFileSync(workspaceYamlPath, addOverrides(workspaceYamlBefore.content, new Map(outstanding.map((override) => [
218
+ override.packageName,
219
+ override.version,
220
+ ])), {
221
+ convergence: convergenceOverrides,
222
+ comment: stickyOverrideReason.join("\n"),
223
+ }));
224
+ if (resolve() !== 0) {
225
+ log(" `pnpm dedupe` failed with the overrides back — reverting");
226
+ revertTo(withoutOverridesSnapshot);
227
+ return unchanged("reverted", changeCount);
228
+ }
229
+ log(` Kept in ${workspaceYamlPath}, with the same explanation as a comment:`);
230
+ for (const override of outstanding) {
231
+ log(` ${describeOverride(override, convergenceOverrides)}`);
232
+ }
233
+ for (const line of stickyOverrideReason) {
234
+ log(` ${line}`);
235
+ }
236
+ return {
237
+ status: "kept-overrides",
238
+ before,
239
+ after: readDuplicates(lockPath),
240
+ stickyOverrides: outstanding,
241
+ plannedChangeCount: changeCount,
242
+ };
243
+ }
244
+ return {
245
+ status: "applied",
246
+ before,
247
+ after: after.duplicates,
248
+ stickyOverrides: [],
249
+ plannedChangeCount: changeCount,
250
+ };
251
+ };
252
+ //# sourceMappingURL=applyClusterFixes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applyClusterFixes.js","sourceRoot":"","sources":["../src/applyClusterFixes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EACL,uBAAuB,EACvB,YAAY,EACZ,mBAAmB,EACnB,yBAAyB,EACzB,cAAc,EACd,+BAA+B,EAC/B,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,cAAc,GACf,MAAM,eAAe,CAAC;AAWvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EACL,8BAA8B,EAC9B,eAAe,EACf,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,SAAS,GAAG,sDAAsD,CAAC;AA2CzE,MAAM,gBAAgB,GAAG,CAAC,UAAkB,EAAgB,EAAE;IAC5D,MAAM,IAAI,GAAG,YAAY,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IAClD,OAAO,oBAAoB,CACzB,IAAI,EACJ,oBAAoB,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,EACjD,oBAAoB,CAAC,UAAU,CAAC,CACjC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,UAAkB,EAAE,YAAoB,EAAU,EAAE,CAC1E,IAAI,CAAC,UAAU,EAAE,YAAY,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;AAE7E,MAAM,gBAAgB,GAAG,CACvB,QAAyB,EACzB,WAAoB,EACZ,EAAE,CACV,IAAI,WAAW,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,OAAO,QAAQ,CAAC,OAAO,GAAG,CAAC;AAE/E;;;;;GAKG;AACH,MAAM,oBAAoB,GAAa;IACrC,sBAAsB;IACtB,8EAA8E;IAC9E,8EAA8E;IAC9E,6EAA6E;IAC7E,wCAAwC,SAAS,EAAE;CACpD,CAAC;AASF,MAAM,SAAS,GAAG,CAAC,KAAmB,EAAe,EAAE,CACrD,IAAI,GAAG,CACL,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CACpB,GAAG,CAAC,UAAU,CAAC,GAAG,CAChB,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,EAAE,EAAE,CACrE,CACF,CACF,CAAC;AAEJ,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,EAChC,UAAU,EACV,MAAM,GAAG,KAAK,EACd,GAAG,GAAG,OAAO,CAAC,GAAG;AACjB,8EAA8E;AAC9E,2EAA2E;AAC3E,4EAA4E;AAC5E,yEAAyE;AACzE,kCAAkC;AAClC,OAAO,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,EAC/D,SAAS,GAAG,gBAAgB,EAC5B,cAAc,GAAG,qBAAqB,EACtC,WAAW,GAAG,GAAG,EAAE,CAAC,eAAe,EAAE,EACrC,oBAAoB,GAAG,IAAI,EAC3B,MAAM,EACN,uBAAuB,EACvB,KAAK,GAAG,cAAc,EAAE,GACC,EAAuB,EAAE;IAClD,MAAM,aAAa,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAClD,MAAM,iBAAiB,GAAG,CAAC,GAAW,EAAwB,EAAE,CAC9D,kBAAkB,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACxC,MAAM,iBAAiB,GAAG,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAAC;IAElE,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,CAChB,MAA0B,EAC1B,kBAAkB,GAAG,CAAC,EACD,EAAE,CAAC,CAAC;QACzB,MAAM;QACN,MAAM;QACN,KAAK,EAAE,MAAM;QACb,eAAe,EAAE,EAAE;QACnB,kBAAkB;KACnB,CAAC,CAAC;IAEH,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,GAC7C,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAErC,MAAM,OAAO,GAAG;QACd,GAAG,WAAW,CAAC,GAAG,CAAC,yBAAyB,CAAC;QAC7C,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAC7B,CAAC,YAAY,EAAE,EAAE,CAAC,GAAG,YAAY,qCAAqC,CACvE;QACD,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CACnB,CAAC,QAAQ,EAAE,EAAE,CACX,GAAG,QAAQ,CAAC,WAAW,aAAa,QAAQ,CAAC,IAAI,cAAc,QAAQ,CAAC,OAAO,2BAA2B,CAC7G;KACF,CAAC;IAEF,uEAAuE;IACvE,2DAA2D;IAC3D,MAAM,gBAAgB,GAAG,oBAAoB;QAC3C,CAAC,CAAC,IAAI,CAAC,SAAS;QAChB,CAAC,CAAC,CAAC,GAAG,EAAE;YACJ,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,+BAA+B,CACxD,KAAK,EACL,IAAI,CAAC,SAAS,CACf,CAAC;YACF,KAAK,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,QAAQ,EAAE,CAAC;gBAChD,KAAK,MAAM,UAAU,IAAI,UAAU,EAAE,CAAC;oBACpC,OAAO,CAAC,IAAI,CACV,YAAY,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,UAAU,CAAC,aAAa,IAAI,WAAW,cAAc,UAAU,CAAC,KAAK,4CAA4C,CACpK,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,EAAE,CAAC;IAET,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC;IAExE,MAAM,oBAAoB,GAAG,CAAC,YAAoB,EAAU,EAAE,CAC5D,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;IAEjE,MAAM,eAAe,GAAG,GAA0B,EAAE,CAAC;QACnD,GAAG;YACD,GAAG,IAAI,GAAG,CACR,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC9B,oBAAoB,CAAC,IAAI,CAAC,YAAY,CAAC,CACxC,CACF;SACF,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACf,IAAI;YACJ,OAAO,EAAE,IAAI,CAAC,aAAa;iBACxB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;iBAClE,GAAG,CACF,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,IAAI,CAAC,WAAW,OAAO,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC,OAAO,GAAG,CAC7E;SACJ,CAAC,CAAC;QACH;YACE,IAAI,EAAE,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;YAC7C,SAAS,EAAE,qCAAqC;YAChD,OAAO,EAAE,gBAAgB,CAAC,GAAG,CAC3B,CAAC,QAAQ,EAAE,EAAE,CACX,GAAG,gBAAgB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,KAAK,QAAQ,CAAC,MAAM,GAAG,CAC7E;SACF;KACF,CAAC;IAEF,4EAA4E;IAC5E,6EAA6E;IAC7E,kCAAkC;IAClC,MAAM,UAAU,GAAG,CAAC,UAAmB,EAAuB,EAAE;QAC9D,eAAe,CAAC;YACd,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE;YAChD,OAAO;YACP,uBAAuB;YACvB,aAAa,EAAE,aAAa;YAC5B,GAAG;YACH,KAAK;SACN,CAAC,CAAC;QACH,OAAO,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC;IAEF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,GAAG,CAAC,aAAa,KAAK,EAAE,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,oBAAoB,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;QAC9B,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,4BAA4B,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/D,GAAG,CACD,8BAA8B,8BAA8B,mCAAmC,OAAO,IAAI,SAAS,kBAAkB,CACtI,CAAC;YACF,oEAAoE;YACpE,OAAO,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,YAAY,GAAG;QACnB,GAAG,IAAI,GAAG,CACR,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC9B,cAAc,CAAC,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,CAC9C,CACF;QACD,iBAAiB;QACjB,QAAQ;KACT,CAAC;IAEF,MAAM,gBAAgB,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAEpD,MAAM,QAAQ,GAAG,CAAC,QAAwB,EAAQ,EAAE;QAClD,YAAY,CAAC,QAAQ,CAAC,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,CAAC,KAA4B,EAAW,EAAE,CAC9D,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE;QACnB,MAAM,IAAI,GAAG,cAAc,CAAC,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,uBAAuB,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1E,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,GAAG,CACD,KAAK,IAAI,wBAAwB,IAAI,CAAC,WAAW,OAAO,IAAI,CAAC,KAAK,iCAAiC,CACpG,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QACD,GAAG,CACD,KAAK,IAAI,MAAM,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,EAAE,OAAO,IAAI,CAAC,OAAO,EAAE,CAClF,CAAC;QACF,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IAEL,4EAA4E;IAC5E,8EAA8E;IAC9E,qEAAqE;IACrE,MAAM,SAAS,GAAG,GAAe,EAAE,CAAC,CAAC;QACnC,UAAU,EAAE,cAAc,CAAC,QAAQ,CAAC;QACpC,MAAM,EAAE,SAAS,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC;KAC1D,CAAC,CAAC;IAEH,4EAA4E;IAC5E,2EAA2E;IAC3E,MAAM,eAAe,GAAG,CAAC,KAAa,EAAqB,EAAE;QAC3D,IAAI,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;YACpB,GAAG,CAAC,kCAAkC,KAAK,cAAc,CAAC,CAAC;YAC3D,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;QAC1B,MAAM,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAC3D,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,GAAG,CAAC,KAAK,KAAK,eAAe,KAAK,CAAC,MAAM,+BAA+B,CAAC,CAAC;YAC1E,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QACjC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;YACvC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;YAC3B,OAAO,SAAS,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,eAAe,CAAC,2BAA2B,CAAC,KAAK,IAAI,EAAE,CAAC;YAC1D,QAAQ,CAAC,gBAAgB,CAAC,CAAC;YAC3B,OAAO,SAAS,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,MAAM,wBAAwB,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAC5D,MAAM,mBAAmB,GAAG,wBAAwB,CAAC,IAAI,CACvD,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,KAAK,iBAAiB,CACjD,CAAC;IAEH,MAAM,SAAS,GAAG,SAAS,EAAE,CAAC;IAC9B,yEAAyE;IACzE,2DAA2D;IAC3D,MAAM,WAAW,GAAG,gBAAgB,CAAC,MAAM,CACzC,CAAC,QAAQ,EAAE,EAAE,CACX,CAAC,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAC5C,UAAU,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,WAAW,GAAG,CAAC,CAClD;QACD,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CACjC,GAAG,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC,CAC7D,CACJ,CAAC;IAEF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,MAAM,EAAE,SAAS;YACjB,MAAM;YACN,KAAK,EAAE,SAAS,CAAC,UAAU;YAC3B,eAAe,EAAE,EAAE;YACnB,kBAAkB,EAAE,WAAW;SAChC,CAAC;IACJ,CAAC;IAED,GAAG,CACD,UAAU,oBAAoB,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,oCAAoC,CAC7F,CAAC;IACF,KAAK,MAAM,QAAQ,IAAI,WAAW,EAAE,CAAC;QACnC,GAAG,CACD,KAAK,gBAAgB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,KAAK,QAAQ,CAAC,MAAM,GAAG,CAC7E,CAAC;IACJ,CAAC;IACD,aAAa,CACX,iBAAiB,EACjB,YAAY,CACV,mBAAmB,CAAC,OAAO,EAC3B,IAAI,GAAG,CACL,WAAW,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CACxE,EACD,EAAE,WAAW,EAAE,oBAAoB,EAAE,CACtC,CACF,CAAC;IAEF,MAAM,aAAa,GAAG,eAAe,CAAC,eAAe,CAAC,CAAC;IACvD,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC3B,QAAQ,CAAC,wBAAwB,CAAC,CAAC;QACnC,OAAO,SAAS,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAC5C,CAAC;IAED,8EAA8E;IAC9E,yEAAyE;IACzE,GAAG,CAAC,oEAAoE,CAAC,CAAC;IAC1E,YAAY,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAEpC,IAAI,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;QACpB,GAAG,CAAC,0DAA0D,CAAC,CAAC;QAChE,QAAQ,CAAC,wBAAwB,CAAC,CAAC;QACnC,OAAO,SAAS,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,kBAAkB,GAAG,cAAc,CACvC,aAAa,CAAC,UAAU,EACxB,KAAK,CAAC,UAAU,CACjB,CAAC,KAAK,CAAC;IACR,MAAM,cAAc,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAC7C,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CACxC,CAAC;IAEF,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/D,aAAa,CACX,iBAAiB,EACjB,YAAY,CACV,mBAAmB,CAAC,OAAO,EAC3B,IAAI,GAAG,CACL,WAAW,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;YAC5B,QAAQ,CAAC,WAAW;YACpB,QAAQ,CAAC,OAAO;SACjB,CAAC,CACH,EACD;YACE,WAAW,EAAE,oBAAoB;YACjC,OAAO,EAAE,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC;SACzC,CACF,CACF,CAAC;QAEF,IAAI,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;YACpB,GAAG,CAAC,4DAA4D,CAAC,CAAC;YAClE,QAAQ,CAAC,wBAAwB,CAAC,CAAC;YACnC,OAAO,SAAS,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAC5C,CAAC;QAED,GAAG,CACD,aAAa,iBAAiB,2CAA2C,CAC1E,CAAC;QACF,KAAK,MAAM,QAAQ,IAAI,WAAW,EAAE,CAAC;YACnC,GAAG,CAAC,OAAO,gBAAgB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,EAAE,CAAC,CAAC;QACjE,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,oBAAoB,EAAE,CAAC;YACxC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QACnB,CAAC;QAED,OAAO;YACL,MAAM,EAAE,gBAAgB;YACxB,MAAM;YACN,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC;YAC/B,eAAe,EAAE,WAAW;YAC5B,kBAAkB,EAAE,WAAW;SAChC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM,EAAE,SAAS;QACjB,MAAM;QACN,KAAK,EAAE,KAAK,CAAC,UAAU;QACvB,eAAe,EAAE,EAAE;QACnB,kBAAkB,EAAE,WAAW;KAChC,CAAC;AACJ,CAAC,CAAC"}