@pnpm/installing.deps-installer 1102.2.2 → 1102.3.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,62 @@
1
1
  # @pnpm/core
2
2
 
3
+ ## 1102.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added a new override selector form with an empty range — `"pkg@": "<version>"` — called a convergence override. It rewrites a dependency edge only when its exact version satisfies the edge's declared range, so compatible consumers converge on one version while incompatible consumers keep their own resolution — now and for any dependent added in the future [#12794](https://github.com/pnpm/pnpm/issues/12794).
8
+
9
+ ```yaml
10
+ overrides:
11
+ "form-data@": 4.0.6
12
+ ```
13
+
14
+ The value must be an exact version. When a full resolution detects that every declared range also admits a newer version, pnpm warns that the override is stale and names the version to converge on. Previously an empty range in an override selector was undocumented and behaved like a bare (unscoped) override.
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies:
19
+ - @pnpm/bins.linker@1100.0.17
20
+ - @pnpm/bins.remover@1100.0.13
21
+ - @pnpm/building.after-install@1102.0.6
22
+ - @pnpm/building.during-install@1102.0.6
23
+ - @pnpm/building.policy@1100.0.13
24
+ - @pnpm/config.normalize-registries@1100.0.9
25
+ - @pnpm/config.parse-overrides@1100.1.0
26
+ - @pnpm/config.version-policy@1100.1.7
27
+ - @pnpm/core-loggers@1100.2.2
28
+ - @pnpm/deps.graph-hasher@1100.2.10
29
+ - @pnpm/deps.path@1100.0.9
30
+ - @pnpm/exec.lifecycle@1100.1.3
31
+ - @pnpm/fs.symlink-dependency@1100.0.11
32
+ - @pnpm/hooks.read-package-hook@1100.1.0
33
+ - @pnpm/hooks.types@1100.2.1
34
+ - @pnpm/installing.context@1100.0.24
35
+ - @pnpm/installing.deps-resolver@1100.2.9
36
+ - @pnpm/installing.deps-restorer@1102.1.5
37
+ - @pnpm/installing.linking.direct-dep-linker@1100.0.11
38
+ - @pnpm/installing.linking.hoist@1100.0.17
39
+ - @pnpm/installing.linking.modules-cleaner@1100.1.12
40
+ - @pnpm/installing.modules-yaml@1100.0.10
41
+ - @pnpm/installing.package-requester@1102.1.3
42
+ - @pnpm/lockfile.filtering@1100.1.11
43
+ - @pnpm/lockfile.fs@1100.1.11
44
+ - @pnpm/lockfile.preferred-versions@1100.0.20
45
+ - @pnpm/lockfile.pruner@1100.0.14
46
+ - @pnpm/lockfile.settings-checker@1100.1.3
47
+ - @pnpm/lockfile.to-pnp@1100.1.5
48
+ - @pnpm/lockfile.utils@1100.1.3
49
+ - @pnpm/lockfile.verification@1100.0.24
50
+ - @pnpm/lockfile.walker@1100.0.14
51
+ - @pnpm/network.auth-header@1101.1.4
52
+ - @pnpm/patching.config@1100.0.10
53
+ - @pnpm/pkg-manifest.utils@1100.2.7
54
+ - @pnpm/pnpr.client@1.3.3
55
+ - @pnpm/resolving.resolver-base@1100.5.2
56
+ - @pnpm/store.controller-types@1100.1.8
57
+ - @pnpm/types@1101.4.0
58
+ - @pnpm/workspace.project-manifest-reader@1100.0.15
59
+
3
60
  ## 1102.2.2
4
61
 
5
62
  ### Patch Changes
@@ -259,5 +259,11 @@ export type InstallOptions = Partial<StrictInstallOptions> & Pick<StrictInstallO
259
259
  export interface ProcessedInstallOptions extends StrictInstallOptions {
260
260
  readPackageHook?: ReadPackageHook;
261
261
  parsedOverrides: VersionOverride[];
262
+ /**
263
+ * Present when the overrides contain convergence entries (`"pkg@"`). The
264
+ * versions overrider fills it with every declared semver range it sees for
265
+ * those packages; the staleness check reads it after a full resolution.
266
+ */
267
+ convergeDeclaredRanges?: Map<string, Set<string>>;
262
268
  }
263
269
  export declare function extendOptions(opts: InstallOptions): ProcessedInstallOptions;
@@ -124,10 +124,14 @@ export function extendOptions(opts) {
124
124
  storeDir: defaultOpts.storeDir,
125
125
  parsedOverrides: parseOverrides(opts.overrides ?? {}, opts.catalogs ?? {}),
126
126
  };
127
+ if (extendedOpts.parsedOverrides.some(({ converge }) => converge)) {
128
+ extendedOpts.convergeDeclaredRanges = new Map();
129
+ }
127
130
  extendedOpts.readPackageHook = createReadPackageHook({
128
131
  ignoreCompatibilityDb: extendedOpts.ignoreCompatibilityDb,
129
132
  readPackageHook: extendedOpts.hooks?.readPackage,
130
133
  overrides: extendedOpts.parsedOverrides,
134
+ convergeDeclaredRanges: extendedOpts.convergeDeclaredRanges,
131
135
  lockfileDir: extendedOpts.lockfileDir,
132
136
  packageExtensions: extendedOpts.packageExtensions,
133
137
  ignoredOptionalDependencies: extendedOpts.ignoredOptionalDependencies,
@@ -41,6 +41,7 @@ import { linkPackages } from './link.js';
41
41
  import { reportPeerDependencyIssues } from './reportPeerDependencyIssues.js';
42
42
  import { validateModules } from './validateModules.js';
43
43
  import { verifyLockfileResolutions } from './verifyLockfileResolutions.js';
44
+ import { warnOnStaleConvergenceOverrides } from './warnOnStaleConvergenceOverrides.js';
44
45
  import { writeLockfilesAndRecordVerified } from './writeLockfilesAndRecordVerified.js';
45
46
  import { writeWantedLockfileAndRecordVerified } from './writeWantedLockfileAndRecordVerified.js';
46
47
  class LockfileConfigMismatchError extends PnpmError {
@@ -1167,6 +1168,20 @@ const _installInContext = async (projects, ctx, opts) => {
1167
1168
  allProjectIds: Object.values(ctx.projects).map((p) => p.id),
1168
1169
  handleResolutionPolicyViolations: opts.handleResolutionPolicyViolations,
1169
1170
  });
1171
+ // Only a full resolution walks every manifest through the versions
1172
+ // overrider, making the collected declared ranges complete enough for the
1173
+ // staleness verdict; partial resolutions must stay silent to avoid false
1174
+ // positives from unseen ranges.
1175
+ if (opts.convergeDeclaredRanges != null && (forceFullResolution || opts.dedupe)) {
1176
+ await warnOnStaleConvergenceOverrides({
1177
+ convergeDeclaredRanges: opts.convergeDeclaredRanges,
1178
+ parsedOverrides: opts.parsedOverrides,
1179
+ requestPackage: opts.storeController.requestPackage,
1180
+ lockfileDir: opts.lockfileDir,
1181
+ minimumReleaseAge: opts.minimumReleaseAge,
1182
+ minimumReleaseAgeExclude: opts.minimumReleaseAgeExclude,
1183
+ });
1184
+ }
1170
1185
  if (!opts.include.optionalDependencies || !opts.include.devDependencies || !opts.include.dependencies) {
1171
1186
  linkedDependenciesByProjectId = mapValues((linkedDeps) => linkedDeps.filter((linkedDep) => !(linkedDep.dev && !opts.include.devDependencies ||
1172
1187
  linkedDep.optional && !opts.include.optionalDependencies ||
@@ -0,0 +1,31 @@
1
+ import type { VersionOverride } from '@pnpm/config.parse-overrides';
2
+ import type { RequestPackageFunction } from '@pnpm/store.controller-types';
3
+ export interface WarnOnStaleConvergenceOverridesOptions {
4
+ convergeDeclaredRanges: Map<string, Set<string>>;
5
+ parsedOverrides: VersionOverride[];
6
+ requestPackage: RequestPackageFunction;
7
+ lockfileDir: string;
8
+ minimumReleaseAge?: number;
9
+ minimumReleaseAgeExclude?: string[];
10
+ }
11
+ /**
12
+ * A convergence override's value is derived state — the best version that
13
+ * converges the currently declared ranges. It goes stale when dependents
14
+ * start declaring ranges that admit newer versions: the override then keeps
15
+ * the edges it governs on the old version while newer edges resolve past it,
16
+ * producing the duplication the entry was written to prevent.
17
+ *
18
+ * Must only run after a full resolution: only then has every manifest
19
+ * streamed through the versions overrider, so `convergeDeclaredRanges` holds
20
+ * the complete set of declared ranges. For each convergence override this
21
+ * resolves every declared range to the best version it admits (metadata is
22
+ * already cached from the resolution that just ran, and the release-age
23
+ * policy applies, so the recommendation is a version the resolver would
24
+ * actually pick) and warns when a version newer than the override's value
25
+ * satisfies every declared range — a strictly better convergence.
26
+ *
27
+ * A range that fails to resolve contributes no candidate but still
28
+ * participates in the satisfies-every-range check, so failures can only
29
+ * suppress the warning, never fabricate one.
30
+ */
31
+ export declare function warnOnStaleConvergenceOverrides(opts: WarnOnStaleConvergenceOverridesOptions): Promise<void>;
@@ -0,0 +1,62 @@
1
+ import { getPublishedByPolicy } from '@pnpm/config.version-policy';
2
+ import { globalWarn } from '@pnpm/logger';
3
+ import semver from 'semver';
4
+ /**
5
+ * A convergence override's value is derived state — the best version that
6
+ * converges the currently declared ranges. It goes stale when dependents
7
+ * start declaring ranges that admit newer versions: the override then keeps
8
+ * the edges it governs on the old version while newer edges resolve past it,
9
+ * producing the duplication the entry was written to prevent.
10
+ *
11
+ * Must only run after a full resolution: only then has every manifest
12
+ * streamed through the versions overrider, so `convergeDeclaredRanges` holds
13
+ * the complete set of declared ranges. For each convergence override this
14
+ * resolves every declared range to the best version it admits (metadata is
15
+ * already cached from the resolution that just ran, and the release-age
16
+ * policy applies, so the recommendation is a version the resolver would
17
+ * actually pick) and warns when a version newer than the override's value
18
+ * satisfies every declared range — a strictly better convergence.
19
+ *
20
+ * A range that fails to resolve contributes no candidate but still
21
+ * participates in the satisfies-every-range check, so failures can only
22
+ * suppress the warning, never fabricate one.
23
+ */
24
+ export async function warnOnStaleConvergenceOverrides(opts) {
25
+ const convergeOverrides = opts.parsedOverrides.filter(({ converge }) => converge);
26
+ if (convergeOverrides.length === 0)
27
+ return;
28
+ const { publishedBy, publishedByExclude } = getPublishedByPolicy(opts);
29
+ await Promise.all(convergeOverrides.map(async (override) => {
30
+ const name = override.targetPkg.name;
31
+ const ranges = opts.convergeDeclaredRanges.get(name);
32
+ if (ranges == null || ranges.size === 0)
33
+ return;
34
+ const candidates = await Promise.all([...ranges].map(async (range) => {
35
+ try {
36
+ const response = await opts.requestPackage({ alias: name, bareSpecifier: range }, {
37
+ downloadPriority: 0,
38
+ lockfileDir: opts.lockfileDir,
39
+ projectDir: opts.lockfileDir,
40
+ preferredVersions: {},
41
+ skipFetch: true,
42
+ publishedBy,
43
+ publishedByExclude,
44
+ });
45
+ if (response.body.policyViolation != null)
46
+ return undefined;
47
+ return response.body.manifest?.version;
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ }));
53
+ const best = candidates
54
+ .filter((version) => version != null && semver.gt(version, override.newBareSpecifier))
55
+ .sort(semver.rcompare)
56
+ .find((version) => [...ranges].every((range) => semver.satisfies(version, range, true)));
57
+ if (best == null)
58
+ return;
59
+ globalWarn(`The convergence override "${name}@": "${override.newBareSpecifier}" is stale: every declared range of ${name} also admits ${best}. Change the override's value to ${best} in pnpm-workspace.yaml, or remove the override and run "pnpm dedupe".`);
60
+ }));
61
+ }
62
+ //# sourceMappingURL=warnOnStaleConvergenceOverrides.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/installing.deps-installer",
3
- "version": "1102.2.2",
3
+ "version": "1102.3.0",
4
4
  "description": "Fast, disk space efficient installation engine",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -51,60 +51,61 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "@inquirer/prompts": "^8.4.3",
54
- "@pnpm/bins.linker": "1100.0.16",
55
- "@pnpm/bins.remover": "1100.0.12",
56
- "@pnpm/building.after-install": "1102.0.5",
57
- "@pnpm/building.during-install": "1102.0.5",
58
- "@pnpm/building.policy": "1100.0.12",
54
+ "@pnpm/bins.linker": "1100.0.17",
55
+ "@pnpm/bins.remover": "1100.0.13",
56
+ "@pnpm/building.after-install": "1102.0.6",
57
+ "@pnpm/building.during-install": "1102.0.6",
58
+ "@pnpm/building.policy": "1100.0.13",
59
59
  "@pnpm/catalogs.config": "1100.0.2",
60
60
  "@pnpm/catalogs.protocol-parser": "1100.0.0",
61
61
  "@pnpm/catalogs.resolver": "1100.0.0",
62
62
  "@pnpm/catalogs.types": "1100.0.0",
63
63
  "@pnpm/config.matcher": "1100.0.1",
64
- "@pnpm/config.normalize-registries": "1100.0.8",
65
- "@pnpm/config.parse-overrides": "1100.0.2",
64
+ "@pnpm/config.normalize-registries": "1100.0.9",
65
+ "@pnpm/config.parse-overrides": "1100.1.0",
66
+ "@pnpm/config.version-policy": "1100.1.7",
66
67
  "@pnpm/constants": "1100.0.0",
67
- "@pnpm/core-loggers": "1100.2.1",
68
+ "@pnpm/core-loggers": "1100.2.2",
68
69
  "@pnpm/crypto.hash": "1100.0.1",
69
70
  "@pnpm/crypto.object-hasher": "1100.0.0",
70
- "@pnpm/deps.graph-hasher": "1100.2.9",
71
+ "@pnpm/deps.graph-hasher": "1100.2.10",
71
72
  "@pnpm/deps.graph-sequencer": "1100.0.0",
72
- "@pnpm/deps.path": "1100.0.8",
73
+ "@pnpm/deps.path": "1100.0.9",
73
74
  "@pnpm/error": "1100.0.1",
74
- "@pnpm/exec.lifecycle": "1100.1.2",
75
+ "@pnpm/exec.lifecycle": "1100.1.3",
75
76
  "@pnpm/fs.read-modules-dir": "1100.0.1",
76
- "@pnpm/fs.symlink-dependency": "1100.0.10",
77
- "@pnpm/hooks.read-package-hook": "1100.0.9",
78
- "@pnpm/hooks.types": "1100.2.0",
79
- "@pnpm/installing.context": "1100.0.23",
80
- "@pnpm/installing.deps-resolver": "1100.2.8",
81
- "@pnpm/installing.deps-restorer": "1102.1.4",
82
- "@pnpm/installing.linking.direct-dep-linker": "1100.0.10",
83
- "@pnpm/installing.linking.hoist": "1100.0.16",
84
- "@pnpm/installing.linking.modules-cleaner": "1100.1.11",
85
- "@pnpm/installing.modules-yaml": "1100.0.9",
86
- "@pnpm/installing.package-requester": "1102.1.2",
87
- "@pnpm/lockfile.filtering": "1100.1.10",
88
- "@pnpm/lockfile.fs": "1100.1.10",
89
- "@pnpm/lockfile.preferred-versions": "1100.0.19",
90
- "@pnpm/lockfile.pruner": "1100.0.13",
91
- "@pnpm/lockfile.settings-checker": "1100.1.2",
92
- "@pnpm/lockfile.to-pnp": "1100.1.4",
93
- "@pnpm/lockfile.utils": "1100.1.2",
94
- "@pnpm/lockfile.verification": "1100.0.23",
95
- "@pnpm/lockfile.walker": "1100.0.13",
96
- "@pnpm/network.auth-header": "1101.1.3",
77
+ "@pnpm/fs.symlink-dependency": "1100.0.11",
78
+ "@pnpm/hooks.read-package-hook": "1100.1.0",
79
+ "@pnpm/hooks.types": "1100.2.1",
80
+ "@pnpm/installing.context": "1100.0.24",
81
+ "@pnpm/installing.deps-resolver": "1100.2.9",
82
+ "@pnpm/installing.deps-restorer": "1102.1.5",
83
+ "@pnpm/installing.linking.direct-dep-linker": "1100.0.11",
84
+ "@pnpm/installing.linking.hoist": "1100.0.17",
85
+ "@pnpm/installing.linking.modules-cleaner": "1100.1.12",
86
+ "@pnpm/installing.modules-yaml": "1100.0.10",
87
+ "@pnpm/installing.package-requester": "1102.1.3",
88
+ "@pnpm/lockfile.filtering": "1100.1.11",
89
+ "@pnpm/lockfile.fs": "1100.1.11",
90
+ "@pnpm/lockfile.preferred-versions": "1100.0.20",
91
+ "@pnpm/lockfile.pruner": "1100.0.14",
92
+ "@pnpm/lockfile.settings-checker": "1100.1.3",
93
+ "@pnpm/lockfile.to-pnp": "1100.1.5",
94
+ "@pnpm/lockfile.utils": "1100.1.3",
95
+ "@pnpm/lockfile.verification": "1100.0.24",
96
+ "@pnpm/lockfile.walker": "1100.0.14",
97
+ "@pnpm/network.auth-header": "1101.1.4",
97
98
  "@pnpm/npm-package-arg": "^2.0.0",
98
- "@pnpm/patching.config": "1100.0.9",
99
- "@pnpm/pkg-manifest.utils": "1100.2.6",
100
- "@pnpm/pnpr.client": "1.3.2",
99
+ "@pnpm/patching.config": "1100.0.10",
100
+ "@pnpm/pkg-manifest.utils": "1100.2.7",
101
+ "@pnpm/pnpr.client": "1.3.3",
101
102
  "@pnpm/resolving.parse-wanted-dependency": "1100.0.1",
102
- "@pnpm/resolving.resolver-base": "1100.5.1",
103
- "@pnpm/store.controller-types": "1100.1.7",
103
+ "@pnpm/resolving.resolver-base": "1100.5.2",
104
+ "@pnpm/store.controller-types": "1100.1.8",
104
105
  "@pnpm/store.index": "1100.2.1",
105
- "@pnpm/types": "1101.3.2",
106
+ "@pnpm/types": "1101.4.0",
106
107
  "@pnpm/util.lex-comparator": "^4.0.1",
107
- "@pnpm/workspace.project-manifest-reader": "1100.0.14",
108
+ "@pnpm/workspace.project-manifest-reader": "1100.0.15",
108
109
  "@zkochan/rimraf": "^4.0.0",
109
110
  "is-inner-link": "^5.0.0",
110
111
  "is-subdir": "^2.0.0",
@@ -120,26 +121,26 @@
120
121
  },
121
122
  "peerDependencies": {
122
123
  "@pnpm/logger": "^1100.0.0",
123
- "@pnpm/worker": "^1100.2.3"
124
+ "@pnpm/worker": "^1100.2.4"
124
125
  },
125
126
  "devDependencies": {
126
127
  "@jest/globals": "30.4.1",
127
- "@pnpm/assert-project": "1100.0.18",
128
- "@pnpm/assert-store": "1100.0.18",
129
- "@pnpm/installing.deps-installer": "1102.2.2",
130
- "@pnpm/lockfile.types": "1100.0.13",
128
+ "@pnpm/assert-project": "1100.0.19",
129
+ "@pnpm/assert-store": "1100.0.19",
130
+ "@pnpm/installing.deps-installer": "1102.3.0",
131
+ "@pnpm/lockfile.types": "1100.0.14",
131
132
  "@pnpm/logger": "1100.0.0",
132
133
  "@pnpm/network.git-utils": "1100.0.2",
133
- "@pnpm/pkg-manifest.reader": "1100.0.9",
134
- "@pnpm/prepare": "1100.0.18",
135
- "@pnpm/resolving.registry.types": "1100.1.3",
136
- "@pnpm/store.cafs": "1100.1.12",
134
+ "@pnpm/pkg-manifest.reader": "1100.0.10",
135
+ "@pnpm/prepare": "1100.0.19",
136
+ "@pnpm/resolving.registry.types": "1100.1.4",
137
+ "@pnpm/store.cafs": "1100.1.13",
137
138
  "@pnpm/store.path": "1100.0.2",
138
139
  "@pnpm/test-fixtures": "1100.0.0",
139
140
  "@pnpm/test-ipc-server": "1100.0.0",
140
- "@pnpm/testing.mock-agent": "1101.0.4",
141
- "@pnpm/testing.registry-mock": "1100.0.8",
142
- "@pnpm/testing.temp-store": "1100.1.14",
141
+ "@pnpm/testing.mock-agent": "1101.0.5",
142
+ "@pnpm/testing.registry-mock": "1100.0.9",
143
+ "@pnpm/testing.temp-store": "1100.1.15",
143
144
  "@types/fs-extra": "^11.0.4",
144
145
  "@types/is-windows": "^1.0.2",
145
146
  "@types/normalize-path": "^3.0.2",