@pnpm/resolving.npm-resolver 1101.0.3 → 1101.1.1
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/fetch.js +53 -6
- package/lib/index.d.ts +16 -3
- package/lib/index.js +131 -44
- package/lib/parseBareSpecifier.d.ts +5 -0
- package/lib/parseBareSpecifier.js +88 -6
- package/lib/pickPackage.js +116 -9
- package/package.json +21 -21
package/lib/fetch.js
CHANGED
|
@@ -3,24 +3,71 @@ import { requestRetryLogger } from '@pnpm/core-loggers';
|
|
|
3
3
|
import { FetchError, PnpmError, } from '@pnpm/error';
|
|
4
4
|
import { globalWarn } from '@pnpm/logger';
|
|
5
5
|
import * as retry from '@zkochan/retry';
|
|
6
|
-
|
|
7
|
-
// eslint-disable-next-line regexp/no-super-linear-backtracking, regexp/use-ignore-case
|
|
8
|
-
const semverRegex = /(.*)(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
|
6
|
+
import semver from 'semver';
|
|
9
7
|
export class RegistryResponseError extends FetchError {
|
|
10
8
|
pkgName;
|
|
11
9
|
constructor(request, response, pkgName) {
|
|
12
10
|
let hint;
|
|
13
11
|
if (response.status === 404) {
|
|
14
12
|
hint = `${pkgName} is not in the npm registry, or you have no permission to fetch it.`;
|
|
15
|
-
const
|
|
16
|
-
if (
|
|
17
|
-
hint += ` Did you mean ${
|
|
13
|
+
const nameWithoutVersion = stripTrailingSemverSuffix(pkgName);
|
|
14
|
+
if (nameWithoutVersion != null) {
|
|
15
|
+
hint += ` Did you mean ${nameWithoutVersion}?`;
|
|
18
16
|
}
|
|
19
17
|
}
|
|
20
18
|
super(request, response, hint);
|
|
21
19
|
this.pkgName = pkgName;
|
|
22
20
|
}
|
|
23
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Detect when a package name accidentally includes a `<version>` suffix
|
|
24
|
+
* (e.g. `lodash@4.17.21` or `lodash4.17.21`) and return the part before the
|
|
25
|
+
* version. Returns `undefined` when no semver suffix is present.
|
|
26
|
+
*
|
|
27
|
+
* Implemented as an O(n) scan to avoid polynomial backtracking on adversarial
|
|
28
|
+
* input (CodeQL: js/polynomial-redos).
|
|
29
|
+
*/
|
|
30
|
+
function stripTrailingSemverSuffix(pkgName) {
|
|
31
|
+
// Common case: "name@version" – split on the rightmost '@'.
|
|
32
|
+
// `atIdx > 0` rules out the leading '@' of scoped names like '@scope/foo'.
|
|
33
|
+
const atIdx = pkgName.lastIndexOf('@');
|
|
34
|
+
if (atIdx > 0 && semver.valid(pkgName.slice(atIdx + 1)) != null) {
|
|
35
|
+
return pkgName.slice(0, atIdx);
|
|
36
|
+
}
|
|
37
|
+
// Fallback: detect a trailing "<digits>.<digits>.<digits>" appended to a name
|
|
38
|
+
// with no separator (e.g. "foo1.0.0"). We walk backwards through three
|
|
39
|
+
// digit-blocks separated by dots; this is O(n) and free of regex backtracking.
|
|
40
|
+
let i = pkgName.length;
|
|
41
|
+
i = consumeTrailingDigits(pkgName, i);
|
|
42
|
+
if (i === pkgName.length || i === 0 || pkgName.charCodeAt(i - 1) !== 46 /* '.' */)
|
|
43
|
+
return undefined;
|
|
44
|
+
i--;
|
|
45
|
+
const beforePatch = i;
|
|
46
|
+
i = consumeTrailingDigits(pkgName, i);
|
|
47
|
+
if (i === beforePatch || i === 0 || pkgName.charCodeAt(i - 1) !== 46)
|
|
48
|
+
return undefined;
|
|
49
|
+
i--;
|
|
50
|
+
const beforeMinor = i;
|
|
51
|
+
i = consumeTrailingDigits(pkgName, i);
|
|
52
|
+
if (i === beforeMinor || i === 0)
|
|
53
|
+
return undefined;
|
|
54
|
+
if (semver.valid(pkgName.slice(i)) == null)
|
|
55
|
+
return undefined;
|
|
56
|
+
let prefix = pkgName.slice(0, i);
|
|
57
|
+
if (prefix.endsWith('@'))
|
|
58
|
+
prefix = prefix.slice(0, -1);
|
|
59
|
+
return prefix.length > 0 ? prefix : undefined;
|
|
60
|
+
}
|
|
61
|
+
function consumeTrailingDigits(s, end) {
|
|
62
|
+
let i = end;
|
|
63
|
+
while (i > 0) {
|
|
64
|
+
const c = s.charCodeAt(i - 1);
|
|
65
|
+
if (c < 48 || c > 57)
|
|
66
|
+
break;
|
|
67
|
+
i--;
|
|
68
|
+
}
|
|
69
|
+
return i;
|
|
70
|
+
}
|
|
24
71
|
export async function fetchMetadataFromFromRegistry(fetchOpts, pkgName, { authHeaderValue, etag: cachedEtag, fullMetadata, modified: cachedModified, registry, }) {
|
|
25
72
|
const uri = toUri(pkgName, registry);
|
|
26
73
|
const op = retry.operation(fetchOpts.retry);
|
package/lib/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { PackageMeta } from '@pnpm/resolving.registry.types';
|
|
|
4
4
|
import type { DirectoryResolution, PkgResolutionId, PreferredVersions, ResolveResult, TarballResolution, WantedDependency, WorkspacePackages } from '@pnpm/resolving.resolver-base';
|
|
5
5
|
import type { DependencyManifest, PackageVersionPolicy, PinnedVersion, Registries, TrustPolicy } from '@pnpm/types';
|
|
6
6
|
import { fetchMetadataFromFromRegistry, type FetchMetadataFromFromRegistryOptions, RegistryResponseError } from './fetch.js';
|
|
7
|
-
import { parseBareSpecifier, type RegistryPackageSpec } from './parseBareSpecifier.js';
|
|
7
|
+
import { BUILTIN_NAMED_REGISTRIES, parseBareSpecifier, type RegistryPackageSpec } from './parseBareSpecifier.js';
|
|
8
8
|
import { type PackageMetaCache, pickPackage, type PickPackageOptions } from './pickPackage.js';
|
|
9
9
|
import { pickPackageFromMeta, pickVersionByVersionRange } from './pickPackageFromMeta.js';
|
|
10
10
|
import { workspacePrefToNpm } from './workspacePrefToNpm.js';
|
|
@@ -20,7 +20,8 @@ export declare class NoMatchingVersionError extends PnpmError {
|
|
|
20
20
|
readonly immatureVersion?: string;
|
|
21
21
|
constructor(opts: NoMatchingVersionErrorOptions);
|
|
22
22
|
}
|
|
23
|
-
export
|
|
23
|
+
export declare function formatTimeAgo(date: Date): string | null;
|
|
24
|
+
export { BUILTIN_NAMED_REGISTRIES, fetchMetadataFromFromRegistry, type FetchMetadataFromFromRegistryOptions, type PackageMeta, type PackageMetaCache, parseBareSpecifier, pickPackageFromMeta, pickVersionByVersionRange, type RegistryPackageSpec, RegistryResponseError, workspacePrefToNpm, };
|
|
24
25
|
export { whichVersionIsPinned } from './whichVersionIsPinned.js';
|
|
25
26
|
export interface ResolverFactoryOptions {
|
|
26
27
|
cacheDir: string;
|
|
@@ -32,6 +33,7 @@ export interface ResolverFactoryOptions {
|
|
|
32
33
|
retry?: RetryTimeoutOptions;
|
|
33
34
|
timeout?: number;
|
|
34
35
|
registries: Registries;
|
|
36
|
+
namedRegistries?: Record<string, string>;
|
|
35
37
|
saveWorkspaceProtocol?: boolean | 'rolling';
|
|
36
38
|
preserveAbsolutePaths?: boolean;
|
|
37
39
|
strictPublishedByCheck?: boolean;
|
|
@@ -54,6 +56,14 @@ export interface JsrResolveResult extends ResolveResult {
|
|
|
54
56
|
resolution: TarballResolution;
|
|
55
57
|
resolvedVia: 'jsr-registry';
|
|
56
58
|
}
|
|
59
|
+
export interface NamedRegistryResolveResult extends ResolveResult {
|
|
60
|
+
alias: string;
|
|
61
|
+
/** The named-registry alias that was matched, e.g. `gh` or a user-defined name. */
|
|
62
|
+
registryName: string;
|
|
63
|
+
manifest: DependencyManifest;
|
|
64
|
+
resolution: TarballResolution;
|
|
65
|
+
resolvedVia: 'named-registry';
|
|
66
|
+
}
|
|
57
67
|
export interface WorkspaceResolveResult extends ResolveResult {
|
|
58
68
|
manifest: DependencyManifest;
|
|
59
69
|
resolution: DirectoryResolution;
|
|
@@ -61,16 +71,19 @@ export interface WorkspaceResolveResult extends ResolveResult {
|
|
|
61
71
|
}
|
|
62
72
|
export type NpmResolver = (wantedDependency: WantedDependency & {
|
|
63
73
|
optional?: boolean;
|
|
64
|
-
}, opts: ResolveFromNpmOptions) => Promise<NpmResolveResult | JsrResolveResult | WorkspaceResolveResult | null>;
|
|
74
|
+
}, opts: ResolveFromNpmOptions) => Promise<NpmResolveResult | JsrResolveResult | NamedRegistryResolveResult | WorkspaceResolveResult | null>;
|
|
65
75
|
export declare function createNpmResolver(fetchFromRegistry: FetchFromRegistry, getAuthHeader: GetAuthHeader, opts: ResolverFactoryOptions): {
|
|
66
76
|
resolveFromNpm: NpmResolver;
|
|
67
77
|
resolveFromJsr: NpmResolver;
|
|
78
|
+
resolveFromNamedRegistry: NpmResolver;
|
|
68
79
|
clearCache: () => void;
|
|
69
80
|
};
|
|
70
81
|
export interface ResolveFromNpmContext {
|
|
71
82
|
pickPackage: (spec: RegistryPackageSpec, opts: PickPackageOptions) => ReturnType<typeof pickPackage>;
|
|
72
83
|
getAuthHeaderValueByURI: (registry: string) => string | undefined;
|
|
73
84
|
registries: Registries;
|
|
85
|
+
namedRegistries: Record<string, string>;
|
|
86
|
+
namedRegistryNames: ReadonlySet<string>;
|
|
74
87
|
saveWorkspaceProtocol?: boolean | 'rolling';
|
|
75
88
|
peekManifestFromStore?: (opts: {
|
|
76
89
|
id: PkgResolutionId;
|
package/lib/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import ssri from 'ssri';
|
|
|
13
13
|
import versionSelectorType from 'version-selector-type';
|
|
14
14
|
import { fetchMetadataFromFromRegistry, RegistryResponseError } from './fetch.js';
|
|
15
15
|
import { normalizeRegistryUrl } from './normalizeRegistryUrl.js';
|
|
16
|
-
import { parseBareSpecifier, parseJsrSpecifierToRegistryPackageSpec, } from './parseBareSpecifier.js';
|
|
16
|
+
import { BUILTIN_NAMED_REGISTRIES, parseBareSpecifier, parseJsrSpecifierToRegistryPackageSpec, parseNamedRegistrySpecifierToRegistryPackageSpec, } from './parseBareSpecifier.js';
|
|
17
17
|
import { pickPackage, } from './pickPackage.js';
|
|
18
18
|
import { pickPackageFromMeta, pickVersionByVersionRange } from './pickPackageFromMeta.js';
|
|
19
19
|
import { failIfTrustDowngraded } from './trustChecks.js';
|
|
@@ -29,7 +29,7 @@ export class NoMatchingVersionError extends PnpmError {
|
|
|
29
29
|
let errorMessage;
|
|
30
30
|
if (opts.publishedBy && opts.immatureVersion && opts.packageMeta.time) {
|
|
31
31
|
const time = new Date(opts.packageMeta.time[opts.immatureVersion]);
|
|
32
|
-
const releaseAgeText = formatTimeAgo(time);
|
|
32
|
+
const releaseAgeText = formatTimeAgo(time) ?? 'just now';
|
|
33
33
|
const pkgName = opts.wantedDependency.alias ?? opts.packageMeta.name;
|
|
34
34
|
errorMessage = `Version ${opts.immatureVersion} (released ${releaseAgeText}) of ${pkgName} does not meet the minimumReleaseAge constraint`;
|
|
35
35
|
}
|
|
@@ -41,25 +41,36 @@ export class NoMatchingVersionError extends PnpmError {
|
|
|
41
41
|
this.immatureVersion = opts.immatureVersion;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
function formatTimeAgo(date) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (diffMs < 60 * 1000) {
|
|
49
|
-
return 'just now';
|
|
50
|
-
}
|
|
51
|
-
const diffMinutes = Math.floor(diffMs / (60 * 1000));
|
|
52
|
-
const diffHours = Math.floor(diffMs / (60 * 60 * 1000));
|
|
53
|
-
const diffDays = Math.floor(diffMs / (24 * 60 * 60 * 1000));
|
|
54
|
-
if (diffHours >= 48) {
|
|
55
|
-
return `${diffDays} day${diffDays === 1 ? '' : 's'} ago`;
|
|
44
|
+
export function formatTimeAgo(date) {
|
|
45
|
+
const ts = date.getTime();
|
|
46
|
+
if (isNaN(ts)) {
|
|
47
|
+
return null;
|
|
56
48
|
}
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
const diffMs = now - ts;
|
|
51
|
+
// Handle clock skew (future dates)
|
|
52
|
+
if (diffMs < 0) {
|
|
53
|
+
return null;
|
|
59
54
|
}
|
|
60
|
-
|
|
55
|
+
const diffSec = Math.floor(diffMs / 1000);
|
|
56
|
+
const diffMin = Math.floor(diffSec / 60);
|
|
57
|
+
const diffHour = Math.floor(diffMin / 60);
|
|
58
|
+
const diffDay = Math.floor(diffHour / 24);
|
|
59
|
+
const diffMonth = Math.floor(diffDay / 30);
|
|
60
|
+
const diffYear = Math.floor(diffDay / 365);
|
|
61
|
+
if (diffYear > 0)
|
|
62
|
+
return `${diffYear} year${diffYear === 1 ? '' : 's'} ago`;
|
|
63
|
+
if (diffMonth > 0)
|
|
64
|
+
return `${diffMonth} month${diffMonth === 1 ? '' : 's'} ago`;
|
|
65
|
+
if (diffDay > 0)
|
|
66
|
+
return `${diffDay} day${diffDay === 1 ? '' : 's'} ago`;
|
|
67
|
+
if (diffHour > 0)
|
|
68
|
+
return `${diffHour} hour${diffHour === 1 ? '' : 's'} ago`;
|
|
69
|
+
if (diffMin > 0)
|
|
70
|
+
return `${diffMin} minute${diffMin === 1 ? '' : 's'} ago`;
|
|
71
|
+
return 'a few seconds ago';
|
|
61
72
|
}
|
|
62
|
-
export { fetchMetadataFromFromRegistry, parseBareSpecifier, pickPackageFromMeta, pickVersionByVersionRange, RegistryResponseError, workspacePrefToNpm, };
|
|
73
|
+
export { BUILTIN_NAMED_REGISTRIES, fetchMetadataFromFromRegistry, parseBareSpecifier, pickPackageFromMeta, pickVersionByVersionRange, RegistryResponseError, workspacePrefToNpm, };
|
|
63
74
|
export { whichVersionIsPinned } from './whichVersionIsPinned.js';
|
|
64
75
|
export function createNpmResolver(fetchFromRegistry, getAuthHeader, opts) {
|
|
65
76
|
if (typeof opts.cacheDir !== 'string') {
|
|
@@ -103,6 +114,8 @@ export function createNpmResolver(fetchFromRegistry, getAuthHeader, opts) {
|
|
|
103
114
|
return request;
|
|
104
115
|
};
|
|
105
116
|
}
|
|
117
|
+
const namedRegistries = mergeNamedRegistries(opts.namedRegistries);
|
|
118
|
+
const namedRegistryNames = new Set(Object.keys(namedRegistries));
|
|
106
119
|
const ctx = {
|
|
107
120
|
getAuthHeaderValueByURI: getAuthHeader,
|
|
108
121
|
pickPackage: pickPackage.bind(null, {
|
|
@@ -117,12 +130,15 @@ export function createNpmResolver(fetchFromRegistry, getAuthHeader, opts) {
|
|
|
117
130
|
ignoreMissingTimeField: opts.ignoreMissingTimeField,
|
|
118
131
|
}),
|
|
119
132
|
registries: opts.registries,
|
|
133
|
+
namedRegistries,
|
|
134
|
+
namedRegistryNames,
|
|
120
135
|
saveWorkspaceProtocol: opts.saveWorkspaceProtocol,
|
|
121
136
|
peekManifestFromStore,
|
|
122
137
|
};
|
|
123
138
|
return {
|
|
124
139
|
resolveFromNpm: resolveNpm.bind(null, ctx),
|
|
125
140
|
resolveFromJsr: resolveJsr.bind(null, ctx),
|
|
141
|
+
resolveFromNamedRegistry: resolveFromNamedRegistry.bind(null, ctx),
|
|
126
142
|
clearCache: () => {
|
|
127
143
|
if ('clear' in metaCache && typeof metaCache.clear === 'function') {
|
|
128
144
|
metaCache.clear();
|
|
@@ -164,7 +180,13 @@ async function resolveNpm(ctx, wantedDependency, opts) {
|
|
|
164
180
|
// Fast path: if we have a current resolution with integrity, try to peek the manifest from the store.
|
|
165
181
|
// This avoids the expensive metadata fetch from the registry.
|
|
166
182
|
// We do this AFTER ensuring the spec is valid for this resolver to avoids hijacking other resolvers.
|
|
167
|
-
|
|
183
|
+
// If publishedBy is set (resolutionMode=time-based or minimumReleaseAge is configured), we only take
|
|
184
|
+
// the fast path when publishedAt is already known from the lockfile's `time:` block; otherwise we
|
|
185
|
+
// fall through to a registry fetch so the cutoff isn't computed from missing data.
|
|
186
|
+
if (ctx.peekManifestFromStore &&
|
|
187
|
+
opts.currentPkg?.resolution &&
|
|
188
|
+
!opts.update &&
|
|
189
|
+
(opts.publishedBy == null || opts.currentPkg.publishedAt != null)) {
|
|
168
190
|
const currentResolution = opts.currentPkg.resolution;
|
|
169
191
|
// Only use this optimization for tarball resolutions with integrity (npm packages)
|
|
170
192
|
if ('tarball' in currentResolution && currentResolution.integrity) {
|
|
@@ -184,7 +206,7 @@ async function resolveNpm(ctx, wantedDependency, opts) {
|
|
|
184
206
|
manifest,
|
|
185
207
|
resolution: currentResolution,
|
|
186
208
|
resolvedVia: 'npm-registry',
|
|
187
|
-
publishedAt:
|
|
209
|
+
publishedAt: opts.currentPkg.publishedAt,
|
|
188
210
|
};
|
|
189
211
|
}
|
|
190
212
|
}
|
|
@@ -326,52 +348,117 @@ async function resolveNpm(ctx, wantedDependency, opts) {
|
|
|
326
348
|
async function resolveJsr(ctx, wantedDependency, opts) {
|
|
327
349
|
if (!wantedDependency.bareSpecifier)
|
|
328
350
|
return null;
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
351
|
+
const spec = parseJsrSpecifierToRegistryPackageSpec(wantedDependency.bareSpecifier, wantedDependency.alias, opts.defaultTag ?? 'latest');
|
|
352
|
+
if (spec == null)
|
|
353
|
+
return null;
|
|
354
|
+
const picked = await pickFromSimpleRegistry(ctx, wantedDependency, opts, spec, ctx.registries['@jsr']); // '@jsr' is always defined
|
|
355
|
+
return {
|
|
356
|
+
...picked,
|
|
357
|
+
normalizedBareSpecifier: opts.calcSpecifier
|
|
358
|
+
? calcPrefixedSpecifier('jsr:', spec.jsrPkgName, wantedDependency, picked.manifest.version, opts.pinnedVersion)
|
|
359
|
+
: undefined,
|
|
360
|
+
resolvedVia: 'jsr-registry',
|
|
361
|
+
alias: spec.jsrPkgName,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
// Merges user-supplied named-registry aliases (from config) on top of pnpm's
|
|
365
|
+
// built-in defaults (e.g. `gh` → GitHub Packages). User entries take precedence
|
|
366
|
+
// so GHES users can point `gh` at their enterprise host. URLs are validated
|
|
367
|
+
// here so typos like `npm.work.example.com` (no scheme) surface at startup
|
|
368
|
+
// rather than as a confusing 404 during resolution. The named-registry
|
|
369
|
+
// resolver runs last in the resolution chain, so an alias that collides with
|
|
370
|
+
// another specifier scheme (e.g. `git`, `github`, `jsr`) is silently shadowed
|
|
371
|
+
// by that scheme's dedicated resolver — no cross-resolver knowledge needed.
|
|
372
|
+
function mergeNamedRegistries(userDefined) {
|
|
373
|
+
const merged = { ...BUILTIN_NAMED_REGISTRIES };
|
|
374
|
+
if (!userDefined)
|
|
375
|
+
return merged;
|
|
376
|
+
for (const [alias, url] of Object.entries(userDefined)) {
|
|
377
|
+
if (typeof url !== 'string' || !isValidHttpUrl(url)) {
|
|
378
|
+
throw new PnpmError('INVALID_NAMED_REGISTRY_URL', `The named registry alias '${alias}' is mapped to '${String(url)}', which is not a valid http(s) URL.`, { hint: 'Provide a URL that starts with http:// or https://, e.g. https://npm.pkg.example.com/' });
|
|
379
|
+
}
|
|
380
|
+
merged[alias] = url;
|
|
381
|
+
}
|
|
382
|
+
return merged;
|
|
383
|
+
}
|
|
384
|
+
function isValidHttpUrl(url) {
|
|
385
|
+
try {
|
|
386
|
+
const parsed = new URL(url);
|
|
387
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// Resolves a `<alias>:` specifier from one of the configured named registries.
|
|
394
|
+
// The `gh:` alias ships as a built-in default pointing at the GitHub Packages
|
|
395
|
+
// npm registry; additional aliases come from pnpm-workspace.yaml's
|
|
396
|
+
// `namedRegistries` field. Auth tokens are looked up by the resolved registry
|
|
397
|
+
// URL, so a `//npm.pkg.github.com/:_authToken=...` entry in `.npmrc` is
|
|
398
|
+
// picked up automatically for `gh:` specifiers (and analogously for any user-
|
|
399
|
+
// configured alias).
|
|
400
|
+
async function resolveFromNamedRegistry(ctx, wantedDependency, opts) {
|
|
401
|
+
if (!wantedDependency.bareSpecifier)
|
|
402
|
+
return null;
|
|
403
|
+
const spec = parseNamedRegistrySpecifierToRegistryPackageSpec(wantedDependency.bareSpecifier, ctx.namedRegistryNames, wantedDependency.alias, opts.defaultTag ?? 'latest');
|
|
332
404
|
if (spec == null)
|
|
333
405
|
return null;
|
|
406
|
+
const registry = ctx.namedRegistries[spec.registryName];
|
|
407
|
+
if (!registry)
|
|
408
|
+
return null; // defensive: should never trigger because parse checks the alias set
|
|
409
|
+
const picked = await pickFromSimpleRegistry(ctx, wantedDependency, opts, spec, registry);
|
|
410
|
+
return {
|
|
411
|
+
...picked,
|
|
412
|
+
normalizedBareSpecifier: opts.calcSpecifier
|
|
413
|
+
? calcPrefixedSpecifier(`${spec.registryName}:`, spec.name, wantedDependency, picked.manifest.version, opts.pinnedVersion)
|
|
414
|
+
: undefined,
|
|
415
|
+
resolvedVia: 'named-registry',
|
|
416
|
+
registryName: spec.registryName,
|
|
417
|
+
// Exposes the scoped package name so callers that omit an explicit alias
|
|
418
|
+
// (e.g. `pnpm add gh:@acme/foo`) record the dependency under `@acme/foo`.
|
|
419
|
+
alias: spec.name,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
// Shared inner shell for resolvers that pull from a single registry URL with
|
|
423
|
+
// an already-parsed RegistryPackageSpec (jsr, named-registry). Returns the
|
|
424
|
+
// fields common to their result envelopes; each caller adds its own
|
|
425
|
+
// resolvedVia, alias, and normalizedBareSpecifier.
|
|
426
|
+
async function pickFromSimpleRegistry(ctx, wantedDependency, opts, spec, registry) {
|
|
334
427
|
const authHeaderValue = ctx.getAuthHeaderValueByURI(registry);
|
|
335
428
|
const { meta, pickedPackage } = await ctx.pickPackage(spec, {
|
|
336
429
|
pickLowestVersion: opts.pickLowestVersion,
|
|
337
430
|
publishedBy: opts.publishedBy,
|
|
431
|
+
publishedByExclude: opts.publishedByExclude,
|
|
338
432
|
authHeaderValue,
|
|
339
433
|
dryRun: opts.dryRun === true,
|
|
340
434
|
preferredVersionSelectors: opts.preferredVersions?.[spec.name],
|
|
341
435
|
registry,
|
|
342
436
|
includeLatestTag: opts.update === 'latest',
|
|
437
|
+
optional: wantedDependency.optional,
|
|
343
438
|
});
|
|
344
439
|
if (pickedPackage == null) {
|
|
345
440
|
throw new NoMatchingVersionError({ wantedDependency, packageMeta: meta, registry });
|
|
346
441
|
}
|
|
347
|
-
const id = `${pickedPackage.name}@${pickedPackage.version}`;
|
|
348
|
-
const resolution = {
|
|
349
|
-
integrity: getIntegrity(pickedPackage.dist),
|
|
350
|
-
tarball: normalizeRegistryUrl(pickedPackage.dist.tarball),
|
|
351
|
-
};
|
|
352
442
|
return {
|
|
353
|
-
id
|
|
443
|
+
id: `${pickedPackage.name}@${pickedPackage.version}`,
|
|
354
444
|
latest: meta['dist-tags'].latest,
|
|
355
445
|
manifest: pickedPackage,
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
version: pickedPackage.version,
|
|
361
|
-
defaultPinnedVersion: opts.pinnedVersion,
|
|
362
|
-
})
|
|
363
|
-
: undefined,
|
|
364
|
-
resolution,
|
|
365
|
-
resolvedVia: 'jsr-registry',
|
|
446
|
+
resolution: {
|
|
447
|
+
integrity: getIntegrity(pickedPackage.dist),
|
|
448
|
+
tarball: normalizeRegistryUrl(pickedPackage.dist.tarball),
|
|
449
|
+
},
|
|
366
450
|
publishedAt: meta.time?.[pickedPackage.version],
|
|
367
|
-
alias: spec.jsrPkgName,
|
|
368
451
|
};
|
|
369
452
|
}
|
|
370
|
-
|
|
453
|
+
// Builds a `<prefix><pkgName>@<range>` specifier (or a bare `<prefix><range>`
|
|
454
|
+
// when the dependency alias matches the package name). Shared between the
|
|
455
|
+
// jsr and named-registry resolvers since they only differ in `prefix` and
|
|
456
|
+
// which spec field holds the package name.
|
|
457
|
+
function calcPrefixedSpecifier(prefix, pkgName, wantedDependency, version, defaultPinnedVersion) {
|
|
371
458
|
const range = calcRange(version, wantedDependency, defaultPinnedVersion);
|
|
372
|
-
if (!wantedDependency.alias ||
|
|
373
|
-
return
|
|
374
|
-
return
|
|
459
|
+
if (!wantedDependency.alias || pkgName === wantedDependency.alias)
|
|
460
|
+
return `${prefix}${range}`;
|
|
461
|
+
return `${prefix}${pkgName}@${range}`;
|
|
375
462
|
}
|
|
376
463
|
function calcSpecifier({ wantedDependency, spec, version, defaultPinnedVersion, }) {
|
|
377
464
|
if (wantedDependency.prevSpecifier === wantedDependency.bareSpecifier && wantedDependency.prevSpecifier && versionSelectorType(wantedDependency.prevSpecifier)?.type === 'tag') {
|
|
@@ -9,3 +9,8 @@ export interface JsrRegistryPackageSpec extends RegistryPackageSpec {
|
|
|
9
9
|
jsrPkgName: string;
|
|
10
10
|
}
|
|
11
11
|
export declare function parseJsrSpecifierToRegistryPackageSpec(rawSpecifier: string, alias: string | undefined, defaultTag: string): JsrRegistryPackageSpec | null;
|
|
12
|
+
export declare const BUILTIN_NAMED_REGISTRIES: Readonly<Record<string, string>>;
|
|
13
|
+
export interface NamedRegistryPackageSpec extends RegistryPackageSpec {
|
|
14
|
+
registryName: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function parseNamedRegistrySpecifierToRegistryPackageSpec(rawSpecifier: string, knownRegistryNames: ReadonlySet<string>, packageAlias: string | undefined, defaultTag: string): NamedRegistryPackageSpec | null;
|
|
@@ -1,18 +1,29 @@
|
|
|
1
|
+
import { PnpmError } from '@pnpm/error';
|
|
1
2
|
import { parseJsrSpecifier } from '@pnpm/resolving.jsr-specifier-parser';
|
|
2
3
|
import { parseNpmTarballUrl } from 'parse-npm-tarball-url';
|
|
4
|
+
import semver from 'semver';
|
|
3
5
|
import getVersionSelectorType from 'version-selector-type';
|
|
4
6
|
export function parseBareSpecifier(bareSpecifier, alias, defaultTag, registry) {
|
|
5
7
|
let name = alias;
|
|
6
8
|
if (bareSpecifier.startsWith('npm:')) {
|
|
7
9
|
bareSpecifier = bareSpecifier.slice(4);
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
// `npm:<version_selector>` — fall back to the outer dependency alias as
|
|
11
|
+
// the package name, mirroring the named-registry shape (e.g. `gh:^1.0.0`).
|
|
12
|
+
// Restricted to semver ranges/versions so unscoped package names like
|
|
13
|
+
// `npm:is-positive` keep their npm package-aliasing meaning.
|
|
14
|
+
if (alias && semver.validRange(bareSpecifier) != null) {
|
|
15
|
+
name = alias;
|
|
12
16
|
}
|
|
13
17
|
else {
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
const index = bareSpecifier.lastIndexOf('@');
|
|
19
|
+
if (index < 1) {
|
|
20
|
+
name = bareSpecifier;
|
|
21
|
+
bareSpecifier = defaultTag;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
name = bareSpecifier.slice(0, index);
|
|
25
|
+
bareSpecifier = bareSpecifier.slice(index + 1);
|
|
26
|
+
}
|
|
16
27
|
}
|
|
17
28
|
}
|
|
18
29
|
if (name) {
|
|
@@ -52,4 +63,75 @@ export function parseJsrSpecifierToRegistryPackageSpec(rawSpecifier, alias, defa
|
|
|
52
63
|
jsrPkgName: spec.jsrPkgName,
|
|
53
64
|
};
|
|
54
65
|
}
|
|
66
|
+
export const BUILTIN_NAMED_REGISTRIES = Object.freeze({
|
|
67
|
+
gh: 'https://npm.pkg.github.com/',
|
|
68
|
+
});
|
|
69
|
+
// Parses a named-registry specifier of the shape `<alias>:<body>` into a
|
|
70
|
+
// RegistryPackageSpec. Returns `null` when the specifier does not use one of
|
|
71
|
+
// the configured aliases, so the caller can fall through to other resolvers.
|
|
72
|
+
// Supported shapes:
|
|
73
|
+
// - `<alias>:[@<owner>/]<name>[@<version_selector>]`
|
|
74
|
+
// - `<alias>:<version_selector>` paired with a package alias
|
|
75
|
+
export function parseNamedRegistrySpecifierToRegistryPackageSpec(rawSpecifier, knownRegistryNames, packageAlias, defaultTag) {
|
|
76
|
+
const colon = rawSpecifier.indexOf(':');
|
|
77
|
+
if (colon <= 0)
|
|
78
|
+
return null;
|
|
79
|
+
const registryName = rawSpecifier.substring(0, colon);
|
|
80
|
+
if (!knownRegistryNames.has(registryName))
|
|
81
|
+
return null;
|
|
82
|
+
const body = rawSpecifier.substring(colon + 1);
|
|
83
|
+
let pkgName;
|
|
84
|
+
let versionSelector;
|
|
85
|
+
if (semver.validRange(body) != null) {
|
|
86
|
+
// `<alias>:<version_selector>` — fall back to the dependency alias as
|
|
87
|
+
// the package name. Unresolvable without one.
|
|
88
|
+
if (!packageAlias)
|
|
89
|
+
return null;
|
|
90
|
+
pkgName = packageAlias;
|
|
91
|
+
versionSelector = body;
|
|
92
|
+
}
|
|
93
|
+
else if (body[0] === '@') {
|
|
94
|
+
// `<alias>:@<owner>/<name>[@<version_selector>]` — scoped package.
|
|
95
|
+
const index = body.lastIndexOf('@');
|
|
96
|
+
if (index === 0) {
|
|
97
|
+
pkgName = body;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
pkgName = body.substring(0, index);
|
|
101
|
+
versionSelector = body.substring(index + '@'.length);
|
|
102
|
+
}
|
|
103
|
+
if (pkgName.indexOf('/') === -1 || pkgName.endsWith('/')) {
|
|
104
|
+
throw new PnpmError('INVALID_NAMED_REGISTRY_PACKAGE_NAME', `The package name '${pkgName}' in named registry '${registryName}:' is invalid`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else if (packageAlias?.startsWith('@')) {
|
|
108
|
+
// `<alias>:<tag>` paired with a scoped alias — body is a version
|
|
109
|
+
// selector (tag/dist-tag). Mirrors GitHub Packages, where the package
|
|
110
|
+
// is always scoped and a bare body is a tag.
|
|
111
|
+
pkgName = packageAlias;
|
|
112
|
+
versionSelector = body;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// `<alias>:<name>[@<version_selector>]` — unscoped package in body.
|
|
116
|
+
const index = body.lastIndexOf('@');
|
|
117
|
+
if (index < 1) {
|
|
118
|
+
pkgName = body;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
pkgName = body.substring(0, index);
|
|
122
|
+
versionSelector = body.substring(index + '@'.length);
|
|
123
|
+
}
|
|
124
|
+
if (!pkgName)
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const selector = getVersionSelectorType(versionSelector ?? defaultTag);
|
|
128
|
+
if (selector == null)
|
|
129
|
+
return null;
|
|
130
|
+
return {
|
|
131
|
+
fetchSpec: selector.normalized,
|
|
132
|
+
name: pkgName,
|
|
133
|
+
type: selector.type,
|
|
134
|
+
registryName,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
55
137
|
//# sourceMappingURL=parseBareSpecifier.js.map
|
package/lib/pickPackage.js
CHANGED
|
@@ -123,15 +123,30 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
123
123
|
: ABBREVIATED_META_DIR;
|
|
124
124
|
// Cache key includes fullMetadata to avoid returning abbreviated metadata when full metadata is requested.
|
|
125
125
|
const cacheKey = fullMetadata ? `${spec.name}:full` : spec.name;
|
|
126
|
+
const registryName = getRegistryName(opts.registry);
|
|
127
|
+
const pkgMirror = path.join(ctx.cacheDir, metaDir, registryName, `${encodePkgName(spec.name)}.jsonl`);
|
|
126
128
|
const cachedMeta = ctx.metaCache.get(cacheKey);
|
|
127
129
|
if (cachedMeta != null) {
|
|
130
|
+
// The in-memory cache may hold abbreviated metadata from an earlier call
|
|
131
|
+
// that didn't need `time` (no publishedBy then). If this call has
|
|
132
|
+
// publishedBy and the package was modified recently, upgrade to full
|
|
133
|
+
// metadata so the maturity check runs properly.
|
|
134
|
+
const upgrade = await maybeUpgradeAbbreviatedMetaForReleaseAge(ctx, spec, opts, cachedMeta);
|
|
135
|
+
let metaForCache = upgrade.meta;
|
|
136
|
+
if (upgrade.upgradedFrom != null) {
|
|
137
|
+
// Persist the upgraded meta to disk too: the on-disk mirror still holds
|
|
138
|
+
// the abbreviated form, so without this a fresh process would re-trigger
|
|
139
|
+
// the upgrade fetch on its next install.
|
|
140
|
+
metaForCache = opts.dryRun
|
|
141
|
+
? upgrade.meta
|
|
142
|
+
: persistUpgradedMeta(ctx, pkgMirror, upgrade.upgradedFrom);
|
|
143
|
+
ctx.metaCache.set(cacheKey, metaForCache);
|
|
144
|
+
}
|
|
128
145
|
return {
|
|
129
|
-
meta:
|
|
130
|
-
pickedPackage: pickMatchingVersionFinal(pickerOpts, spec,
|
|
146
|
+
meta: metaForCache,
|
|
147
|
+
pickedPackage: pickMatchingVersionFinal(pickerOpts, spec, metaForCache),
|
|
131
148
|
};
|
|
132
149
|
}
|
|
133
|
-
const registryName = getRegistryName(opts.registry);
|
|
134
|
-
const pkgMirror = path.join(ctx.cacheDir, metaDir, registryName, `${encodePkgName(spec.name)}.jsonl`);
|
|
135
150
|
return runLimited(pkgMirror, async (limit) => {
|
|
136
151
|
let metaCachedInStore;
|
|
137
152
|
if (ctx.offline === true || ctx.preferOffline === true || opts.pickLowestVersion) {
|
|
@@ -145,6 +160,17 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
145
160
|
throw new PnpmError('NO_OFFLINE_META', `Failed to resolve ${toRaw(spec)} in package mirror ${pkgMirror}`);
|
|
146
161
|
}
|
|
147
162
|
if (metaCachedInStore != null) {
|
|
163
|
+
// Disk-cached meta may be abbreviated; upgrade for the maturity check
|
|
164
|
+
// before letting pickMatchingVersionFinal warn-and-skip on missing time.
|
|
165
|
+
const upgrade = await maybeUpgradeAbbreviatedMetaForReleaseAge(ctx, spec, opts, metaCachedInStore);
|
|
166
|
+
metaCachedInStore = upgrade.meta;
|
|
167
|
+
if (upgrade.upgradedFrom != null) {
|
|
168
|
+
// Persist so the next install skips this upgrade fetch entirely.
|
|
169
|
+
if (!opts.dryRun) {
|
|
170
|
+
metaCachedInStore = persistUpgradedMeta(ctx, pkgMirror, upgrade.upgradedFrom);
|
|
171
|
+
}
|
|
172
|
+
ctx.metaCache.set(cacheKey, metaCachedInStore);
|
|
173
|
+
}
|
|
148
174
|
const pickedPackage = pickMatchingVersionFinal(pickerOpts, spec, metaCachedInStore);
|
|
149
175
|
if (pickedPackage) {
|
|
150
176
|
return {
|
|
@@ -169,7 +195,7 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
169
195
|
}
|
|
170
196
|
}
|
|
171
197
|
catch (err) {
|
|
172
|
-
if (ctx.strictPublishedByCheck) {
|
|
198
|
+
if (shouldRethrowFromFastPathCache(err, ctx.strictPublishedByCheck)) {
|
|
173
199
|
throw err;
|
|
174
200
|
}
|
|
175
201
|
}
|
|
@@ -190,10 +216,7 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
190
216
|
}
|
|
191
217
|
}
|
|
192
218
|
catch (err) {
|
|
193
|
-
|
|
194
|
-
// let the code fall through to the network fetch path which will get full metadata.
|
|
195
|
-
if (ctx.strictPublishedByCheck &&
|
|
196
|
-
!(isMissingTimeError(err))) {
|
|
219
|
+
if (shouldRethrowFromFastPathCache(err, ctx.strictPublishedByCheck)) {
|
|
197
220
|
throw err;
|
|
198
221
|
}
|
|
199
222
|
}
|
|
@@ -219,6 +242,19 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
219
242
|
if (fetchResult.notModified) {
|
|
220
243
|
metaCachedInStore = metaCachedInStore ?? await limit(async () => loadMeta(pkgMirror));
|
|
221
244
|
if (metaCachedInStore != null) {
|
|
245
|
+
// The cached metadata may be abbreviated (no per-version `time`).
|
|
246
|
+
// When minimumReleaseAge is active we need `time` for the maturity check,
|
|
247
|
+
// so upgrade to full metadata via a follow-up fetch when warranted.
|
|
248
|
+
// Without this, repeat installs of recently-modified packages would
|
|
249
|
+
// silently bypass the maturity check via the warn-and-skip fallback.
|
|
250
|
+
const upgrade = await maybeUpgradeAbbreviatedMetaForReleaseAge(ctx, spec, opts, metaCachedInStore);
|
|
251
|
+
metaCachedInStore = upgrade.meta;
|
|
252
|
+
if (upgrade.upgradedFrom != null && !opts.dryRun) {
|
|
253
|
+
// Persist the upgraded full metadata to disk so subsequent installs
|
|
254
|
+
// skip this upgrade fetch entirely (the cached meta will then have
|
|
255
|
+
// `time` populated, so the upgrade condition won't trigger).
|
|
256
|
+
metaCachedInStore = persistUpgradedMeta(ctx, pkgMirror, upgrade.upgradedFrom);
|
|
257
|
+
}
|
|
222
258
|
ctx.metaCache.set(cacheKey, metaCachedInStore);
|
|
223
259
|
return {
|
|
224
260
|
meta: metaCachedInStore,
|
|
@@ -307,6 +343,77 @@ export async function pickPackage(ctx, spec, opts) {
|
|
|
307
343
|
}
|
|
308
344
|
});
|
|
309
345
|
}
|
|
346
|
+
// When `minimumReleaseAge` is active and we have abbreviated metadata (which
|
|
347
|
+
// the npm registry serves by default and which omits per-version `time`),
|
|
348
|
+
// the maturity check can't run on the data we have. If the package has been
|
|
349
|
+
// modified since the maturity cutoff, re-fetch with `fullMetadata: true` so
|
|
350
|
+
// `time` is populated and the check can proceed properly. Without this,
|
|
351
|
+
// `pickMatchingVersionFinal` would fall back to its warn-and-skip path,
|
|
352
|
+
// silently bypassing the minimumReleaseAge guarantee for affected packages.
|
|
353
|
+
//
|
|
354
|
+
// Returns the original meta when no upgrade is needed. When an upgrade
|
|
355
|
+
// happens, returns both the upgraded meta and the underlying fetch result
|
|
356
|
+
// so callers can persist it to disk and avoid re-fetching on next install.
|
|
357
|
+
async function maybeUpgradeAbbreviatedMetaForReleaseAge(ctx, spec, opts, meta) {
|
|
358
|
+
if (ctx.offline === true ||
|
|
359
|
+
!opts.publishedBy ||
|
|
360
|
+
meta.time != null ||
|
|
361
|
+
opts.publishedByExclude?.(spec.name) === true) {
|
|
362
|
+
return { meta };
|
|
363
|
+
}
|
|
364
|
+
const modifiedDate = meta.modified ? new Date(meta.modified) : null;
|
|
365
|
+
const isModifiedValid = modifiedDate != null && !Number.isNaN(modifiedDate.getTime());
|
|
366
|
+
if (isModifiedValid && modifiedDate < opts.publishedBy) {
|
|
367
|
+
// The package was last modified before the maturity cutoff. No individual
|
|
368
|
+
// version can be newer than the cutoff, so the abbreviated form is fine.
|
|
369
|
+
return { meta };
|
|
370
|
+
}
|
|
371
|
+
// When `modified` is missing or malformed we fall through to the upgrade
|
|
372
|
+
// fetch: prefer correctness (run the maturity check on real `time` data)
|
|
373
|
+
// over saving a network call when our cached freshness signal is unusable.
|
|
374
|
+
// Forward etag/modified so the registry can answer 304 if the upgraded
|
|
375
|
+
// representation hasn't actually changed (rare on the npm registry where
|
|
376
|
+
// full and abbreviated have distinct etags, but cheap to support).
|
|
377
|
+
const fullFetchResult = await ctx.fetch(spec.name, {
|
|
378
|
+
authHeaderValue: opts.authHeaderValue,
|
|
379
|
+
fullMetadata: true,
|
|
380
|
+
etag: meta.etag,
|
|
381
|
+
modified: meta.modified,
|
|
382
|
+
registry: opts.registry,
|
|
383
|
+
});
|
|
384
|
+
if (fullFetchResult.notModified) {
|
|
385
|
+
// Upgrade fetch came back 304: keep the abbreviated meta. The downstream
|
|
386
|
+
// `pickMatchingVersionFinal` will fall through to its warn-and-skip path.
|
|
387
|
+
return { meta };
|
|
388
|
+
}
|
|
389
|
+
return { meta: fullFetchResult.meta, upgradedFrom: fullFetchResult };
|
|
390
|
+
}
|
|
391
|
+
// Returns true when a fast-path cache catch should rethrow under
|
|
392
|
+
// strictPublishedByCheck. ERR_PNPM_MISSING_TIME is excluded so callers fall
|
|
393
|
+
// through to the network fetch path, which can upgrade abbreviated cached
|
|
394
|
+
// metadata to full and run the maturity check on real `time` data.
|
|
395
|
+
function shouldRethrowFromFastPathCache(err, strictPublishedByCheck) {
|
|
396
|
+
return strictPublishedByCheck === true && !isMissingTimeError(err);
|
|
397
|
+
}
|
|
398
|
+
// Persists upgraded full metadata to the on-disk cache mirror and returns
|
|
399
|
+
// the meta to store in the in-memory cache. When `filterMetadata` is on, the
|
|
400
|
+
// in-memory and on-disk forms are both stripped via `clearMeta`; otherwise
|
|
401
|
+
// the original raw response body is written and the unstripped meta is kept.
|
|
402
|
+
function persistUpgradedMeta(ctx, pkgMirror, upgradedFrom) {
|
|
403
|
+
const metaForCache = ctx.filterMetadata ? clearMeta(upgradedFrom.meta) : upgradedFrom.meta;
|
|
404
|
+
const jsonForDisk = ctx.filterMetadata
|
|
405
|
+
? prepareJsonForDisk(metaForCache, upgradedFrom.etag)
|
|
406
|
+
: prepareJsonForDisk(upgradedFrom.meta, upgradedFrom.etag, upgradedFrom.jsonText);
|
|
407
|
+
runLimited(pkgMirror, (l) => l(async () => {
|
|
408
|
+
try {
|
|
409
|
+
await saveMeta(pkgMirror, jsonForDisk);
|
|
410
|
+
}
|
|
411
|
+
catch (err) { // eslint-disable-line
|
|
412
|
+
// We don't care if this file was not written to the cache
|
|
413
|
+
}
|
|
414
|
+
}));
|
|
415
|
+
return metaForCache;
|
|
416
|
+
}
|
|
310
417
|
function clearMeta(pkg) {
|
|
311
418
|
const versions = {};
|
|
312
419
|
for (const [version, info] of Object.entries(pkg.versions)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/resolving.npm-resolver",
|
|
3
|
-
"version": "1101.
|
|
3
|
+
"version": "1101.1.1",
|
|
4
4
|
"description": "Resolver for npm-hosted packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -28,38 +28,38 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@zkochan/retry": "^0.2.0",
|
|
30
30
|
"encode-registry": "^3.0.1",
|
|
31
|
-
"lru-cache": "^11.
|
|
31
|
+
"lru-cache": "^11.2.7",
|
|
32
32
|
"normalize-path": "^3.0.0",
|
|
33
|
-
"p-limit": "^7.
|
|
33
|
+
"p-limit": "^7.1.0",
|
|
34
34
|
"p-memoize": "8.0.0",
|
|
35
35
|
"parse-npm-tarball-url": "^5.0.0",
|
|
36
36
|
"path-temp": "^3.0.0",
|
|
37
37
|
"ramda": "npm:@pnpm/ramda@0.28.1",
|
|
38
38
|
"rename-overwrite": "^7.0.1",
|
|
39
|
-
"semver": "^7.7.
|
|
39
|
+
"semver": "^7.7.2",
|
|
40
40
|
"semver-utils": "^1.1.4",
|
|
41
41
|
"ssri": "13.0.1",
|
|
42
42
|
"version-selector-type": "^3.0.0",
|
|
43
|
-
"@pnpm/core-loggers": "1100.0.
|
|
44
|
-
"@pnpm/
|
|
45
|
-
"@pnpm/fetching.types": "1100.0.1",
|
|
43
|
+
"@pnpm/core-loggers": "1100.0.2",
|
|
44
|
+
"@pnpm/constants": "1100.0.0",
|
|
46
45
|
"@pnpm/crypto.hash": "1100.0.1",
|
|
47
46
|
"@pnpm/fs.graceful-fs": "1100.1.0",
|
|
48
|
-
"@pnpm/
|
|
49
|
-
"@pnpm/
|
|
50
|
-
"@pnpm/resolving.registry.
|
|
51
|
-
"@pnpm/resolving.
|
|
47
|
+
"@pnpm/error": "1100.0.0",
|
|
48
|
+
"@pnpm/fetching.types": "1100.0.1",
|
|
49
|
+
"@pnpm/resolving.registry.types": "1100.0.3",
|
|
50
|
+
"@pnpm/resolving.registry.pkg-metadata-filter": "1100.0.3",
|
|
51
|
+
"@pnpm/resolving.resolver-base": "1100.1.3",
|
|
52
|
+
"@pnpm/types": "1101.1.0",
|
|
52
53
|
"@pnpm/store.index": "1100.1.0",
|
|
53
|
-
"@pnpm/store.cafs": "1100.1.
|
|
54
|
-
"@pnpm/types": "1101.0.0",
|
|
54
|
+
"@pnpm/store.cafs": "1100.1.4",
|
|
55
55
|
"@pnpm/workspace.spec-parser": "1100.0.0",
|
|
56
56
|
"@pnpm/workspace.range-resolver": "1100.0.1",
|
|
57
|
-
"@pnpm/
|
|
58
|
-
"@pnpm/
|
|
57
|
+
"@pnpm/resolving.jsr-specifier-parser": "1100.0.0",
|
|
58
|
+
"@pnpm/config.pick-registry-for-package": "1100.0.3"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"@pnpm/logger": "
|
|
62
|
-
"@pnpm/worker": "^1100.1.
|
|
61
|
+
"@pnpm/logger": ">=1001.0.0 <1002.0.0",
|
|
62
|
+
"@pnpm/worker": "^1100.1.5"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@jest/globals": "30.3.0",
|
|
@@ -69,12 +69,12 @@
|
|
|
69
69
|
"@types/ssri": "^7.1.5",
|
|
70
70
|
"load-json-file": "^7.0.1",
|
|
71
71
|
"tempy": "3.0.0",
|
|
72
|
-
"@pnpm/config.version-policy": "1100.0.
|
|
72
|
+
"@pnpm/config.version-policy": "1100.0.3",
|
|
73
|
+
"@pnpm/network.fetch": "1100.0.4",
|
|
74
|
+
"@pnpm/resolving.npm-resolver": "1101.1.1",
|
|
73
75
|
"@pnpm/logger": "1100.0.0",
|
|
74
|
-
"@pnpm/network.fetch": "1100.0.2",
|
|
75
|
-
"@pnpm/resolving.npm-resolver": "1101.0.3",
|
|
76
76
|
"@pnpm/test-fixtures": "1100.0.0",
|
|
77
|
-
"@pnpm/testing.mock-agent": "1100.0.
|
|
77
|
+
"@pnpm/testing.mock-agent": "1100.0.4"
|
|
78
78
|
},
|
|
79
79
|
"engines": {
|
|
80
80
|
"node": ">=22.13"
|