@pnpm/resolving.npm-resolver 1102.1.2 → 1102.1.4
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 +1885 -0
- package/package.json +26 -26
- package/lib/clearMeta.d.ts +0 -16
- package/lib/clearMeta.js +0 -55
- package/lib/createNpmResolutionVerifier.d.ts +0 -89
- package/lib/createNpmResolutionVerifier.js +0 -724
- package/lib/fetch.d.ts +0 -37
- package/lib/fetch.js +0 -209
- package/lib/fetchAttestationPublishedAt.d.ts +0 -31
- package/lib/fetchAttestationPublishedAt.js +0 -99
- package/lib/fetchFullMetadataCached.d.ts +0 -33
- package/lib/fetchFullMetadataCached.js +0 -63
- package/lib/index.d.ts +0 -133
- package/lib/memoizeFetchMetadata.d.ts +0 -24
- package/lib/memoizeFetchMetadata.js +0 -37
- package/lib/normalizeRegistryUrl.d.ts +0 -4
- package/lib/normalizeRegistryUrl.js +0 -12
- package/lib/parseBareSpecifier.d.ts +0 -16
- package/lib/parseBareSpecifier.js +0 -143
- package/lib/pickPackage.d.ts +0 -109
- package/lib/pickPackage.js +0 -631
- package/lib/pickPackageFromMeta.d.ts +0 -20
- package/lib/pickPackageFromMeta.js +0 -227
- package/lib/toRaw.d.ts +0 -2
- package/lib/toRaw.js +0 -4
- package/lib/trustChecks.d.ts +0 -9
- package/lib/trustChecks.js +0 -96
- package/lib/violationCodes.d.ts +0 -14
- package/lib/violationCodes.js +0 -15
- package/lib/whichVersionIsPinned.d.ts +0 -2
- package/lib/whichVersionIsPinned.js +0 -36
- package/lib/workspacePrefToNpm.d.ts +0 -1
- package/lib/workspacePrefToNpm.js +0 -13
package/lib/fetch.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { FetchError, type FetchErrorRequest, type FetchErrorResponse } from '@pnpm/error';
|
|
2
|
-
import type { FetchFromRegistry, RetryTimeoutOptions } from '@pnpm/fetching.types';
|
|
3
|
-
import type { PackageMeta } from '@pnpm/resolving.registry.types';
|
|
4
|
-
export interface FetchMetadataResult {
|
|
5
|
-
meta: PackageMeta;
|
|
6
|
-
/**
|
|
7
|
-
* The raw registry response body, used only to mirror the response to disk
|
|
8
|
-
* without re-serializing `meta`. A fresh fetch always sets it, but it
|
|
9
|
-
* reaches only the caller that initiated the request: the phase-long memo
|
|
10
|
-
* cache holds a body-less clone (see memoizeFetchMetadata.ts), so cache
|
|
11
|
-
* hits see `undefined` and the cache never pins the body.
|
|
12
|
-
*/
|
|
13
|
-
jsonText: string | undefined;
|
|
14
|
-
etag?: string;
|
|
15
|
-
notModified?: false;
|
|
16
|
-
}
|
|
17
|
-
export interface FetchMetadataNotModifiedResult {
|
|
18
|
-
notModified: true;
|
|
19
|
-
}
|
|
20
|
-
export declare class RegistryResponseError extends FetchError {
|
|
21
|
-
readonly pkgName: string;
|
|
22
|
-
constructor(request: FetchErrorRequest, response: FetchErrorResponse, pkgName: string);
|
|
23
|
-
}
|
|
24
|
-
export interface FetchMetadataFromFromRegistryOptions {
|
|
25
|
-
fetch: FetchFromRegistry;
|
|
26
|
-
retry: RetryTimeoutOptions;
|
|
27
|
-
timeout: number;
|
|
28
|
-
fetchWarnTimeoutMs: number;
|
|
29
|
-
}
|
|
30
|
-
export interface FetchMetadataOptions {
|
|
31
|
-
registry: string;
|
|
32
|
-
authHeaderValue?: string;
|
|
33
|
-
fullMetadata?: boolean;
|
|
34
|
-
etag?: string;
|
|
35
|
-
modified?: string;
|
|
36
|
-
}
|
|
37
|
-
export declare function fetchMetadataFromFromRegistry(fetchOpts: FetchMetadataFromFromRegistryOptions, pkgName: string, { authHeaderValue, etag: cachedEtag, fullMetadata, modified: cachedModified, registry, }: FetchMetadataOptions): Promise<FetchMetadataResult | FetchMetadataNotModifiedResult>;
|
package/lib/fetch.js
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import url from 'node:url';
|
|
2
|
-
import util from 'node:util';
|
|
3
|
-
import { requestRetryLogger } from '@pnpm/core-loggers';
|
|
4
|
-
import { FetchError, PnpmError, redactUrlCredentials, } from '@pnpm/error';
|
|
5
|
-
import { globalWarn } from '@pnpm/logger';
|
|
6
|
-
import * as retry from '@zkochan/retry';
|
|
7
|
-
import semver from 'semver';
|
|
8
|
-
import { clearMeta } from './clearMeta.js';
|
|
9
|
-
/**
|
|
10
|
-
* Content type of an abbreviated (install-oriented) package metadata document.
|
|
11
|
-
* A spec-compliant registry echoes this in the response `Content-Type` when it
|
|
12
|
-
* honors the abbreviated `Accept` header. Its absence signals that the registry
|
|
13
|
-
* ignored the header and served the full document instead.
|
|
14
|
-
* https://github.com/npm/registry/blob/main/docs/responses/package-metadata.md
|
|
15
|
-
*/
|
|
16
|
-
const ABBREVIATED_META_CONTENT_TYPE = 'application/vnd.npm.install-v1+json';
|
|
17
|
-
export class RegistryResponseError extends FetchError {
|
|
18
|
-
pkgName;
|
|
19
|
-
constructor(request, response, pkgName) {
|
|
20
|
-
let hint;
|
|
21
|
-
if (response.status === 404) {
|
|
22
|
-
hint = `${pkgName} is not in the npm registry, or you have no permission to fetch it.`;
|
|
23
|
-
const nameWithoutVersion = stripTrailingSemverSuffix(pkgName);
|
|
24
|
-
if (nameWithoutVersion != null) {
|
|
25
|
-
hint += ` Did you mean ${nameWithoutVersion}?`;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
super(request, response, hint);
|
|
29
|
-
this.pkgName = pkgName;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Detect when a package name accidentally includes a `<version>` suffix
|
|
34
|
-
* (e.g. `lodash@4.17.21` or `lodash4.17.21`) and return the part before the
|
|
35
|
-
* version. Returns `undefined` when no semver suffix is present.
|
|
36
|
-
*
|
|
37
|
-
* Implemented as an O(n) scan to avoid polynomial backtracking on adversarial
|
|
38
|
-
* input (CodeQL: js/polynomial-redos).
|
|
39
|
-
*/
|
|
40
|
-
function stripTrailingSemverSuffix(pkgName) {
|
|
41
|
-
// Common case: "name@version" – split on the rightmost '@'.
|
|
42
|
-
// `atIdx > 0` rules out the leading '@' of scoped names like '@scope/foo'.
|
|
43
|
-
const atIdx = pkgName.lastIndexOf('@');
|
|
44
|
-
if (atIdx > 0 && semver.valid(pkgName.slice(atIdx + 1)) != null) {
|
|
45
|
-
return pkgName.slice(0, atIdx);
|
|
46
|
-
}
|
|
47
|
-
// Fallback: detect a trailing "<digits>.<digits>.<digits>" appended to a name
|
|
48
|
-
// with no separator (e.g. "foo1.0.0"). We walk backwards through three
|
|
49
|
-
// digit-blocks separated by dots; this is O(n) and free of regex backtracking.
|
|
50
|
-
let i = pkgName.length;
|
|
51
|
-
i = consumeTrailingDigits(pkgName, i);
|
|
52
|
-
if (i === pkgName.length || i === 0 || pkgName.charCodeAt(i - 1) !== 46 /* '.' */)
|
|
53
|
-
return undefined;
|
|
54
|
-
i--;
|
|
55
|
-
const beforePatch = i;
|
|
56
|
-
i = consumeTrailingDigits(pkgName, i);
|
|
57
|
-
if (i === beforePatch || i === 0 || pkgName.charCodeAt(i - 1) !== 46)
|
|
58
|
-
return undefined;
|
|
59
|
-
i--;
|
|
60
|
-
const beforeMinor = i;
|
|
61
|
-
i = consumeTrailingDigits(pkgName, i);
|
|
62
|
-
if (i === beforeMinor || i === 0)
|
|
63
|
-
return undefined;
|
|
64
|
-
if (semver.valid(pkgName.slice(i)) == null)
|
|
65
|
-
return undefined;
|
|
66
|
-
let prefix = pkgName.slice(0, i);
|
|
67
|
-
if (prefix.endsWith('@'))
|
|
68
|
-
prefix = prefix.slice(0, -1);
|
|
69
|
-
return prefix.length > 0 ? prefix : undefined;
|
|
70
|
-
}
|
|
71
|
-
function consumeTrailingDigits(s, end) {
|
|
72
|
-
let i = end;
|
|
73
|
-
while (i > 0) {
|
|
74
|
-
const c = s.charCodeAt(i - 1);
|
|
75
|
-
if (c < 48 || c > 57)
|
|
76
|
-
break;
|
|
77
|
-
i--;
|
|
78
|
-
}
|
|
79
|
-
return i;
|
|
80
|
-
}
|
|
81
|
-
export async function fetchMetadataFromFromRegistry(fetchOpts, pkgName, { authHeaderValue, etag: cachedEtag, fullMetadata, modified: cachedModified, registry, }) {
|
|
82
|
-
const uri = toUri(pkgName, registry);
|
|
83
|
-
const op = retry.operation(fetchOpts.retry);
|
|
84
|
-
return new Promise((resolve, reject) => {
|
|
85
|
-
op.attempt(async (attempt) => {
|
|
86
|
-
let response;
|
|
87
|
-
const startTime = Date.now();
|
|
88
|
-
try {
|
|
89
|
-
response = await fetchOpts.fetch(uri, {
|
|
90
|
-
authHeaderValue,
|
|
91
|
-
compress: true,
|
|
92
|
-
fullMetadata,
|
|
93
|
-
ifNoneMatch: cachedEtag,
|
|
94
|
-
ifModifiedSince: cachedModified ? new Date(cachedModified).toUTCString() : undefined,
|
|
95
|
-
retry: fetchOpts.retry,
|
|
96
|
-
timeout: fetchOpts.timeout,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
catch (error) { // eslint-disable-line
|
|
100
|
-
// Redact credentials embedded in the URL from the cause as well, not
|
|
101
|
-
// just the top-level message: a reporter or debugger that renders
|
|
102
|
-
// `error.cause` would otherwise print the raw URL-bearing message. The
|
|
103
|
-
// `stack` string embeds the original (pre-mutation) message, so redact
|
|
104
|
-
// it too — mutating `message` alone leaves the credentials in `stack`.
|
|
105
|
-
if (util.types.isNativeError(error)) {
|
|
106
|
-
if (typeof error.message === 'string')
|
|
107
|
-
error.message = redactUrlCredentials(error.message);
|
|
108
|
-
if (typeof error.stack === 'string')
|
|
109
|
-
error.stack = redactUrlCredentials(error.stack);
|
|
110
|
-
}
|
|
111
|
-
reject(new PnpmError('META_FETCH_FAIL', redactUrlCredentials(`GET ${uri}: ${error.message}`), { attempts: attempt, cause: error }));
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (response.status === 304) {
|
|
115
|
-
resolve({ notModified: true });
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (response.status >= 400) {
|
|
119
|
-
const request = {
|
|
120
|
-
authHeaderValue,
|
|
121
|
-
url: uri,
|
|
122
|
-
};
|
|
123
|
-
reject(new RegistryResponseError(request, response, pkgName));
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
// Here we only retry broken JSON responses.
|
|
127
|
-
// Other HTTP issues are retried by the @pnpm/network.fetch library
|
|
128
|
-
try {
|
|
129
|
-
const jsonText = await response.text();
|
|
130
|
-
const meta = JSON.parse(jsonText);
|
|
131
|
-
// Check if request took longer than expected
|
|
132
|
-
const elapsedMs = Date.now() - startTime;
|
|
133
|
-
if (elapsedMs > fetchOpts.fetchWarnTimeoutMs) {
|
|
134
|
-
globalWarn(`Request took ${elapsedMs}ms: ${uri}`);
|
|
135
|
-
}
|
|
136
|
-
resolve({
|
|
137
|
-
...normalizeAbbreviatedResponse({ fullMetadata, meta, jsonText, response }),
|
|
138
|
-
etag: response.headers.get('etag') ?? undefined,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
catch (error) { // eslint-disable-line
|
|
142
|
-
const timeout = op.retry(new PnpmError('BROKEN_METADATA_JSON', error.message));
|
|
143
|
-
if (timeout === false) {
|
|
144
|
-
reject(op.mainError());
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
// Extract error properties into a plain object because Error properties
|
|
148
|
-
// are non-enumerable and don't serialize well through the logging system
|
|
149
|
-
const errorInfo = {
|
|
150
|
-
name: error.name,
|
|
151
|
-
message: error.message,
|
|
152
|
-
code: error.code,
|
|
153
|
-
errno: error.errno,
|
|
154
|
-
};
|
|
155
|
-
requestRetryLogger.debug({
|
|
156
|
-
attempt,
|
|
157
|
-
error: errorInfo,
|
|
158
|
-
maxRetries: fetchOpts.retry.retries,
|
|
159
|
-
method: 'GET',
|
|
160
|
-
timeout,
|
|
161
|
-
url: uri,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* When the resolver asked for abbreviated metadata but the registry ignored the
|
|
169
|
-
* `Accept` header and returned the full document (detected via the response
|
|
170
|
-
* `Content-Type`), strip it down to the abbreviated field set so downstream
|
|
171
|
-
* consumers — the in-memory cache, the on-disk mirror, and the resolver — never
|
|
172
|
-
* carry the megabytes of install-irrelevant data (scripts, exports, readme,
|
|
173
|
-
* custom fields) that a full document contains.
|
|
174
|
-
*
|
|
175
|
-
* Registries that honor the header (e.g. the npm registry) echo the abbreviated
|
|
176
|
-
* `Content-Type`, so this is a no-op for them: no re-serialization, no field
|
|
177
|
-
* stripping — the happy path pays nothing.
|
|
178
|
-
*/
|
|
179
|
-
function normalizeAbbreviatedResponse({ fullMetadata, meta, jsonText, response }) {
|
|
180
|
-
if (fullMetadata)
|
|
181
|
-
return { meta, jsonText };
|
|
182
|
-
if (parseMediaType(response.headers.get('content-type')) === ABBREVIATED_META_CONTENT_TYPE)
|
|
183
|
-
return { meta, jsonText };
|
|
184
|
-
const normalized = clearMeta(meta);
|
|
185
|
-
return { meta: normalized, jsonText: JSON.stringify(normalized) };
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Extracts the media type from a `Content-Type` header value, dropping
|
|
189
|
-
* parameters such as `; charset=utf-8`. Media types are case-insensitive
|
|
190
|
-
* (RFC 9110 §8.3.1), so the result is lowercased for comparison.
|
|
191
|
-
*/
|
|
192
|
-
function parseMediaType(contentType) {
|
|
193
|
-
if (contentType == null)
|
|
194
|
-
return undefined;
|
|
195
|
-
const semicolonIndex = contentType.indexOf(';');
|
|
196
|
-
const mediaType = semicolonIndex === -1 ? contentType : contentType.slice(0, semicolonIndex);
|
|
197
|
-
return mediaType.trim().toLowerCase();
|
|
198
|
-
}
|
|
199
|
-
function toUri(pkgName, registry) {
|
|
200
|
-
let encodedName;
|
|
201
|
-
if (pkgName[0] === '@') {
|
|
202
|
-
encodedName = `@${encodeURIComponent(pkgName.slice(1))}`;
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
encodedName = encodeURIComponent(pkgName);
|
|
206
|
-
}
|
|
207
|
-
return new url.URL(encodedName, registry.endsWith('/') ? registry : `${registry}/`).toString();
|
|
208
|
-
}
|
|
209
|
-
//# sourceMappingURL=fetch.js.map
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { FetchMetadataFromFromRegistryOptions } from './fetch.js';
|
|
2
|
-
/**
|
|
3
|
-
* Per-version publish timestamp from npm's attestation endpoint —
|
|
4
|
-
* `/-/npm/v1/attestations/<name>@<version>`.
|
|
5
|
-
*
|
|
6
|
-
* The response is a small JSON document containing one or more Sigstore
|
|
7
|
-
* bundles. We read `bundle.verificationMaterial.tlogEntries[].integratedTime`
|
|
8
|
-
* (the Rekor inclusion time) and surface it as an ISO date. This is a
|
|
9
|
-
* couple of seconds after the actual publish — close enough for a
|
|
10
|
-
* release-age policy that operates in minutes/hours/days.
|
|
11
|
-
*
|
|
12
|
-
* We deliberately do **not** verify the Sigstore signature here: the
|
|
13
|
-
* trust model is identical to reading the registry's `time` field on
|
|
14
|
-
* the full metadata document. The win is bandwidth — the attestation
|
|
15
|
-
* payload is tens of KB versus the multi-MB full metadata document, so
|
|
16
|
-
* cold-cache + `--frozen-lockfile` installs against a fleet of
|
|
17
|
-
* provenance-published packages pay far less to verify timestamps.
|
|
18
|
-
*
|
|
19
|
-
* Returns `undefined` when:
|
|
20
|
-
*
|
|
21
|
-
* - The package has no published attestations (`404`).
|
|
22
|
-
* - The response is malformed or missing the timestamp.
|
|
23
|
-
* - The request itself fails (network error, registry 5xx).
|
|
24
|
-
*
|
|
25
|
-
* In all of those cases the caller falls back to fetching full metadata.
|
|
26
|
-
*/
|
|
27
|
-
export interface FetchAttestationOptions {
|
|
28
|
-
registry: string;
|
|
29
|
-
authHeaderValue?: string;
|
|
30
|
-
}
|
|
31
|
-
export declare function fetchAttestationPublishedAt(fetchOpts: FetchMetadataFromFromRegistryOptions, pkgName: string, version: string, opts: FetchAttestationOptions): Promise<string | undefined>;
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import * as retry from '@zkochan/retry';
|
|
2
|
-
export async function fetchAttestationPublishedAt(fetchOpts, pkgName, version, opts) {
|
|
3
|
-
const url = `${opts.registry.replace(/\/$/, '')}/-/npm/v1/attestations/${pkgName}@${version}`;
|
|
4
|
-
const retryOperation = retry.operation(fetchOpts.retry);
|
|
5
|
-
return new Promise((resolve) => {
|
|
6
|
-
retryOperation.attempt(async () => {
|
|
7
|
-
let response;
|
|
8
|
-
try {
|
|
9
|
-
response = await fetchOpts.fetch(url, {
|
|
10
|
-
authHeaderValue: opts.authHeaderValue,
|
|
11
|
-
retry: fetchOpts.retry,
|
|
12
|
-
timeout: fetchOpts.timeout,
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
// Network errors fall through to the full-metadata path; the
|
|
17
|
-
// caller's `fetchFullMetadataCached` has its own retry policy.
|
|
18
|
-
resolve(undefined);
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
// 404 = package never published attestations. Other 4xx/5xx also
|
|
22
|
-
// mean "can't get an answer from this endpoint, fall back."
|
|
23
|
-
if (response.status >= 400) {
|
|
24
|
-
resolve(undefined);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
let body;
|
|
28
|
-
try {
|
|
29
|
-
body = await response.json();
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
resolve(undefined);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
resolve(extractPublishedAt(body));
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Pull the earliest `integratedTime` across every attestation bundle in
|
|
41
|
-
* the response and convert it to an ISO timestamp. Earliest is the
|
|
42
|
-
* conservative choice: if two attestations disagree (e.g. publish
|
|
43
|
-
* v0.1 vs SLSA provenance v1), we attribute the publish to the older
|
|
44
|
-
* Rekor entry. The Rekor timestamp is what tells us when the artifact
|
|
45
|
-
* existed in a transparency log — that's the floor on publish time.
|
|
46
|
-
*/
|
|
47
|
-
function extractPublishedAt(body) {
|
|
48
|
-
if (!body || typeof body !== 'object')
|
|
49
|
-
return undefined;
|
|
50
|
-
const attestations = body.attestations;
|
|
51
|
-
if (!Array.isArray(attestations))
|
|
52
|
-
return undefined;
|
|
53
|
-
let earliestSeconds;
|
|
54
|
-
for (const attestation of attestations) {
|
|
55
|
-
const seconds = readEarliestIntegratedTime(attestation);
|
|
56
|
-
if (seconds == null)
|
|
57
|
-
continue;
|
|
58
|
-
if (earliestSeconds == null || seconds < earliestSeconds) {
|
|
59
|
-
earliestSeconds = seconds;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (earliestSeconds == null)
|
|
63
|
-
return undefined;
|
|
64
|
-
return new Date(earliestSeconds * 1000).toISOString();
|
|
65
|
-
}
|
|
66
|
-
function readEarliestIntegratedTime(attestation) {
|
|
67
|
-
if (!attestation || typeof attestation !== 'object')
|
|
68
|
-
return undefined;
|
|
69
|
-
const bundle = attestation.bundle;
|
|
70
|
-
if (!bundle || typeof bundle !== 'object')
|
|
71
|
-
return undefined;
|
|
72
|
-
const verificationMaterial = bundle.verificationMaterial;
|
|
73
|
-
if (!verificationMaterial || typeof verificationMaterial !== 'object')
|
|
74
|
-
return undefined;
|
|
75
|
-
const tlogEntries = verificationMaterial.tlogEntries;
|
|
76
|
-
if (!Array.isArray(tlogEntries))
|
|
77
|
-
return undefined;
|
|
78
|
-
let earliest;
|
|
79
|
-
for (const entry of tlogEntries) {
|
|
80
|
-
if (!entry || typeof entry !== 'object')
|
|
81
|
-
continue;
|
|
82
|
-
const rawIntegratedTime = entry.integratedTime;
|
|
83
|
-
// npm serializes integratedTime as a string ("1778583836") to avoid
|
|
84
|
-
// JSON precision loss; accept either string or number defensively.
|
|
85
|
-
const seconds = parseIntegratedTimeSeconds(rawIntegratedTime);
|
|
86
|
-
if (seconds == null)
|
|
87
|
-
continue;
|
|
88
|
-
if (earliest == null || seconds < earliest)
|
|
89
|
-
earliest = seconds;
|
|
90
|
-
}
|
|
91
|
-
return earliest;
|
|
92
|
-
}
|
|
93
|
-
function parseIntegratedTimeSeconds(raw) {
|
|
94
|
-
const seconds = typeof raw === 'string' ? Number(raw) : typeof raw === 'number' ? raw : NaN;
|
|
95
|
-
if (!Number.isFinite(seconds) || seconds <= 0)
|
|
96
|
-
return undefined;
|
|
97
|
-
return seconds;
|
|
98
|
-
}
|
|
99
|
-
//# sourceMappingURL=fetchAttestationPublishedAt.js.map
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { PackageMeta } from '@pnpm/resolving.registry.types';
|
|
2
|
-
import { type FetchMetadataFromFromRegistryOptions } from './fetch.js';
|
|
3
|
-
export interface FetchMetadataCachedOptions {
|
|
4
|
-
registry: string;
|
|
5
|
-
authHeaderValue?: string;
|
|
6
|
-
/**
|
|
7
|
-
* pnpm's on-disk cache directory. When set, the call issues a conditional
|
|
8
|
-
* GET against the matching mirror the resolver populates: a 304 Not
|
|
9
|
-
* Modified response serves the body from disk, a 200 writes the new body
|
|
10
|
-
* back. Omit to disable caching — every call re-fetches.
|
|
11
|
-
*/
|
|
12
|
-
cacheDir?: string;
|
|
13
|
-
}
|
|
14
|
-
export type FetchFullMetadataCachedOptions = FetchMetadataCachedOptions;
|
|
15
|
-
/**
|
|
16
|
-
* Fetch a full registry metadata document for `pkgName`, reusing pnpm's
|
|
17
|
-
* shared on-disk metadata mirror when `cacheDir` is supplied. Built for the
|
|
18
|
-
* `minimumReleaseAge` lockfile revalidation gate, which needs the `time`
|
|
19
|
-
* field that abbreviated metadata omits; the cache reuse keeps repeat
|
|
20
|
-
* installs from re-downloading the same multi-megabyte document for every
|
|
21
|
-
* locked package.
|
|
22
|
-
*/
|
|
23
|
-
export declare function fetchFullMetadataCached(fetchOpts: FetchMetadataFromFromRegistryOptions, pkgName: string, opts: FetchFullMetadataCachedOptions): Promise<PackageMeta>;
|
|
24
|
-
/**
|
|
25
|
-
* Sibling of {@link fetchFullMetadataCached} that hits the abbreviated
|
|
26
|
-
* metadata endpoint (`Accept: application/vnd.npm.install-v1+json`) and
|
|
27
|
-
* caches under `ABBREVIATED_META_DIR` — the same mirror the resolver
|
|
28
|
-
* populates by default. Used by the lockfile verification gate as a
|
|
29
|
-
* cheap upper-bound check: if the package's `modified` field is older
|
|
30
|
-
* than the policy cutoff, every version in it predates the cutoff and
|
|
31
|
-
* no per-version timestamp lookup is needed.
|
|
32
|
-
*/
|
|
33
|
-
export declare function fetchAbbreviatedMetadataCached(fetchOpts: FetchMetadataFromFromRegistryOptions, pkgName: string, opts: FetchMetadataCachedOptions): Promise<PackageMeta>;
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { ABBREVIATED_META_DIR, FULL_META_DIR } from '@pnpm/constants';
|
|
2
|
-
import { PnpmError } from '@pnpm/error';
|
|
3
|
-
import { fetchMetadataFromFromRegistry } from './fetch.js';
|
|
4
|
-
import { getPkgMirrorPath, loadMeta, loadMetaHeaders, prepareJsonForDisk, saveMeta } from './pickPackage.js';
|
|
5
|
-
/**
|
|
6
|
-
* Fetch a full registry metadata document for `pkgName`, reusing pnpm's
|
|
7
|
-
* shared on-disk metadata mirror when `cacheDir` is supplied. Built for the
|
|
8
|
-
* `minimumReleaseAge` lockfile revalidation gate, which needs the `time`
|
|
9
|
-
* field that abbreviated metadata omits; the cache reuse keeps repeat
|
|
10
|
-
* installs from re-downloading the same multi-megabyte document for every
|
|
11
|
-
* locked package.
|
|
12
|
-
*/
|
|
13
|
-
export async function fetchFullMetadataCached(fetchOpts, pkgName, opts) {
|
|
14
|
-
return fetchMetadataCached(fetchOpts, pkgName, { ...opts, fullMetadata: true, metaDir: FULL_META_DIR });
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Sibling of {@link fetchFullMetadataCached} that hits the abbreviated
|
|
18
|
-
* metadata endpoint (`Accept: application/vnd.npm.install-v1+json`) and
|
|
19
|
-
* caches under `ABBREVIATED_META_DIR` — the same mirror the resolver
|
|
20
|
-
* populates by default. Used by the lockfile verification gate as a
|
|
21
|
-
* cheap upper-bound check: if the package's `modified` field is older
|
|
22
|
-
* than the policy cutoff, every version in it predates the cutoff and
|
|
23
|
-
* no per-version timestamp lookup is needed.
|
|
24
|
-
*/
|
|
25
|
-
export async function fetchAbbreviatedMetadataCached(fetchOpts, pkgName, opts) {
|
|
26
|
-
return fetchMetadataCached(fetchOpts, pkgName, { ...opts, fullMetadata: false, metaDir: ABBREVIATED_META_DIR });
|
|
27
|
-
}
|
|
28
|
-
async function fetchMetadataCached(fetchOpts, pkgName, opts) {
|
|
29
|
-
const pkgMirror = opts.cacheDir != null
|
|
30
|
-
? getPkgMirrorPath(opts.cacheDir, opts.metaDir, opts.registry, pkgName)
|
|
31
|
-
: null;
|
|
32
|
-
const cacheHeaders = pkgMirror != null ? await loadMetaHeaders(pkgMirror) : null;
|
|
33
|
-
const result = await fetchMetadataFromFromRegistry(fetchOpts, pkgName, {
|
|
34
|
-
registry: opts.registry,
|
|
35
|
-
authHeaderValue: opts.authHeaderValue,
|
|
36
|
-
fullMetadata: opts.fullMetadata,
|
|
37
|
-
etag: cacheHeaders?.etag,
|
|
38
|
-
modified: cacheHeaders?.modified,
|
|
39
|
-
});
|
|
40
|
-
if ('notModified' in result && result.notModified) {
|
|
41
|
-
if (pkgMirror == null) {
|
|
42
|
-
// We didn't send conditional headers (no cacheDir), but the registry
|
|
43
|
-
// returned 304 anyway. There's no body to fall back on.
|
|
44
|
-
throw new PnpmError('META_NOT_MODIFIED_WITHOUT_CACHE', `Registry returned 304 for ${pkgName} without an existing cache to refresh.`);
|
|
45
|
-
}
|
|
46
|
-
const meta = await loadMeta(pkgMirror);
|
|
47
|
-
if (meta == null) {
|
|
48
|
-
// Cache file vanished between header-load and meta-load (concurrent
|
|
49
|
-
// store cleanup, antivirus, etc.).
|
|
50
|
-
throw new PnpmError('META_CACHE_MISSING_AFTER_304', `Metadata cache for ${pkgName} disappeared between headers read and full read.`);
|
|
51
|
-
}
|
|
52
|
-
return meta;
|
|
53
|
-
}
|
|
54
|
-
if (pkgMirror != null) {
|
|
55
|
-
// Persist so the next install can do a headers-only conditional GET.
|
|
56
|
-
// Fire-and-forget — a cache-write failure isn't a reason to fail the
|
|
57
|
-
// caller; the next install just won't get the speedup.
|
|
58
|
-
const json = prepareJsonForDisk(result.meta, result.etag, result.jsonText);
|
|
59
|
-
saveMeta(pkgMirror, json).catch(() => { });
|
|
60
|
-
}
|
|
61
|
-
return result.meta;
|
|
62
|
-
}
|
|
63
|
-
//# sourceMappingURL=fetchFullMetadataCached.js.map
|
package/lib/index.d.ts
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { PnpmError } from '@pnpm/error';
|
|
2
|
-
import type { FetchFromRegistry, GetAuthHeader, RetryTimeoutOptions } from '@pnpm/fetching.types';
|
|
3
|
-
import type { PackageMeta } from '@pnpm/resolving.registry.types';
|
|
4
|
-
import type { DirectoryResolution, LatestInfo, LatestQuery, PkgResolutionId, PreferredVersions, ResolveOptions, ResolveResult, TarballResolution, WantedDependency, WorkspacePackages } from '@pnpm/resolving.resolver-base';
|
|
5
|
-
import type { DependencyManifest, PackageVersionPolicy, PinnedVersion, Registries, TrustPolicy } from '@pnpm/types';
|
|
6
|
-
import { fetchMetadataFromFromRegistry, type FetchMetadataFromFromRegistryOptions, RegistryResponseError } from './fetch.js';
|
|
7
|
-
import { BUILTIN_NAMED_REGISTRIES, parseBareSpecifier, type RegistryPackageSpec } from './parseBareSpecifier.js';
|
|
8
|
-
import { type PackageMetaCache, pickPackage, type PickPackageOptions } from './pickPackage.js';
|
|
9
|
-
import { pickPackageFromMeta, pickVersionByVersionRange } from './pickPackageFromMeta.js';
|
|
10
|
-
import { workspacePrefToNpm } from './workspacePrefToNpm.js';
|
|
11
|
-
export interface NoMatchingVersionErrorOptions {
|
|
12
|
-
wantedDependency: WantedDependency;
|
|
13
|
-
packageMeta: PackageMeta;
|
|
14
|
-
registry: string;
|
|
15
|
-
}
|
|
16
|
-
export declare class NoMatchingVersionError extends PnpmError {
|
|
17
|
-
readonly packageMeta: PackageMeta;
|
|
18
|
-
constructor(opts: NoMatchingVersionErrorOptions);
|
|
19
|
-
}
|
|
20
|
-
export declare function formatTimeAgo(date: Date): string | null;
|
|
21
|
-
export { BUILTIN_NAMED_REGISTRIES, fetchMetadataFromFromRegistry, type FetchMetadataFromFromRegistryOptions, type PackageMeta, type PackageMetaCache, parseBareSpecifier, pickPackageFromMeta, pickVersionByVersionRange, type RegistryPackageSpec, RegistryResponseError, workspacePrefToNpm, };
|
|
22
|
-
export { createNpmResolutionVerifier, type CreateNpmResolutionVerifierOptions } from './createNpmResolutionVerifier.js';
|
|
23
|
-
export { MINIMUM_RELEASE_AGE_VIOLATION_CODE, TRUST_DOWNGRADE_VIOLATION_CODE, } from './violationCodes.js';
|
|
24
|
-
export { whichVersionIsPinned } from './whichVersionIsPinned.js';
|
|
25
|
-
export interface ResolverFactoryOptions {
|
|
26
|
-
cacheDir: string;
|
|
27
|
-
storeDir?: string;
|
|
28
|
-
frozenStore?: boolean;
|
|
29
|
-
fullMetadata?: boolean;
|
|
30
|
-
filterMetadata?: boolean;
|
|
31
|
-
offline?: boolean;
|
|
32
|
-
preferOffline?: boolean;
|
|
33
|
-
retry?: RetryTimeoutOptions;
|
|
34
|
-
timeout?: number;
|
|
35
|
-
registries: Registries;
|
|
36
|
-
namedRegistries?: Record<string, string>;
|
|
37
|
-
saveWorkspaceProtocol?: boolean | 'rolling';
|
|
38
|
-
preserveAbsolutePaths?: boolean;
|
|
39
|
-
ignoreMissingTimeField?: boolean;
|
|
40
|
-
fetchWarnTimeoutMs?: number;
|
|
41
|
-
/** Pre-populated metadata cache. When provided, the resolver uses this
|
|
42
|
-
* instead of creating a new LRU cache. Useful for servers that keep
|
|
43
|
-
* metadata in SQLite or persist it across requests. */
|
|
44
|
-
metaCache?: PackageMetaCache;
|
|
45
|
-
}
|
|
46
|
-
export interface NpmResolveResult extends ResolveResult {
|
|
47
|
-
latest?: string;
|
|
48
|
-
manifest: DependencyManifest;
|
|
49
|
-
resolution: TarballResolution;
|
|
50
|
-
resolvedVia: 'npm-registry';
|
|
51
|
-
}
|
|
52
|
-
export interface JsrResolveResult extends ResolveResult {
|
|
53
|
-
alias: string;
|
|
54
|
-
manifest: DependencyManifest;
|
|
55
|
-
resolution: TarballResolution;
|
|
56
|
-
resolvedVia: 'jsr-registry';
|
|
57
|
-
}
|
|
58
|
-
export interface NamedRegistryResolveResult extends ResolveResult {
|
|
59
|
-
alias: string;
|
|
60
|
-
/** The named-registry alias that was matched, e.g. `gh` or a user-defined name. */
|
|
61
|
-
registryName: string;
|
|
62
|
-
manifest: DependencyManifest;
|
|
63
|
-
resolution: TarballResolution;
|
|
64
|
-
resolvedVia: 'named-registry';
|
|
65
|
-
}
|
|
66
|
-
export interface WorkspaceResolveResult extends ResolveResult {
|
|
67
|
-
manifest: DependencyManifest;
|
|
68
|
-
resolution: DirectoryResolution;
|
|
69
|
-
resolvedVia: 'workspace';
|
|
70
|
-
}
|
|
71
|
-
export type NpmResolver = (wantedDependency: WantedDependency & {
|
|
72
|
-
optional?: boolean;
|
|
73
|
-
}, opts: ResolveFromNpmOptions) => Promise<NpmResolveResult | JsrResolveResult | NamedRegistryResolveResult | WorkspaceResolveResult | null>;
|
|
74
|
-
export type ResolveLatestFromNpmStyle = (query: LatestQuery, opts: ResolveOptions) => Promise<LatestInfo | undefined>;
|
|
75
|
-
export declare function createNpmResolver(fetchFromRegistry: FetchFromRegistry, getAuthHeader: GetAuthHeader, opts: ResolverFactoryOptions): {
|
|
76
|
-
resolveFromNpm: NpmResolver;
|
|
77
|
-
resolveFromJsr: NpmResolver;
|
|
78
|
-
resolveFromNamedRegistry: NpmResolver;
|
|
79
|
-
resolveLatestFromNpm: ResolveLatestFromNpmStyle;
|
|
80
|
-
resolveLatestFromJsr: ResolveLatestFromNpmStyle;
|
|
81
|
-
resolveLatestFromNamedRegistry: ResolveLatestFromNpmStyle;
|
|
82
|
-
clearCache: () => void;
|
|
83
|
-
};
|
|
84
|
-
export interface ResolveFromNpmContext {
|
|
85
|
-
pickPackage: (spec: RegistryPackageSpec, opts: PickPackageOptions) => ReturnType<typeof pickPackage>;
|
|
86
|
-
getAuthHeaderValueByURI: GetAuthHeader;
|
|
87
|
-
registries: Registries;
|
|
88
|
-
namedRegistries: Record<string, string>;
|
|
89
|
-
namedRegistryNames: ReadonlySet<string>;
|
|
90
|
-
saveWorkspaceProtocol?: boolean | 'rolling';
|
|
91
|
-
peekManifestFromStore?: (opts: {
|
|
92
|
-
id: PkgResolutionId;
|
|
93
|
-
integrity: string;
|
|
94
|
-
name?: string;
|
|
95
|
-
version?: string;
|
|
96
|
-
}) => Promise<DependencyManifest | undefined>;
|
|
97
|
-
/** Deduplicates the held-back-update warning per `(name, picked, preferred)`. */
|
|
98
|
-
warnedHeldBackUpdates: Set<string>;
|
|
99
|
-
}
|
|
100
|
-
export type ResolveFromNpmOptions = {
|
|
101
|
-
alwaysTryWorkspacePackages?: boolean;
|
|
102
|
-
defaultTag?: string;
|
|
103
|
-
publishedBy?: Date;
|
|
104
|
-
publishedByExclude?: PackageVersionPolicy;
|
|
105
|
-
pickLowestVersion?: boolean;
|
|
106
|
-
trustPolicy?: TrustPolicy;
|
|
107
|
-
trustPolicyExclude?: PackageVersionPolicy;
|
|
108
|
-
trustPolicyIgnoreAfter?: number;
|
|
109
|
-
dryRun?: boolean;
|
|
110
|
-
lockfileDir?: string;
|
|
111
|
-
preferredVersions?: PreferredVersions;
|
|
112
|
-
preferWorkspacePackages?: boolean;
|
|
113
|
-
update?: false | 'compatible' | 'latest';
|
|
114
|
-
updateRequested?: boolean;
|
|
115
|
-
updateChecksums?: boolean;
|
|
116
|
-
injectWorkspacePackages?: boolean;
|
|
117
|
-
calcSpecifier?: boolean;
|
|
118
|
-
pinnedVersion?: PinnedVersion;
|
|
119
|
-
} & ({
|
|
120
|
-
projectDir?: string;
|
|
121
|
-
workspacePackages?: undefined;
|
|
122
|
-
} | {
|
|
123
|
-
projectDir: string;
|
|
124
|
-
workspacePackages: WorkspacePackages;
|
|
125
|
-
});
|
|
126
|
-
/**
|
|
127
|
-
* Construct the LRU `PackageMetaCache` instance the resolver uses by
|
|
128
|
-
* default. Exported so the install layer can build one cache and hand
|
|
129
|
-
* the same reference to both the resolver and the verifier — the
|
|
130
|
-
* verifier's fast path reads from it when the resolver has already
|
|
131
|
-
* fetched a packument during the same install.
|
|
132
|
-
*/
|
|
133
|
-
export declare function createDefaultPackageMetaCache(): PackageMetaCache;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { FetchMetadataNotModifiedResult, FetchMetadataOptions, FetchMetadataResult } from './fetch.js';
|
|
2
|
-
export type FetchMetadata = (pkgName: string, opts: FetchMetadataOptions) => Promise<FetchMetadataResult | FetchMetadataNotModifiedResult>;
|
|
3
|
-
export interface MemoizedFetchMetadata {
|
|
4
|
-
fetch: FetchMetadata;
|
|
5
|
-
clear: () => void;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Memoizes metadata fetches for the whole resolution phase (cleared via
|
|
9
|
-
* `clear`, see `clearResolutionCache`), deduplicating concurrent and repeat
|
|
10
|
-
* requests for the same package.
|
|
11
|
-
*
|
|
12
|
-
* Unlike plain memoization, the cache holds a body-less clone of each result:
|
|
13
|
-
* `jsonText` — the raw registry response body, up to tens of MB for a popular
|
|
14
|
-
* package — reaches only the caller that initiated the fetch, which is the
|
|
15
|
-
* caller that writes the disk mirror. A phase-long cache that kept the bodies
|
|
16
|
-
* would pin hundreds of MB on large cold-cache graphs. A cache-hit caller
|
|
17
|
-
* that also writes the mirror falls back to `JSON.stringify(meta)` in
|
|
18
|
-
* `prepareJsonForDisk`, which is equivalent on read: `loadMeta` re-derives
|
|
19
|
-
* `etag` from the headers line.
|
|
20
|
-
*
|
|
21
|
-
* A rejected fetch is evicted so a transient network failure is retried by
|
|
22
|
-
* the next request instead of being cached for the rest of the phase.
|
|
23
|
-
*/
|
|
24
|
-
export declare function memoizeFetchMetadata(fetch: FetchMetadata): MemoizedFetchMetadata;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memoizes metadata fetches for the whole resolution phase (cleared via
|
|
3
|
-
* `clear`, see `clearResolutionCache`), deduplicating concurrent and repeat
|
|
4
|
-
* requests for the same package.
|
|
5
|
-
*
|
|
6
|
-
* Unlike plain memoization, the cache holds a body-less clone of each result:
|
|
7
|
-
* `jsonText` — the raw registry response body, up to tens of MB for a popular
|
|
8
|
-
* package — reaches only the caller that initiated the fetch, which is the
|
|
9
|
-
* caller that writes the disk mirror. A phase-long cache that kept the bodies
|
|
10
|
-
* would pin hundreds of MB on large cold-cache graphs. A cache-hit caller
|
|
11
|
-
* that also writes the mirror falls back to `JSON.stringify(meta)` in
|
|
12
|
-
* `prepareJsonForDisk`, which is equivalent on read: `loadMeta` re-derives
|
|
13
|
-
* `etag` from the headers line.
|
|
14
|
-
*
|
|
15
|
-
* A rejected fetch is evicted so a transient network failure is retried by
|
|
16
|
-
* the next request instead of being cached for the rest of the phase.
|
|
17
|
-
*/
|
|
18
|
-
export function memoizeFetchMetadata(fetch) {
|
|
19
|
-
const cache = new Map();
|
|
20
|
-
return {
|
|
21
|
-
fetch: (pkgName, opts) => {
|
|
22
|
-
const key = JSON.stringify([pkgName, opts]);
|
|
23
|
-
const cached = cache.get(key);
|
|
24
|
-
if (cached != null)
|
|
25
|
-
return cached;
|
|
26
|
-
const pending = fetch(pkgName, opts);
|
|
27
|
-
const bodiless = pending.then((result) => result.notModified ? result : { ...result, jsonText: undefined });
|
|
28
|
-
bodiless.catch(() => cache.delete(key));
|
|
29
|
-
cache.set(key, bodiless);
|
|
30
|
-
return pending;
|
|
31
|
-
},
|
|
32
|
-
clear: () => {
|
|
33
|
-
cache.clear();
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
//# sourceMappingURL=memoizeFetchMetadata.js.map
|