@pnpm/installing.deps-installer 1101.6.1 → 1101.8.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/lib/install/extendInstallOptions.d.ts +5 -2
- package/lib/install/index.js +118 -136
- package/lib/install/link.js +1 -1
- package/lib/install/recordLockfileVerified.js +2 -1
- package/lib/install/verifyLockfileResolutions.d.ts +11 -0
- package/lib/install/verifyLockfileResolutions.js +97 -18
- package/package.json +60 -56
|
@@ -229,8 +229,11 @@ export interface StrictInstallOptions {
|
|
|
229
229
|
* summary at the very end instead of one summary per install.
|
|
230
230
|
*/
|
|
231
231
|
omitSummaryLog: boolean;
|
|
232
|
-
/**
|
|
233
|
-
|
|
232
|
+
/**
|
|
233
|
+
* URL of a pnpr server that resolves dependencies server-side and serves
|
|
234
|
+
* only the files missing from the client's store.
|
|
235
|
+
*/
|
|
236
|
+
pnprServer?: string;
|
|
234
237
|
}
|
|
235
238
|
export type InstallOptions = Partial<StrictInstallOptions> & Pick<StrictInstallOptions, 'storeDir' | 'storeController'>;
|
|
236
239
|
export interface ProcessedInstallOptions extends StrictInstallOptions {
|
package/lib/install/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { getContext } from '@pnpm/installing.context';
|
|
|
15
15
|
import { getWantedDependencies, resolveDependencies, } from '@pnpm/installing.deps-resolver';
|
|
16
16
|
import { extendProjectsWithTargetDirs, headlessInstall } from '@pnpm/installing.deps-restorer';
|
|
17
17
|
import { writeModulesManifest } from '@pnpm/installing.modules-yaml';
|
|
18
|
-
import { cleanGitBranchLockfiles, getWantedLockfileName, isEmptyLockfile,
|
|
18
|
+
import { cleanGitBranchLockfiles, getWantedLockfileName, isEmptyLockfile, readWantedLockfileFile, writeCurrentLockfile, writeLockfiles, writeWantedLockfile, } from '@pnpm/lockfile.fs';
|
|
19
19
|
import { getPreferredVersionsFromLockfileAndManifests } from '@pnpm/lockfile.preferred-versions';
|
|
20
20
|
import { calcPatchHashes, createOverridesMapFromParsed, getOutdatedLockfileSetting, } from '@pnpm/lockfile.settings-checker';
|
|
21
21
|
import { writePnpFile } from '@pnpm/lockfile.to-pnp';
|
|
@@ -55,10 +55,10 @@ const BROKEN_LOCKFILE_INTEGRITY_ERRORS = new Set([
|
|
|
55
55
|
const DEV_PREINSTALL = 'pnpm:devPreinstall';
|
|
56
56
|
export async function install(manifest, opts) {
|
|
57
57
|
const rootDir = (opts.dir ?? process.cwd());
|
|
58
|
-
// When a
|
|
58
|
+
// When a pnpr server is configured, use server-side resolution
|
|
59
59
|
// instead of the normal resolution flow.
|
|
60
|
-
if (opts.
|
|
61
|
-
return
|
|
60
|
+
if (opts.pnprServer) {
|
|
61
|
+
return installViaPnprServer(manifest, rootDir, opts);
|
|
62
62
|
}
|
|
63
63
|
const { updatedCatalogs, updatedProjects: projects, ignoredBuilds, resolutionPolicyViolations } = await mutateModules([
|
|
64
64
|
{
|
|
@@ -120,14 +120,14 @@ export async function mutateModules(projects, maybeOpts) {
|
|
|
120
120
|
streamParser.on('data', reporter);
|
|
121
121
|
}
|
|
122
122
|
const opts = extendOptions(maybeOpts);
|
|
123
|
-
// When a
|
|
123
|
+
// When a pnpr server is configured, use server-side resolution. The pnpr server
|
|
124
124
|
// path supports `install`, `installSome` (pnpm add), and `uninstallSome`
|
|
125
125
|
// (pnpm remove). Mutations that need full client-side resolution (update
|
|
126
126
|
// flags) still fall through to the normal flow.
|
|
127
|
-
if (opts.
|
|
128
|
-
const
|
|
129
|
-
if (
|
|
130
|
-
return
|
|
127
|
+
if (opts.pnprServer && canUsePnprForMutations(projects)) {
|
|
128
|
+
const pnprResult = await mutateModulesViaPnpr(projects, opts);
|
|
129
|
+
if (pnprResult)
|
|
130
|
+
return pnprResult;
|
|
131
131
|
}
|
|
132
132
|
const allowBuild = createAllowBuildFunction(opts);
|
|
133
133
|
if (!opts.include.dependencies && opts.include.optionalDependencies) {
|
|
@@ -262,7 +262,7 @@ export async function mutateModules(projects, maybeOpts) {
|
|
|
262
262
|
}
|
|
263
263
|
let ignoredBuilds = result.ignoredBuilds;
|
|
264
264
|
if (!opts.ignoreScripts && ignoredBuilds?.size) {
|
|
265
|
-
ignoredBuilds = await runUnignoredDependencyBuilds(opts, ignoredBuilds, allowBuild);
|
|
265
|
+
ignoredBuilds = await runUnignoredDependencyBuilds(opts, ignoredBuilds, ctx.wantedLockfile, allowBuild);
|
|
266
266
|
}
|
|
267
267
|
// Detect packages whose build approval was revoked between the previous
|
|
268
268
|
// and current install. A package is considered revoked when it was
|
|
@@ -277,10 +277,13 @@ export async function mutateModules(projects, maybeOpts) {
|
|
|
277
277
|
for (const depPath of Object.keys(ctx.wantedLockfile.packages)) {
|
|
278
278
|
if (ignoredBuilds?.has(depPath))
|
|
279
279
|
continue;
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
// The old policy is evaluated with identity trust overridden so that
|
|
281
|
+
// package-name approvals count as they did when they were granted,
|
|
282
|
+
// even for git/tarball artifacts that the current policy no longer
|
|
283
|
+
// approves by name.
|
|
284
|
+
if (oldAllowBuild(depPath, { trustPackageIdentity: true }) !== true)
|
|
282
285
|
continue;
|
|
283
|
-
if (
|
|
286
|
+
if (allowBuild?.(depPath) === undefined) {
|
|
284
287
|
ignoredBuilds ??= new Set();
|
|
285
288
|
ignoredBuilds.add(depPath);
|
|
286
289
|
}
|
|
@@ -536,14 +539,21 @@ export async function mutateModules(projects, maybeOpts) {
|
|
|
536
539
|
});
|
|
537
540
|
if (opts.catalogMode !== 'manual') {
|
|
538
541
|
for (const wantedDep of wantedDeps) {
|
|
542
|
+
// A `runtime:` specifier (e.g. node from `devEngines.runtime` or
|
|
543
|
+
// `pnpm runtime set`) round-trips to `devEngines.runtime` through the
|
|
544
|
+
// manifest writer, which only recognizes the `runtime:` protocol.
|
|
545
|
+
// Promoting it into a catalog rewrites the entry to `catalog:`, which
|
|
546
|
+
// breaks that round-trip and strands it in `devDependencies`.
|
|
547
|
+
if (wantedDep.bareSpecifier?.startsWith('runtime:'))
|
|
548
|
+
continue;
|
|
539
549
|
const perDepCatalogName = getPerDepCatalogName(wantedDep, opts.saveCatalogName);
|
|
540
550
|
const catalogBareSpecifier = `catalog:${perDepCatalogName === 'default' ? '' : perDepCatalogName}`;
|
|
541
551
|
const catalog = resolveFromCatalog(opts.catalogs, { ...wantedDep, bareSpecifier: catalogBareSpecifier });
|
|
542
552
|
const catalogDepSpecifier = matchCatalogResolveResult(catalog, pickCatalogSpecifier);
|
|
543
553
|
if (!catalogDepSpecifier ||
|
|
544
554
|
wantedDep.bareSpecifier === catalogBareSpecifier ||
|
|
545
|
-
semver.
|
|
546
|
-
semver.
|
|
555
|
+
semver.valid(wantedDep.bareSpecifier) &&
|
|
556
|
+
semver.valid(catalogDepSpecifier) &&
|
|
547
557
|
semver.eq(wantedDep.bareSpecifier, catalogDepSpecifier)) {
|
|
548
558
|
wantedDep.saveCatalogName = perDepCatalogName;
|
|
549
559
|
continue;
|
|
@@ -789,19 +799,17 @@ Note that in CI environments, this setting is enabled by default.`,
|
|
|
789
799
|
}
|
|
790
800
|
}
|
|
791
801
|
}
|
|
792
|
-
async function runUnignoredDependencyBuilds(opts, previousIgnoredBuilds, allowBuild) {
|
|
802
|
+
async function runUnignoredDependencyBuilds(opts, previousIgnoredBuilds, currentLockfile, allowBuild) {
|
|
793
803
|
if (!allowBuild) {
|
|
794
804
|
return previousIgnoredBuilds;
|
|
795
805
|
}
|
|
796
806
|
const pkgsToBuild = [];
|
|
797
807
|
for (const ignoredPkg of previousIgnoredBuilds) {
|
|
798
|
-
|
|
799
|
-
if (!parsed.name || !parsed.version)
|
|
808
|
+
if (currentLockfile.packages?.[ignoredPkg] == null)
|
|
800
809
|
continue;
|
|
801
|
-
|
|
802
|
-
if (allowed === true) {
|
|
810
|
+
if (allowBuild(ignoredPkg) === true) {
|
|
803
811
|
// Package is explicitly allowed - rebuild it
|
|
804
|
-
pkgsToBuild.push(
|
|
812
|
+
pkgsToBuild.push(dp.getPkgIdWithPatchHash(ignoredPkg));
|
|
805
813
|
}
|
|
806
814
|
}
|
|
807
815
|
if (pkgsToBuild.length) {
|
|
@@ -1409,7 +1417,7 @@ function allMutationsAreInstalls(projects) {
|
|
|
1409
1417
|
* Run the pacquet binary if it's configured, otherwise run the JS
|
|
1410
1418
|
* `headlessInstall`. Callers can hand off any code path that materializes
|
|
1411
1419
|
* an already-resolved lockfile (workspace partial install, hoisted
|
|
1412
|
-
* linker,
|
|
1420
|
+
* linker, pnpr server install, frozen install) without restating the
|
|
1413
1421
|
* delegation choice.
|
|
1414
1422
|
*
|
|
1415
1423
|
* Pacquet reads the wanted lockfile from disk and produces its own
|
|
@@ -1423,7 +1431,7 @@ function allMutationsAreInstalls(projects) {
|
|
|
1423
1431
|
async function materializeOrDelegate(opts, runHeadlessInstall) {
|
|
1424
1432
|
if (opts.runPacquet != null) {
|
|
1425
1433
|
// Reached only from the resolve-then-materialize call sites
|
|
1426
|
-
// (workspace-partial, hoisted-linker,
|
|
1434
|
+
// (workspace-partial, hoisted-linker, pnpr server install). Each ran a
|
|
1427
1435
|
// lockfileOnly resolve pass that emitted one
|
|
1428
1436
|
// `pnpm:progress status:resolved` per package, so pacquet's
|
|
1429
1437
|
// duplicate `resolved` events would double the reporter's count.
|
|
@@ -1565,7 +1573,7 @@ export class IgnoredBuildsError extends PnpmError {
|
|
|
1565
1573
|
}
|
|
1566
1574
|
}
|
|
1567
1575
|
function dedupePackageNamesFromIgnoredBuilds(ignoredBuilds) {
|
|
1568
|
-
return Array.from(new Set(Array.from(ignoredBuilds ?? []).map(dp.
|
|
1576
|
+
return Array.from(new Set(Array.from(ignoredBuilds ?? []).map(depPath => dp.getPkgIdWithPatchHash(depPath)))).sort(lexCompare);
|
|
1569
1577
|
}
|
|
1570
1578
|
/**
|
|
1571
1579
|
* Build injectionTargetsByDepPath from the dependenciesGraph for injected workspace packages
|
|
@@ -1588,13 +1596,13 @@ function getProjectsWithTargetDirs(projects, lockfile, dependenciesGraph) {
|
|
|
1588
1596
|
return extendProjectsWithTargetDirs(projects, injectionTargetsByDepPath);
|
|
1589
1597
|
}
|
|
1590
1598
|
/**
|
|
1591
|
-
* Whether the
|
|
1599
|
+
* Whether the pnpr server path can handle this batch of mutations. The pnpr server flow
|
|
1592
1600
|
* supports installing the manifest as-is (`install`), adding new deps
|
|
1593
1601
|
* (`installSome`), and removing deps (`uninstallSome`). It cannot model the
|
|
1594
1602
|
* client-side update-flag behavior (`update`/`updateMatching`/`updateToLatest`)
|
|
1595
1603
|
* yet, so those still go through the normal client-side resolver.
|
|
1596
1604
|
*/
|
|
1597
|
-
function
|
|
1605
|
+
function canUsePnprForMutations(projects) {
|
|
1598
1606
|
if (projects.length === 0)
|
|
1599
1607
|
return false;
|
|
1600
1608
|
return projects.every((p) => {
|
|
@@ -1607,26 +1615,26 @@ function canUseAgentForMutations(projects) {
|
|
|
1607
1615
|
});
|
|
1608
1616
|
}
|
|
1609
1617
|
/**
|
|
1610
|
-
* Pre-process projects for the
|
|
1618
|
+
* Pre-process projects for the pnpr server flow:
|
|
1611
1619
|
* - `install`: send the manifest as-is.
|
|
1612
1620
|
* - `uninstallSome`: drop the named deps from the manifest before sending,
|
|
1613
|
-
* so the
|
|
1621
|
+
* so the pnpr server's resolution naturally produces a lockfile without them.
|
|
1614
1622
|
* - `installSome`: parse selectors and merge them into the manifest. The
|
|
1615
|
-
*
|
|
1623
|
+
* pnpr server then resolves the merged manifest, and we read the resolved
|
|
1616
1624
|
* specifiers (with the right save-prefix applied server-side) back from
|
|
1617
1625
|
* the lockfile importer entries to update the client-side manifest.
|
|
1618
1626
|
*
|
|
1619
1627
|
* Returns null if the projects don't map cleanly to allProjects (caller
|
|
1620
1628
|
* should fall through to the normal flow).
|
|
1621
1629
|
*/
|
|
1622
|
-
async function
|
|
1630
|
+
async function preparePnprProjects(projects, opts) {
|
|
1623
1631
|
const allProjects = opts.allProjects ?? [];
|
|
1624
1632
|
const mutationByRootDir = new Map();
|
|
1625
1633
|
for (const p of projects) {
|
|
1626
1634
|
mutationByRootDir.set(p.rootDir, p);
|
|
1627
1635
|
}
|
|
1628
1636
|
// Include every workspace project, not just the mutated ones — otherwise
|
|
1629
|
-
// the
|
|
1637
|
+
// the pnpr server's resulting lockfile would only contain the targeted importer
|
|
1630
1638
|
// and `headlessInstall` (or a later install) would crash on the missing
|
|
1631
1639
|
// entries for the other workspace projects. Projects without a mutation
|
|
1632
1640
|
// are sent with their current manifest (no-op for resolution).
|
|
@@ -1685,7 +1693,7 @@ async function prepareAgentProjects(projects, opts) {
|
|
|
1685
1693
|
* dependency field per the mutation's `targetDependenciesField` (or the
|
|
1686
1694
|
* existing field if the dep is already in the manifest, defaulting to
|
|
1687
1695
|
* `dependencies`). Selectors without a version use `'latest'` so the
|
|
1688
|
-
*
|
|
1696
|
+
* pnpr server's resolver picks the newest matching release.
|
|
1689
1697
|
*/
|
|
1690
1698
|
function mergeInstallSelectors(manifest, mutation) {
|
|
1691
1699
|
const target = mutation.targetDependenciesField;
|
|
@@ -1729,10 +1737,10 @@ function findExistingSpec(alias, manifest) {
|
|
|
1729
1737
|
manifest.optionalDependencies?.[alias];
|
|
1730
1738
|
}
|
|
1731
1739
|
/**
|
|
1732
|
-
* After the
|
|
1740
|
+
* After the pnpr server resolves, copy the lockfile importer's per-dep specifier
|
|
1733
1741
|
* (which the server's resolver computed with the right save-prefix) back
|
|
1734
1742
|
* into the client manifest for any newly added aliases. We rely on the
|
|
1735
|
-
* lockfile because the
|
|
1743
|
+
* lockfile because the pnpr server applies catalog substitution,
|
|
1736
1744
|
* normalizedBareSpecifier, and save-prefix logic during resolution.
|
|
1737
1745
|
*/
|
|
1738
1746
|
function applyResolvedSpecsFromLockfile(manifest, importerSnapshot, newDeps, pinnedVersion) {
|
|
@@ -1751,7 +1759,7 @@ function applyResolvedSpecsFromLockfile(manifest, importerSnapshot, newDeps, pin
|
|
|
1751
1759
|
const resolvedVersion = importerSnapshot[field]?.[dep.alias];
|
|
1752
1760
|
if (!resolvedVersion || manifest[field]?.[dep.alias] == null)
|
|
1753
1761
|
continue;
|
|
1754
|
-
// The
|
|
1762
|
+
// The pnpr server resolved the tree but, on the plain-install path, it
|
|
1755
1763
|
// writes the user's raw spec (`'latest'`) into the lockfile specifier
|
|
1756
1764
|
// rather than normalizing to a save-prefix range. Compute the
|
|
1757
1765
|
// save-prefix spec client-side from the resolved version.
|
|
@@ -1762,24 +1770,24 @@ function applyResolvedSpecsFromLockfile(manifest, importerSnapshot, newDeps, pin
|
|
|
1762
1770
|
return manifest;
|
|
1763
1771
|
}
|
|
1764
1772
|
/**
|
|
1765
|
-
* Drives the
|
|
1766
|
-
* projects. Returns null if the call can't be served by the
|
|
1773
|
+
* Drives the pnpr server path for a `mutateModules` call across one or more
|
|
1774
|
+
* projects. Returns null if the call can't be served by the pnpr server (e.g. one
|
|
1767
1775
|
* of the projects isn't in `allProjects`).
|
|
1768
1776
|
*/
|
|
1769
|
-
async function
|
|
1770
|
-
const
|
|
1771
|
-
if (!
|
|
1777
|
+
async function mutateModulesViaPnpr(projects, opts) {
|
|
1778
|
+
const pnprProjects = await preparePnprProjects(projects, opts);
|
|
1779
|
+
if (!pnprProjects)
|
|
1772
1780
|
return null;
|
|
1773
|
-
//
|
|
1781
|
+
// installViaPnprServer runs the headless install for the first
|
|
1774
1782
|
// project's root and the workspace path for the rest. Pass the
|
|
1775
1783
|
// pre-processed manifests so resolution sees the post-mutation state.
|
|
1776
|
-
const result = await
|
|
1784
|
+
const result = await installViaPnprServer(pnprProjects[0].manifest, pnprProjects[0].rootDir, opts, pnprProjects.map((p) => ({ rootDir: p.rootDir, manifest: p.manifest })));
|
|
1777
1785
|
// For installSome projects, copy resolved specs from the lockfile importer
|
|
1778
1786
|
// entries back into the client manifest so save-prefix/catalog/etc. take
|
|
1779
1787
|
// effect (the server applies these during its resolution step).
|
|
1780
1788
|
const lockfileDir = opts.lockfileDir ?? projects[0].rootDir;
|
|
1781
1789
|
const mutatedRootDirs = new Set(projects.map((p) => p.rootDir));
|
|
1782
|
-
const updatedProjects =
|
|
1790
|
+
const updatedProjects = pnprProjects
|
|
1783
1791
|
.filter((p) => mutatedRootDirs.has(p.rootDir))
|
|
1784
1792
|
.map((p) => {
|
|
1785
1793
|
if (p.mutation === 'installSome' && p.newDeps.length > 0) {
|
|
@@ -1798,69 +1806,62 @@ async function mutateModulesViaAgent(projects, opts) {
|
|
|
1798
1806
|
};
|
|
1799
1807
|
}
|
|
1800
1808
|
/**
|
|
1801
|
-
* When a
|
|
1802
|
-
*
|
|
1803
|
-
* packages into node_modules.
|
|
1809
|
+
* When a pnpr server is configured, resolve dependencies server-side,
|
|
1810
|
+
* then run a headless install that fetches tarballs from the registries
|
|
1811
|
+
* and links packages into node_modules — like a normal install.
|
|
1804
1812
|
*/
|
|
1805
|
-
async function
|
|
1806
|
-
// The
|
|
1813
|
+
async function installViaPnprServer(manifest, rootDir, opts, allInstallProjects) {
|
|
1814
|
+
// The pnpr server path skips client-side resolution, so resolver-side policies
|
|
1807
1815
|
// can't be enforced locally. `minimumReleaseAge` is forwarded to the
|
|
1808
|
-
//
|
|
1816
|
+
// pnpr server and enforced server-side. `trustPolicy` has no server-side
|
|
1809
1817
|
// counterpart yet, so refuse to run under it instead of silently
|
|
1810
1818
|
// letting through a lockfile the local verifier would reject.
|
|
1811
1819
|
if (opts.trustPolicy === 'no-downgrade') {
|
|
1812
|
-
throw new PnpmError('
|
|
1820
|
+
throw new PnpmError('TRUST_POLICY_INCOMPATIBLE_WITH_PNPR', 'The pnpr server does not yet enforce `trustPolicy: no-downgrade`, so running an install through it under this policy would produce a lockfile that the local verifier rejects.', { hint: 'Unset `trustPolicy` for this install, or disable the pnpr server (unset `--pnpr-server` / `pnprServer` in pnpm-workspace.yaml) so resolution runs locally and the trust check applies.' });
|
|
1813
1821
|
}
|
|
1814
|
-
const {
|
|
1815
|
-
const {
|
|
1816
|
-
|
|
1817
|
-
//
|
|
1818
|
-
//
|
|
1819
|
-
|
|
1820
|
-
const
|
|
1822
|
+
const { resolveViaPnprServer } = await import('@pnpm/pnpr.client');
|
|
1823
|
+
const { createGetAuthHeaderByURI, getAuthHeadersFromCreds } = await import('@pnpm/network.auth-header');
|
|
1824
|
+
// Forward the whole credential map (the registries a graph touches
|
|
1825
|
+
// aren't known up front), so the server attaches the right token per
|
|
1826
|
+
// URL. `authorization` also identifies the caller to pnpr's gate.
|
|
1827
|
+
const configByUri = opts.configByUri ?? {};
|
|
1828
|
+
const forwardedAuthHeaders = getAuthHeadersFromCreds(configByUri);
|
|
1829
|
+
const pnprAuthorization = createGetAuthHeaderByURI(configByUri)(opts.pnprServer);
|
|
1821
1830
|
try {
|
|
1822
1831
|
const lockfileDir = opts.lockfileDir ?? rootDir;
|
|
1823
|
-
// Read existing lockfile if
|
|
1824
|
-
|
|
1832
|
+
// Read the existing lockfile (if any) in its on-disk shape — that's
|
|
1833
|
+
// what the pnpr server protocol carries, so no conversion is needed before
|
|
1834
|
+
// sending it.
|
|
1835
|
+
const existingLockfile = await readWantedLockfileFile(lockfileDir, {
|
|
1825
1836
|
ignoreIncompatible: true,
|
|
1826
1837
|
}).catch(() => null);
|
|
1827
|
-
logger.info({ message: 'Resolving dependencies via
|
|
1828
|
-
//
|
|
1829
|
-
//
|
|
1830
|
-
//
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
lockfile: existingLockfile ?? undefined,
|
|
1855
|
-
}));
|
|
1856
|
-
// Write store index entries so headless install finds them.
|
|
1857
|
-
const { writeRawIndexEntries } = await import('@pnpm/agent.client');
|
|
1858
|
-
writeRawIndexEntries(indexEntries, storeIndex);
|
|
1859
|
-
storeIndex.checkpoint();
|
|
1860
|
-
}
|
|
1861
|
-
finally {
|
|
1862
|
-
storeIndex.close();
|
|
1863
|
-
}
|
|
1838
|
+
logger.info({ message: 'Resolving dependencies via the pnpr server', prefix: rootDir });
|
|
1839
|
+
// Build projects list for workspace support.
|
|
1840
|
+
// Normalize separators to POSIX — on Windows `path.relative` returns
|
|
1841
|
+
// backslashes, which the pnpr server rejects (it treats `\` as an
|
|
1842
|
+
// unsafe/YAML-injection character and normalizes paths as POSIX).
|
|
1843
|
+
const projectsList = allInstallProjects && allInstallProjects.length > 1
|
|
1844
|
+
? allInstallProjects.map(p => ({
|
|
1845
|
+
dir: (path.relative(lockfileDir, p.rootDir) || '.').split(path.sep).join('/'),
|
|
1846
|
+
dependencies: p.manifest.dependencies,
|
|
1847
|
+
devDependencies: p.manifest.devDependencies,
|
|
1848
|
+
optionalDependencies: p.manifest.optionalDependencies,
|
|
1849
|
+
}))
|
|
1850
|
+
: undefined;
|
|
1851
|
+
const { lockfile, stats: pnprStats } = await resolveViaPnprServer({
|
|
1852
|
+
registryUrl: opts.pnprServer,
|
|
1853
|
+
dependencies: projectsList ? undefined : manifest.dependencies,
|
|
1854
|
+
devDependencies: projectsList ? undefined : manifest.devDependencies,
|
|
1855
|
+
optionalDependencies: projectsList ? undefined : manifest.optionalDependencies,
|
|
1856
|
+
projects: projectsList,
|
|
1857
|
+
registry: opts.registries?.default,
|
|
1858
|
+
namedRegistries: opts.namedRegistries,
|
|
1859
|
+
authHeaders: forwardedAuthHeaders,
|
|
1860
|
+
authorization: pnprAuthorization,
|
|
1861
|
+
overrides: opts.overrides,
|
|
1862
|
+
minimumReleaseAge: opts.minimumReleaseAge,
|
|
1863
|
+
lockfile: existingLockfile ?? undefined,
|
|
1864
|
+
});
|
|
1864
1865
|
await writeWantedLockfileAndRecordVerified({
|
|
1865
1866
|
lockfileDir,
|
|
1866
1867
|
lockfile,
|
|
@@ -1870,46 +1871,28 @@ async function installFromPnpmRegistry(manifest, rootDir, opts, allInstallProjec
|
|
|
1870
1871
|
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
|
|
1871
1872
|
});
|
|
1872
1873
|
logger.info({
|
|
1873
|
-
message: `Resolved ${
|
|
1874
|
+
message: `Resolved ${pnprStats.totalPackages} packages`,
|
|
1874
1875
|
prefix: rootDir,
|
|
1875
1876
|
});
|
|
1876
|
-
//
|
|
1877
|
-
//
|
|
1878
|
-
//
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
const filesIndexFile = _storeIndexKey(integrity, fetchOpts.pkg.id);
|
|
1894
|
-
const result = await readPkgFromCafs({ storeDir: opts.storeDir, verifyStoreIntegrity: false }, filesIndexFile, { readManifest: true, expectedPkg: { name: fetchOpts.pkg.name, version: fetchOpts.pkg.version } });
|
|
1895
|
-
return {
|
|
1896
|
-
fetching: () => Promise.resolve({
|
|
1897
|
-
files: result.files,
|
|
1898
|
-
bundledManifest: result.bundledManifest,
|
|
1899
|
-
integrity,
|
|
1900
|
-
}),
|
|
1901
|
-
filesIndexFile,
|
|
1902
|
-
};
|
|
1903
|
-
}
|
|
1904
|
-
return opts.storeController.fetchPackage(fetchOpts);
|
|
1905
|
-
},
|
|
1906
|
-
};
|
|
1877
|
+
// `--lockfile-only`: the pnpr server resolved and we wrote the lockfile, but
|
|
1878
|
+
// pnpm fetches nothing and links nothing in this mode — stop before the
|
|
1879
|
+
// headless install. See https://github.com/pnpm/pnpm/issues/12146.
|
|
1880
|
+
if (opts.lockfileOnly) {
|
|
1881
|
+
return {
|
|
1882
|
+
updatedCatalogs: undefined,
|
|
1883
|
+
updatedManifest: manifest,
|
|
1884
|
+
ignoredBuilds: undefined,
|
|
1885
|
+
stats: { added: 0, removed: 0, linkedToRoot: 0 },
|
|
1886
|
+
lockfile,
|
|
1887
|
+
resolutionPolicyViolations: [],
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
// The pnpr server only resolves; it serves no file content. Fetch every
|
|
1891
|
+
// tarball from the registries with the regular store controller, in
|
|
1892
|
+
// parallel, exactly like a normal install. See
|
|
1893
|
+
// https://github.com/pnpm/pnpm/issues/12230.
|
|
1907
1894
|
const headlessOpts = {
|
|
1908
1895
|
...opts,
|
|
1909
|
-
// Skip re-verifying files just written from the agent — they're
|
|
1910
|
-
// guaranteed correct (server verified, no rehashing needed).
|
|
1911
|
-
verifyStoreIntegrity: false,
|
|
1912
|
-
storeController: wrappedStoreController,
|
|
1913
1896
|
dir: rootDir,
|
|
1914
1897
|
lockfileDir,
|
|
1915
1898
|
engineStrict: opts.engineStrict ?? false,
|
|
@@ -1948,13 +1931,13 @@ async function installFromPnpmRegistry(manifest, rootDir, opts, allInstallProjec
|
|
|
1948
1931
|
updatedManifest: manifest,
|
|
1949
1932
|
ignoredBuilds,
|
|
1950
1933
|
// Pacquet doesn't surface a structured stats return; default to
|
|
1951
|
-
// zeros so the
|
|
1934
|
+
// zeros so the pnpr server's non-optional `stats` slot is filled.
|
|
1952
1935
|
// The reporter still renders accurate counts from pacquet's
|
|
1953
1936
|
// `pnpm:stats` log events.
|
|
1954
1937
|
stats: stats ?? { added: 0, removed: 0, linkedToRoot: 0 },
|
|
1955
1938
|
lockfile,
|
|
1956
|
-
// Server-side resolution (
|
|
1957
|
-
// itself — the
|
|
1939
|
+
// Server-side resolution (pnpr server) enforces `minimumReleaseAge`
|
|
1940
|
+
// itself — the pnpr server picks only mature versions and the lockfile
|
|
1958
1941
|
// can't contain immature entries to auto-collect. `trustPolicy` is
|
|
1959
1942
|
// guarded above (we refuse to enter this path when it's set), so
|
|
1960
1943
|
// there's nothing for the install command to react to here.
|
|
@@ -1966,7 +1949,6 @@ async function installFromPnpmRegistry(manifest, rootDir, opts, allInstallProjec
|
|
|
1966
1949
|
// normal install path does the same; skipping it here would leave
|
|
1967
1950
|
// pending writes on disk and diverge from lifecycle expectations.
|
|
1968
1951
|
await opts.storeController.close();
|
|
1969
|
-
restoreImportConcurrency();
|
|
1970
1952
|
}
|
|
1971
1953
|
}
|
|
1972
1954
|
//# sourceMappingURL=index.js.map
|
package/lib/install/link.js
CHANGED
|
@@ -328,7 +328,7 @@ async function linkAllPkgs(storeController, depNodes, opts) {
|
|
|
328
328
|
depNode.requiresBuild = files.requiresBuild;
|
|
329
329
|
let sideEffectsCacheKey;
|
|
330
330
|
if (opts.sideEffectsCacheRead && files.sideEffectsMaps && !isEmpty(files.sideEffectsMaps)) {
|
|
331
|
-
if (opts.allowBuild?.(depNode.
|
|
331
|
+
if (opts.allowBuild?.(depNode.depPath) === true) {
|
|
332
332
|
sideEffectsCacheKey = calcDepState(opts.depGraph, opts.depsStateCache, depNode.depPath, {
|
|
333
333
|
includeDepGraphHash: !opts.ignoreScripts && depNode.requiresBuild, // true when is built
|
|
334
334
|
patchFileHash: depNode.patch?.hash,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { hashObject } from '@pnpm/crypto.object-hasher';
|
|
2
|
+
import { withResolutionShapeCacheIdentity } from './verifyLockfileResolutions.js';
|
|
2
3
|
import { recordVerification } from './verifyLockfileResolutionsCache.js';
|
|
3
4
|
/**
|
|
4
5
|
* Records the post-resolution lockfile as verified so the next install
|
|
@@ -17,7 +18,7 @@ export function recordLockfileVerified(opts) {
|
|
|
17
18
|
return;
|
|
18
19
|
recordVerification(opts.cacheDir, {
|
|
19
20
|
lockfilePath: opts.lockfilePath,
|
|
20
|
-
verifiers: opts.resolutionVerifiers,
|
|
21
|
+
verifiers: withResolutionShapeCacheIdentity(opts.resolutionVerifiers),
|
|
21
22
|
hashLockfile: () => hashObject(opts.lockfile),
|
|
22
23
|
});
|
|
23
24
|
}
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import type { LockfileObject } from '@pnpm/lockfile.fs';
|
|
2
2
|
import type { ResolutionPolicyViolation, ResolutionVerifier } from '@pnpm/resolving.resolver-base';
|
|
3
|
+
import { type VerifierCacheIdentity } from './verifyLockfileResolutionsCache.js';
|
|
3
4
|
export type { ResolutionPolicyViolation };
|
|
5
|
+
export declare const RESOLUTION_SHAPE_MISMATCH_VIOLATION_CODE = "RESOLUTION_SHAPE_MISMATCH";
|
|
6
|
+
/**
|
|
7
|
+
* Every verifier list that flows into the verification cache must carry
|
|
8
|
+
* the resolution-shape identity, so records written before the shape rule
|
|
9
|
+
* existed cannot stat-fast-path around it. Used by the gate itself and by
|
|
10
|
+
* {@link recordLockfileVerified}, whose freshly-resolved lockfile satisfies
|
|
11
|
+
* the shape invariant by construction (the writer derives every key from
|
|
12
|
+
* the resolution it just produced).
|
|
13
|
+
*/
|
|
14
|
+
export declare function withResolutionShapeCacheIdentity(verifiers: readonly VerifierCacheIdentity[]): VerifierCacheIdentity[];
|
|
4
15
|
export interface VerifyLockfileResolutionsOptions {
|
|
5
16
|
concurrency?: number;
|
|
6
17
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { lockfileVerificationLogger } from '@pnpm/core-loggers';
|
|
2
2
|
import { hashObject } from '@pnpm/crypto.object-hasher';
|
|
3
3
|
import { PnpmError } from '@pnpm/error';
|
|
4
|
-
import { nameVerFromPkgSnapshot } from '@pnpm/lockfile.utils';
|
|
4
|
+
import { isGitHostedTarballUrl, nameVerFromPkgSnapshot } from '@pnpm/lockfile.utils';
|
|
5
5
|
import pLimit from 'p-limit';
|
|
6
6
|
import { recordVerification, tryLockfileVerificationCache, } from './verifyLockfileResolutionsCache.js';
|
|
7
7
|
// Cap the per-entry breakdown so a verifier rejecting hundreds of entries
|
|
@@ -12,6 +12,22 @@ const MAX_VIOLATIONS_TO_PRINT = 20;
|
|
|
12
12
|
// (Math.min(64, Math.max(workers*3, 16))); keep them aligned so the
|
|
13
13
|
// verification pass doesn't push past what the rest of the install respects.
|
|
14
14
|
const DEFAULT_CONCURRENCY = 16;
|
|
15
|
+
export const RESOLUTION_SHAPE_MISMATCH_VIOLATION_CODE = 'RESOLUTION_SHAPE_MISMATCH';
|
|
16
|
+
const RESOLUTION_SHAPE_CACHE_IDENTITY = {
|
|
17
|
+
policy: { resolutionShapeCheck: true },
|
|
18
|
+
canTrustPastCheck: (cached) => cached.resolutionShapeCheck === true,
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Every verifier list that flows into the verification cache must carry
|
|
22
|
+
* the resolution-shape identity, so records written before the shape rule
|
|
23
|
+
* existed cannot stat-fast-path around it. Used by the gate itself and by
|
|
24
|
+
* {@link recordLockfileVerified}, whose freshly-resolved lockfile satisfies
|
|
25
|
+
* the shape invariant by construction (the writer derives every key from
|
|
26
|
+
* the resolution it just produced).
|
|
27
|
+
*/
|
|
28
|
+
export function withResolutionShapeCacheIdentity(verifiers) {
|
|
29
|
+
return [...verifiers, RESOLUTION_SHAPE_CACHE_IDENTITY];
|
|
30
|
+
}
|
|
15
31
|
/**
|
|
16
32
|
* Policy-neutral pass that asks every resolver-supplied
|
|
17
33
|
* {@link ResolutionVerifier} to check every entry in a lockfile loaded
|
|
@@ -40,8 +56,6 @@ const DEFAULT_CONCURRENCY = 16;
|
|
|
40
56
|
* the lookup logic.
|
|
41
57
|
*/
|
|
42
58
|
export async function verifyLockfileResolutions(lockfile, verifiers, options) {
|
|
43
|
-
if (verifiers.length === 0)
|
|
44
|
-
return;
|
|
45
59
|
if (!lockfile.packages)
|
|
46
60
|
return;
|
|
47
61
|
// Caching kicks in only when the caller surfaced both a writable
|
|
@@ -51,6 +65,7 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
|
|
|
51
65
|
const cache = options?.cacheDir && options?.lockfilePath
|
|
52
66
|
? { cacheDir: options.cacheDir, lockfilePath: options.lockfilePath }
|
|
53
67
|
: undefined;
|
|
68
|
+
const cacheVerifiers = withResolutionShapeCacheIdentity(verifiers);
|
|
54
69
|
let cachePrecomputed;
|
|
55
70
|
// hashObject streams and is key-order-stable, unlike JSON.stringify.
|
|
56
71
|
let cachedHash;
|
|
@@ -62,7 +77,7 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
|
|
|
62
77
|
if (cache) {
|
|
63
78
|
const result = tryLockfileVerificationCache(cache.cacheDir, {
|
|
64
79
|
lockfilePath: cache.lockfilePath,
|
|
65
|
-
verifiers,
|
|
80
|
+
verifiers: cacheVerifiers,
|
|
66
81
|
hashLockfile,
|
|
67
82
|
});
|
|
68
83
|
if (result.hit)
|
|
@@ -76,12 +91,17 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
|
|
|
76
91
|
// A degenerate lockfile where every snapshot fails the
|
|
77
92
|
// name/version extraction (so candidates is empty) skips emission
|
|
78
93
|
// entirely — no work, no noise.
|
|
79
|
-
const candidates = collectCandidates(lockfile);
|
|
94
|
+
const { candidates, shapeViolations } = collectCandidates(lockfile);
|
|
95
|
+
if (shapeViolations.length > 0) {
|
|
96
|
+
throw buildVerificationError(shapeViolations);
|
|
97
|
+
}
|
|
98
|
+
if (verifiers.length === 0)
|
|
99
|
+
return;
|
|
80
100
|
if (candidates.size === 0) {
|
|
81
101
|
if (cache) {
|
|
82
102
|
recordVerification(cache.cacheDir, {
|
|
83
103
|
lockfilePath: cache.lockfilePath,
|
|
84
|
-
verifiers,
|
|
104
|
+
verifiers: cacheVerifiers,
|
|
85
105
|
hashLockfile,
|
|
86
106
|
}, cachePrecomputed);
|
|
87
107
|
}
|
|
@@ -108,7 +128,7 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
|
|
|
108
128
|
if (cache) {
|
|
109
129
|
recordVerification(cache.cacheDir, {
|
|
110
130
|
lockfilePath: cache.lockfilePath,
|
|
111
|
-
verifiers,
|
|
131
|
+
verifiers: cacheVerifiers,
|
|
112
132
|
hashLockfile,
|
|
113
133
|
}, cachePrecomputed);
|
|
114
134
|
}
|
|
@@ -174,38 +194,97 @@ export async function collectResolutionPolicyViolations(lockfile, verifiers, opt
|
|
|
174
194
|
return [];
|
|
175
195
|
if (!lockfile.packages)
|
|
176
196
|
return [];
|
|
177
|
-
|
|
197
|
+
// Shape violations are deliberately not collected here: they are hard
|
|
198
|
+
// tampering failures, not policy picks a caller may auto-exclude.
|
|
199
|
+
return iterateLockfileViolations(collectCandidates(lockfile).candidates, verifiers, options?.concurrency);
|
|
200
|
+
}
|
|
201
|
+
function isRegistryShapedResolution(resolution) {
|
|
202
|
+
if (resolution == null)
|
|
203
|
+
return true;
|
|
204
|
+
if (typeof resolution !== 'object')
|
|
205
|
+
return false;
|
|
206
|
+
const { type, gitHosted, tarball, variants } = resolution;
|
|
207
|
+
if (type === 'variations') {
|
|
208
|
+
return Array.isArray(variants) && variants.every((variant) => isRegistryShapedResolution(variant?.resolution));
|
|
209
|
+
}
|
|
210
|
+
// Custom resolver protocols (`type: 'custom:*'`) are a legitimate
|
|
211
|
+
// non-registry source the user opted into. They can only be materialized by
|
|
212
|
+
// a project-configured custom fetcher — an unrecognized custom type throws at
|
|
213
|
+
// fetch time (see @pnpm/fetching.pick-fetcher) — so a forged custom type
|
|
214
|
+
// cannot launder an artifact past this gate into a build.
|
|
215
|
+
if (typeof type === 'string' && type.startsWith('custom:'))
|
|
216
|
+
return true;
|
|
217
|
+
if (type != null)
|
|
218
|
+
return false;
|
|
219
|
+
// Plain tarball / registry resolution. The lockfile is parsed from YAML
|
|
220
|
+
// without schema validation, so the `gitHosted` flag is not trustworthy on
|
|
221
|
+
// its own: a tampered entry could set a non-boolean (dodging a strict
|
|
222
|
+
// `=== true`) or an explicit `false` on a git-host URL (the loader only
|
|
223
|
+
// backfills the flag when absent). Treat any non-boolean flag as git-hosted
|
|
224
|
+
// and gate on the URL so the verdict never depends on the flag alone.
|
|
225
|
+
if (gitHosted != null && (typeof gitHosted !== 'boolean' || gitHosted))
|
|
226
|
+
return false;
|
|
227
|
+
// A registry resolution reconstructs its tarball URL from name+version, so
|
|
228
|
+
// an absent/empty `tarball` is registry-shaped. When a URL is present it
|
|
229
|
+
// must be an http(s) registry artifact: the npm verifier's tarball-URL
|
|
230
|
+
// binding skips non-http(s) schemes (file:, etc.), so a `file:` tarball
|
|
231
|
+
// under a name@semver key would otherwise be trusted with no safety net.
|
|
232
|
+
if (typeof tarball === 'string' && tarball !== '') {
|
|
233
|
+
if (!/^https?:\/\//i.test(tarball))
|
|
234
|
+
return false;
|
|
235
|
+
if (isGitHostedTarballUrl(tarball))
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
return true;
|
|
178
239
|
}
|
|
179
240
|
// depPath can include peer-dependency and patch_hash suffixes (e.g.
|
|
180
241
|
// `react@18.0.0(peer)(patch_hash=…)`); the same (name, version) pair may
|
|
181
242
|
// therefore appear multiple times. Dedupe so we issue at most one
|
|
182
243
|
// verification per package version.
|
|
183
244
|
//
|
|
184
|
-
// Include a serialization of `resolution` in the key
|
|
185
|
-
// share a (name, version) but differ in *what* was
|
|
186
|
-
// pinned via npm, another
|
|
187
|
-
//
|
|
188
|
-
//
|
|
245
|
+
// Include a serialization of `resolution` and `nonSemverVersion` in the key
|
|
246
|
+
// so two entries that share a (name, version) but differ in *what* was
|
|
247
|
+
// resolved (e.g. one pinned via npm, another a URL-keyed tarball whose
|
|
248
|
+
// snapshot copied the same semver `version` from its manifest) don't
|
|
249
|
+
// collapse: `nonSemverVersion` flips whether the npm verifier enforces or
|
|
250
|
+
// skips the tarball/policy checks, so if the wrong shape wins the dedup the
|
|
251
|
+
// surviving entry is verified under the wrong rules and the real one is
|
|
189
252
|
// never checked.
|
|
190
253
|
function collectCandidates(lockfile) {
|
|
191
254
|
const candidates = new Map();
|
|
255
|
+
const shapeViolations = [];
|
|
192
256
|
for (const [depPath, snapshot] of Object.entries(lockfile.packages ?? {})) {
|
|
193
|
-
const { name, version } = nameVerFromPkgSnapshot(depPath, snapshot);
|
|
257
|
+
const { name, version, nonSemverVersion } = nameVerFromPkgSnapshot(depPath, snapshot);
|
|
194
258
|
if (!name || !version)
|
|
195
259
|
continue;
|
|
196
|
-
|
|
260
|
+
// A registry-style depPath (name@semver) must be backed by a
|
|
261
|
+
// registry-shaped resolution: the allowBuilds policy derives a
|
|
262
|
+
// trusted package identity from that key shape, which is only sound
|
|
263
|
+
// while this invariant holds. The check is offline, so it applies
|
|
264
|
+
// even when no policy verifiers are active.
|
|
265
|
+
if (nonSemverVersion == null && !isRegistryShapedResolution(snapshot.resolution)) {
|
|
266
|
+
shapeViolations.push({
|
|
267
|
+
name,
|
|
268
|
+
version,
|
|
269
|
+
resolution: snapshot.resolution,
|
|
270
|
+
code: RESOLUTION_SHAPE_MISMATCH_VIOLATION_CODE,
|
|
271
|
+
reason: 'a registry-style dependency path is backed by a non-registry resolution',
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
const key = `${name}@${version}@${nonSemverVersion ?? ''}@${JSON.stringify(snapshot.resolution)}`;
|
|
197
275
|
candidates.set(key, {
|
|
198
276
|
name,
|
|
199
277
|
version,
|
|
278
|
+
nonSemverVersion,
|
|
200
279
|
resolution: snapshot.resolution,
|
|
201
280
|
});
|
|
202
281
|
}
|
|
203
|
-
return candidates;
|
|
282
|
+
return { candidates, shapeViolations };
|
|
204
283
|
}
|
|
205
284
|
async function iterateLockfileViolations(candidates, verifiers, concurrency) {
|
|
206
285
|
const violations = [];
|
|
207
286
|
const limit = pLimit(concurrency ?? DEFAULT_CONCURRENCY);
|
|
208
|
-
await Promise.all(Array.from(candidates.values(), ({ name, version, resolution }) => limit(async () => {
|
|
287
|
+
await Promise.all(Array.from(candidates.values(), ({ name, version, nonSemverVersion, resolution }) => limit(async () => {
|
|
209
288
|
// Fan out across every active verifier; each handles its own
|
|
210
289
|
// protocol short-circuit (e.g. the npm verifier returns ok:true for
|
|
211
290
|
// git resolutions). We stop at the first failure per entry so a
|
|
@@ -213,7 +292,7 @@ async function iterateLockfileViolations(candidates, verifiers, concurrency) {
|
|
|
213
292
|
// same (name, version).
|
|
214
293
|
for (const verifier of verifiers) {
|
|
215
294
|
// eslint-disable-next-line no-await-in-loop
|
|
216
|
-
const result = await verifier.verify(resolution, { name, version });
|
|
295
|
+
const result = await verifier.verify(resolution, { name, version, nonSemverVersion });
|
|
217
296
|
if (!result.ok) {
|
|
218
297
|
violations.push({ name, version, resolution, code: result.code, reason: result.reason });
|
|
219
298
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/installing.deps-installer",
|
|
3
|
-
"version": "1101.
|
|
3
|
+
"version": "1101.8.0",
|
|
4
4
|
"description": "Fast, disk space efficient installation engine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -28,7 +28,10 @@
|
|
|
28
28
|
],
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"funding": "https://opencollective.com/pnpm",
|
|
31
|
-
"repository":
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/pnpm/pnpm/tree/main/installing/deps-installer"
|
|
34
|
+
},
|
|
32
35
|
"homepage": "https://github.com/pnpm/pnpm/tree/main/installing/deps-installer#readme",
|
|
33
36
|
"bugs": {
|
|
34
37
|
"url": "https://github.com/pnpm/pnpm/issues"
|
|
@@ -62,60 +65,61 @@
|
|
|
62
65
|
"ramda": "npm:@pnpm/ramda@0.28.1",
|
|
63
66
|
"run-groups": "^5.0.0",
|
|
64
67
|
"semver": "^7.8.1",
|
|
65
|
-
"@pnpm/
|
|
66
|
-
"@pnpm/
|
|
67
|
-
"@pnpm/building.after-install": "1101.0.18",
|
|
68
|
-
"@pnpm/bins.linker": "1100.0.10",
|
|
69
|
-
"@pnpm/building.during-install": "1101.0.15",
|
|
68
|
+
"@pnpm/bins.linker": "1100.0.12",
|
|
69
|
+
"@pnpm/building.after-install": "1101.0.20",
|
|
70
70
|
"@pnpm/catalogs.protocol-parser": "1100.0.0",
|
|
71
|
-
"@pnpm/building.
|
|
71
|
+
"@pnpm/building.during-install": "1101.0.17",
|
|
72
|
+
"@pnpm/building.policy": "1100.0.9",
|
|
73
|
+
"@pnpm/catalogs.types": "1100.0.0",
|
|
72
74
|
"@pnpm/catalogs.resolver": "1100.0.0",
|
|
75
|
+
"@pnpm/config.normalize-registries": "1100.0.7",
|
|
73
76
|
"@pnpm/config.matcher": "1100.0.1",
|
|
74
|
-
"@pnpm/
|
|
75
|
-
"@pnpm/
|
|
76
|
-
"@pnpm/config.parse-overrides": "1100.0.1",
|
|
77
|
-
"@pnpm/deps.graph-hasher": "1100.2.2",
|
|
77
|
+
"@pnpm/core-loggers": "1100.1.4",
|
|
78
|
+
"@pnpm/constants": "1100.0.0",
|
|
78
79
|
"@pnpm/crypto.hash": "1100.0.1",
|
|
80
|
+
"@pnpm/deps.graph-hasher": "1100.2.4",
|
|
79
81
|
"@pnpm/crypto.object-hasher": "1100.0.0",
|
|
80
82
|
"@pnpm/deps.graph-sequencer": "1100.0.0",
|
|
81
|
-
"@pnpm/
|
|
82
|
-
"@pnpm/exec.lifecycle": "1100.0.14",
|
|
83
|
-
"@pnpm/hooks.read-package-hook": "1100.0.5",
|
|
84
|
-
"@pnpm/fs.symlink-dependency": "1100.0.6",
|
|
85
|
-
"@pnpm/fs.read-modules-dir": "1100.0.1",
|
|
86
|
-
"@pnpm/installing.deps-resolver": "1100.1.6",
|
|
87
|
-
"@pnpm/installing.deps-restorer": "1101.1.8",
|
|
83
|
+
"@pnpm/config.parse-overrides": "1100.0.1",
|
|
88
84
|
"@pnpm/error": "1100.0.0",
|
|
89
|
-
"@pnpm/
|
|
90
|
-
"@pnpm/
|
|
91
|
-
"@pnpm/
|
|
92
|
-
"@pnpm/
|
|
93
|
-
"@pnpm/installing.
|
|
94
|
-
"@pnpm/
|
|
95
|
-
"@pnpm/
|
|
96
|
-
"@pnpm/installing.
|
|
97
|
-
"@pnpm/installing.linking.
|
|
98
|
-
"@pnpm/
|
|
99
|
-
"@pnpm/
|
|
100
|
-
"@pnpm/
|
|
101
|
-
"@pnpm/
|
|
102
|
-
"@pnpm/
|
|
103
|
-
"@pnpm/lockfile.
|
|
104
|
-
"@pnpm/
|
|
105
|
-
"@pnpm/lockfile.
|
|
106
|
-
"@pnpm/
|
|
107
|
-
"@pnpm/
|
|
85
|
+
"@pnpm/fs.read-modules-dir": "1100.0.1",
|
|
86
|
+
"@pnpm/exec.lifecycle": "1100.0.16",
|
|
87
|
+
"@pnpm/fs.symlink-dependency": "1100.0.8",
|
|
88
|
+
"@pnpm/hooks.read-package-hook": "1100.0.7",
|
|
89
|
+
"@pnpm/installing.deps-restorer": "1101.1.10",
|
|
90
|
+
"@pnpm/hooks.types": "1100.0.11",
|
|
91
|
+
"@pnpm/installing.context": "1100.0.16",
|
|
92
|
+
"@pnpm/installing.deps-resolver": "1100.2.1",
|
|
93
|
+
"@pnpm/installing.linking.direct-dep-linker": "1100.0.8",
|
|
94
|
+
"@pnpm/deps.path": "1100.0.7",
|
|
95
|
+
"@pnpm/installing.linking.modules-cleaner": "1100.1.6",
|
|
96
|
+
"@pnpm/installing.linking.hoist": "1100.0.12",
|
|
97
|
+
"@pnpm/bins.remover": "1100.0.8",
|
|
98
|
+
"@pnpm/installing.modules-yaml": "1100.0.8",
|
|
99
|
+
"@pnpm/lockfile.fs": "1100.1.4",
|
|
100
|
+
"@pnpm/installing.package-requester": "1101.0.12",
|
|
101
|
+
"@pnpm/lockfile.pruner": "1100.0.10",
|
|
102
|
+
"@pnpm/lockfile.settings-checker": "1100.0.16",
|
|
103
|
+
"@pnpm/lockfile.preferred-versions": "1100.0.14",
|
|
104
|
+
"@pnpm/lockfile.filtering": "1100.1.5",
|
|
105
|
+
"@pnpm/lockfile.to-pnp": "1100.0.13",
|
|
106
|
+
"@pnpm/lockfile.verification": "1100.0.16",
|
|
107
|
+
"@pnpm/lockfile.utils": "1100.0.12",
|
|
108
|
+
"@pnpm/lockfile.walker": "1100.0.10",
|
|
109
|
+
"@pnpm/network.auth-header": "1101.1.1",
|
|
108
110
|
"@pnpm/resolving.parse-wanted-dependency": "1100.0.1",
|
|
109
|
-
"@pnpm/pkg-manifest.utils": "1100.2.
|
|
110
|
-
"@pnpm/
|
|
111
|
+
"@pnpm/pkg-manifest.utils": "1100.2.3",
|
|
112
|
+
"@pnpm/patching.config": "1100.0.7",
|
|
113
|
+
"@pnpm/pnpr.client": "1.2.0",
|
|
114
|
+
"@pnpm/resolving.resolver-base": "1100.4.1",
|
|
115
|
+
"@pnpm/store.controller-types": "1100.1.4",
|
|
116
|
+
"@pnpm/workspace.project-manifest-reader": "1100.0.11",
|
|
111
117
|
"@pnpm/store.index": "1100.1.0",
|
|
112
|
-
"@pnpm/types": "1101.
|
|
113
|
-
"@pnpm/workspace.project-manifest-reader": "1100.0.9",
|
|
114
|
-
"@pnpm/store.controller-types": "1100.1.2"
|
|
118
|
+
"@pnpm/types": "1101.3.1"
|
|
115
119
|
},
|
|
116
120
|
"peerDependencies": {
|
|
117
121
|
"@pnpm/logger": "^1001.0.1",
|
|
118
|
-
"@pnpm/worker": "^1100.1.
|
|
122
|
+
"@pnpm/worker": "^1100.1.10"
|
|
119
123
|
},
|
|
120
124
|
"devDependencies": {
|
|
121
125
|
"@jest/globals": "30.3.0",
|
|
@@ -137,22 +141,22 @@
|
|
|
137
141
|
"symlink-dir": "^10.0.1",
|
|
138
142
|
"write-json-file": "^7.0.0",
|
|
139
143
|
"write-yaml-file": "^6.0.0",
|
|
140
|
-
"@pnpm/assert-project": "1100.0.
|
|
141
|
-
"@pnpm/
|
|
142
|
-
"@pnpm/
|
|
143
|
-
"@pnpm/
|
|
144
|
+
"@pnpm/assert-project": "1100.0.14",
|
|
145
|
+
"@pnpm/lockfile.types": "1100.0.10",
|
|
146
|
+
"@pnpm/installing.deps-installer": "1101.8.0",
|
|
147
|
+
"@pnpm/assert-store": "1100.0.14",
|
|
144
148
|
"@pnpm/network.git-utils": "1100.0.1",
|
|
145
|
-
"@pnpm/pkg-manifest.reader": "1100.0.5",
|
|
146
149
|
"@pnpm/logger": "1100.0.0",
|
|
147
|
-
"@pnpm/
|
|
148
|
-
"@pnpm/
|
|
149
|
-
"@pnpm/
|
|
150
|
+
"@pnpm/pkg-manifest.reader": "1100.0.7",
|
|
151
|
+
"@pnpm/resolving.registry.types": "1100.1.2",
|
|
152
|
+
"@pnpm/prepare": "1100.0.14",
|
|
150
153
|
"@pnpm/test-fixtures": "1100.0.0",
|
|
151
|
-
"@pnpm/store.cafs": "1100.1.
|
|
152
|
-
"@pnpm/testing.mock-agent": "
|
|
153
|
-
"@pnpm/testing.temp-store": "1100.1.5",
|
|
154
|
+
"@pnpm/store.cafs": "1100.1.9",
|
|
155
|
+
"@pnpm/testing.mock-agent": "1101.0.1",
|
|
154
156
|
"@pnpm/test-ipc-server": "1100.0.0",
|
|
155
|
-
"@pnpm/
|
|
157
|
+
"@pnpm/store.path": "1100.0.1",
|
|
158
|
+
"@pnpm/testing.temp-store": "1100.1.7",
|
|
159
|
+
"@pnpm/testing.registry-mock": "1100.0.4"
|
|
156
160
|
},
|
|
157
161
|
"engines": {
|
|
158
162
|
"node": ">=22.13"
|