@agntn/registries 0.3.0 → 0.3.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/README.md +26 -3
- package/dist/THIRD-PARTY-LICENSES.md +36 -0
- package/dist/_chunks/cache.mjs +15 -0
- package/dist/{cache/index.mjs.map → _chunks/cache.mjs.map} +1 -1
- package/dist/{commands/cache.mjs → _chunks/cache2.mjs} +6 -8
- package/dist/_chunks/cache2.mjs.map +1 -0
- package/dist/{cache → _chunks}/cached-registry.mjs +12 -8
- package/dist/_chunks/cached-registry.mjs.map +1 -0
- package/dist/{commands → _chunks}/deps.mjs +19 -13
- package/dist/_chunks/deps.mjs.map +1 -0
- package/dist/_chunks/helpers.mjs +53 -19
- package/dist/_chunks/helpers.mjs.map +1 -1
- package/dist/_chunks/index.d.mts +9 -23
- package/dist/_chunks/index.d.mts.map +1 -1
- package/dist/_chunks/index2.d.mts +181 -0
- package/dist/_chunks/index2.d.mts.map +1 -0
- package/dist/_chunks/index3.d.mts +188 -0
- package/dist/_chunks/index3.d.mts.map +1 -0
- package/dist/_chunks/info.mjs +105 -0
- package/dist/_chunks/info.mjs.map +1 -0
- package/dist/_chunks/libs/typebox.mjs +278 -0
- package/dist/_chunks/libs/typebox.mjs.map +1 -0
- package/dist/_chunks/lockfile.mjs +183 -0
- package/dist/_chunks/lockfile.mjs.map +1 -0
- package/dist/_chunks/maintainers.mjs +53 -0
- package/dist/_chunks/maintainers.mjs.map +1 -0
- package/dist/_chunks/mcp.mjs +15 -0
- package/dist/_chunks/mcp.mjs.map +1 -0
- package/dist/_chunks/registries.mjs +514 -245
- package/dist/_chunks/registries.mjs.map +1 -1
- package/dist/_chunks/registry.d.mts +121 -10
- package/dist/_chunks/registry.d.mts.map +1 -1
- package/dist/_chunks/registry.mjs +121 -31
- package/dist/_chunks/registry.mjs.map +1 -1
- package/dist/{commands → _chunks}/shared.mjs +15 -8
- package/dist/_chunks/shared.mjs.map +1 -0
- package/dist/_chunks/tool-operations.mjs +63 -0
- package/dist/_chunks/tool-operations.mjs.map +1 -0
- package/dist/_chunks/version.mjs +4 -0
- package/dist/_chunks/version.mjs.map +1 -0
- package/dist/{commands → _chunks}/versions.mjs +10 -9
- package/dist/_chunks/versions.mjs.map +1 -0
- package/dist/ai.d.mts +4 -4
- package/dist/ai.mjs +0 -1
- package/dist/ai.mjs.map +1 -1
- package/dist/cache/index.d.mts +3 -23
- package/dist/cache/index.mjs +4 -11
- package/dist/cli.mjs +9 -7
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +4 -56
- package/dist/index.mjs +4 -7
- package/dist/mcp.d.mts +7 -0
- package/dist/mcp.d.mts.map +1 -0
- package/dist/mcp.mjs +155 -0
- package/dist/mcp.mjs.map +1 -0
- package/dist/registries/index.d.mts +0 -1
- package/dist/registries/index.mjs +0 -1
- package/dist/tool-operations.d.mts +36 -0
- package/dist/tool-operations.d.mts.map +1 -0
- package/dist/tool-operations.mjs +5 -0
- package/package.json +55 -9
- package/packages/omp/extensions/registries.ts +112 -0
- package/packages/pi/extensions/registries.ts +125 -0
- package/dist/_chunks/errors.d.mts +0 -90
- package/dist/_chunks/errors.d.mts.map +0 -1
- package/dist/_chunks/purl.mjs +0 -200
- package/dist/_chunks/purl.mjs.map +0 -1
- package/dist/_chunks/types.d.mts +0 -73
- package/dist/_chunks/types.d.mts.map +0 -1
- package/dist/cache/cached-registry.d.mts +0 -35
- package/dist/cache/cached-registry.d.mts.map +0 -1
- package/dist/cache/cached-registry.mjs.map +0 -1
- package/dist/cache/index.d.mts.map +0 -1
- package/dist/cache/lockfile.d.mts +0 -50
- package/dist/cache/lockfile.d.mts.map +0 -1
- package/dist/cache/lockfile.mjs +0 -68
- package/dist/cache/lockfile.mjs.map +0 -1
- package/dist/cache/paths.d.mts +0 -11
- package/dist/cache/paths.d.mts.map +0 -1
- package/dist/cache/paths.mjs +0 -26
- package/dist/cache/paths.mjs.map +0 -1
- package/dist/cache/storage.d.mts +0 -22
- package/dist/cache/storage.d.mts.map +0 -1
- package/dist/cache/storage.mjs +0 -40
- package/dist/cache/storage.mjs.map +0 -1
- package/dist/commands/cache.d.mts +0 -7
- package/dist/commands/cache.d.mts.map +0 -1
- package/dist/commands/cache.mjs.map +0 -1
- package/dist/commands/deps.d.mts +0 -23
- package/dist/commands/deps.d.mts.map +0 -1
- package/dist/commands/deps.mjs.map +0 -1
- package/dist/commands/info.d.mts +0 -26
- package/dist/commands/info.d.mts.map +0 -1
- package/dist/commands/info.mjs +0 -54
- package/dist/commands/info.mjs.map +0 -1
- package/dist/commands/maintainers.d.mts +0 -23
- package/dist/commands/maintainers.d.mts.map +0 -1
- package/dist/commands/maintainers.mjs +0 -46
- package/dist/commands/maintainers.mjs.map +0 -1
- package/dist/commands/shared.d.mts +0 -27
- package/dist/commands/shared.d.mts.map +0 -1
- package/dist/commands/shared.mjs.map +0 -1
- package/dist/commands/versions.d.mts +0 -31
- package/dist/commands/versions.d.mts.map +0 -1
- package/dist/commands/versions.mjs.map +0 -1
- package/dist/core/index.d.mts +0 -4
- package/dist/core/index.mjs +0 -3
- package/dist/index.d.mts.map +0 -1
- package/dist/types.d.mts +0 -3
- package/dist/types.mjs +0 -1
|
@@ -1,5 +1,239 @@
|
|
|
1
|
-
import { a as register, c as HTTPError, l as InvalidPURLError, t as Registry, u as NotFoundError } from "./registry.mjs";
|
|
2
|
-
|
|
1
|
+
import { a as register, c as HTTPError, l as InvalidPURLError, n as create, t as Registry, u as NotFoundError } from "./registry.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* Normalize common license names to SPDX expressions.
|
|
4
|
+
*
|
|
5
|
+
* @param raw - License name or SPDX expression.
|
|
6
|
+
* @returns {string} The normalized SPDX expression, or an empty string.
|
|
7
|
+
*/
|
|
8
|
+
function normalizeLicense(raw) {
|
|
9
|
+
if (!raw) return "";
|
|
10
|
+
const trimmed = raw.trim();
|
|
11
|
+
if (!trimmed) return "";
|
|
12
|
+
const lower = trimmed.toLowerCase();
|
|
13
|
+
const known = LICENSE_MAP.get(lower);
|
|
14
|
+
if (known) return known;
|
|
15
|
+
return trimmed;
|
|
16
|
+
}
|
|
17
|
+
const LICENSE_MAP = new Map([
|
|
18
|
+
["mit license", "MIT"],
|
|
19
|
+
["mit", "MIT"],
|
|
20
|
+
["apache license 2.0", "Apache-2.0"],
|
|
21
|
+
["apache 2.0", "Apache-2.0"],
|
|
22
|
+
["apache-2.0", "Apache-2.0"],
|
|
23
|
+
["apache license, version 2.0", "Apache-2.0"],
|
|
24
|
+
["apache2", "Apache-2.0"],
|
|
25
|
+
["bsd-2-clause", "BSD-2-Clause"],
|
|
26
|
+
["bsd-3-clause", "BSD-3-Clause"],
|
|
27
|
+
["bsd 2-clause", "BSD-2-Clause"],
|
|
28
|
+
["bsd 3-clause", "BSD-3-Clause"],
|
|
29
|
+
["simplified bsd license", "BSD-2-Clause"],
|
|
30
|
+
["new bsd license", "BSD-3-Clause"],
|
|
31
|
+
["isc license", "ISC"],
|
|
32
|
+
["isc", "ISC"],
|
|
33
|
+
["gpl-2.0", "GPL-2.0-only"],
|
|
34
|
+
["gpl-3.0", "GPL-3.0-only"],
|
|
35
|
+
["gpl-2.0-only", "GPL-2.0-only"],
|
|
36
|
+
["gpl-3.0-only", "GPL-3.0-only"],
|
|
37
|
+
["lgpl-2.1", "LGPL-2.1-only"],
|
|
38
|
+
["lgpl-3.0", "LGPL-3.0-only"],
|
|
39
|
+
["mpl-2.0", "MPL-2.0"],
|
|
40
|
+
["mozilla public license 2.0", "MPL-2.0"],
|
|
41
|
+
["unlicense", "Unlicense"],
|
|
42
|
+
["the unlicense", "Unlicense"],
|
|
43
|
+
["cc0-1.0", "CC0-1.0"],
|
|
44
|
+
["cc0", "CC0-1.0"],
|
|
45
|
+
["public domain", "Unlicense"],
|
|
46
|
+
["zlib", "Zlib"],
|
|
47
|
+
["wtfpl", "WTFPL"],
|
|
48
|
+
["artistic-2.0", "Artistic-2.0"],
|
|
49
|
+
["boost software license 1.0", "BSL-1.0"],
|
|
50
|
+
["bsl-1.0", "BSL-1.0"]
|
|
51
|
+
]);
|
|
52
|
+
/**
|
|
53
|
+
* Combine license strings into one SPDX expression.
|
|
54
|
+
*
|
|
55
|
+
* @param licenses - License names or expressions to normalize.
|
|
56
|
+
* @param operator - SPDX operator joining multiple licenses.
|
|
57
|
+
* @returns {string} The combined SPDX expression, or an empty string.
|
|
58
|
+
*/
|
|
59
|
+
function combineLicenses(licenses, operator = "OR") {
|
|
60
|
+
const normalized = licenses.map((l) => normalizeLicense(l)).filter(Boolean);
|
|
61
|
+
if (normalized.length === 0) return "";
|
|
62
|
+
if (normalized.length === 1) return normalized[0];
|
|
63
|
+
return normalized.join(` ${operator} `);
|
|
64
|
+
}
|
|
65
|
+
const REPOSITORY_SHORTHANDS = [
|
|
66
|
+
["github:", "https://github.com/"],
|
|
67
|
+
["gitlab:", "https://gitlab.com/"],
|
|
68
|
+
["bitbucket:", "https://bitbucket.org/"]
|
|
69
|
+
];
|
|
70
|
+
const GIT_TRANSPORTS = [["git://", "https://"], ["ssh://git@", "https://"]];
|
|
71
|
+
function extractRepositoryURL(raw) {
|
|
72
|
+
if (typeof raw === "string") return raw;
|
|
73
|
+
if (typeof raw !== "object" || raw === null) return "";
|
|
74
|
+
const url = raw["url"];
|
|
75
|
+
return typeof url === "string" ? url : "";
|
|
76
|
+
}
|
|
77
|
+
function replacePrefix(value, replacements) {
|
|
78
|
+
for (const [prefix, replacement] of replacements) if (value.startsWith(prefix)) return replacement + value.slice(prefix.length);
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Normalize repository shorthands and Git transports to HTTPS.
|
|
83
|
+
*
|
|
84
|
+
* @param raw - Repository URL string or object with a URL field.
|
|
85
|
+
* @returns {string} The normalized URL, or an empty string.
|
|
86
|
+
*/
|
|
87
|
+
function normalizeRepositoryURL(raw) {
|
|
88
|
+
let url = extractRepositoryURL(raw).trim();
|
|
89
|
+
if (!url) return "";
|
|
90
|
+
url = replacePrefix(url, REPOSITORY_SHORTHANDS);
|
|
91
|
+
if (url.startsWith("git+")) url = url.slice(4);
|
|
92
|
+
url = replacePrefix(url, GIT_TRANSPORTS);
|
|
93
|
+
const sshMatch = url.match(/^git@([^:]+):(.+)$/);
|
|
94
|
+
if (sshMatch) url = `https://${sshMatch[1]}/${sshMatch[2]}`;
|
|
95
|
+
return url.replace(/\/$/, "").replace(/\.git$/, "");
|
|
96
|
+
}
|
|
97
|
+
const QUALIFIER_KEY_PATTERN = /^[a-z][a-z0-9._-]*$/;
|
|
98
|
+
function decodePURLComponent(purlStr, value) {
|
|
99
|
+
try {
|
|
100
|
+
return decodeURIComponent(value);
|
|
101
|
+
} catch {
|
|
102
|
+
throw new InvalidPURLError(purlStr, "malformed percent-encoding");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function extractSubpath(purlStr, value) {
|
|
106
|
+
const hashIdx = value.indexOf("#");
|
|
107
|
+
if (hashIdx === -1) return {
|
|
108
|
+
remainder: value,
|
|
109
|
+
subpath: ""
|
|
110
|
+
};
|
|
111
|
+
const subpath = value.slice(hashIdx + 1).split("/").map((segment) => decodePURLComponent(purlStr, segment)).join("/");
|
|
112
|
+
return {
|
|
113
|
+
remainder: value.slice(0, hashIdx),
|
|
114
|
+
subpath
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function extractQualifiers(purlStr, value) {
|
|
118
|
+
const queryIdx = value.indexOf("?");
|
|
119
|
+
if (queryIdx === -1) return {
|
|
120
|
+
qualifiers: {},
|
|
121
|
+
remainder: value
|
|
122
|
+
};
|
|
123
|
+
const qualifiers = {};
|
|
124
|
+
for (const pair of value.slice(queryIdx + 1).split("&")) {
|
|
125
|
+
const eqIdx = pair.indexOf("=");
|
|
126
|
+
if (eqIdx !== -1) qualifiers[decodePURLComponent(purlStr, pair.slice(0, eqIdx))] = decodePURLComponent(purlStr, pair.slice(eqIdx + 1));
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
qualifiers,
|
|
130
|
+
remainder: value.slice(0, queryIdx)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function extractVersion(purlStr, value) {
|
|
134
|
+
const lastSlashIdx = value.lastIndexOf("/");
|
|
135
|
+
const lastAtIdx = value.lastIndexOf("@");
|
|
136
|
+
if (lastAtIdx === -1 || lastAtIdx <= lastSlashIdx) return {
|
|
137
|
+
remainder: value,
|
|
138
|
+
version: ""
|
|
139
|
+
};
|
|
140
|
+
return {
|
|
141
|
+
remainder: value.slice(0, lastAtIdx),
|
|
142
|
+
version: decodePURLComponent(purlStr, value.slice(lastAtIdx + 1))
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Parse a package URL into normalized components.
|
|
147
|
+
*
|
|
148
|
+
* @param purlStr - Package URL to parse.
|
|
149
|
+
* @returns {ParsedPURL} The normalized components.
|
|
150
|
+
* @see https://github.com/package-url/purl-spec (ECMA-427)
|
|
151
|
+
*/
|
|
152
|
+
function parsePURL(purlStr) {
|
|
153
|
+
if (!purlStr.startsWith("pkg:")) throw new InvalidPURLError(purlStr, "must start with \"pkg:\"");
|
|
154
|
+
const subpathParts = extractSubpath(purlStr, purlStr.slice(4));
|
|
155
|
+
const qualifierParts = extractQualifiers(purlStr, subpathParts.remainder);
|
|
156
|
+
const versionParts = extractVersion(purlStr, qualifierParts.remainder);
|
|
157
|
+
const remainder = versionParts.remainder;
|
|
158
|
+
const slashIdx = remainder.indexOf("/");
|
|
159
|
+
if (slashIdx === -1) throw new InvalidPURLError(purlStr, "missing type/name separator");
|
|
160
|
+
const type = remainder.slice(0, slashIdx).toLowerCase();
|
|
161
|
+
if (!type) throw new InvalidPURLError(purlStr, "empty type");
|
|
162
|
+
const rest = remainder.slice(slashIdx + 1);
|
|
163
|
+
if (!rest) throw new InvalidPURLError(purlStr, "empty name");
|
|
164
|
+
const lastSlashIdx = rest.lastIndexOf("/");
|
|
165
|
+
let namespace = "";
|
|
166
|
+
let name;
|
|
167
|
+
if (lastSlashIdx !== -1) {
|
|
168
|
+
namespace = rest.slice(0, lastSlashIdx).split("/").map((s) => decodePURLComponent(purlStr, s)).join("/");
|
|
169
|
+
name = decodePURLComponent(purlStr, rest.slice(lastSlashIdx + 1));
|
|
170
|
+
} else name = decodePURLComponent(purlStr, rest);
|
|
171
|
+
if (type === "pypi") name = name.toLowerCase().replaceAll(/[-_.]+/g, "-");
|
|
172
|
+
if (type === "alpm") name = name.toLowerCase();
|
|
173
|
+
return {
|
|
174
|
+
type,
|
|
175
|
+
namespace,
|
|
176
|
+
name,
|
|
177
|
+
version: versionParts.version,
|
|
178
|
+
qualifiers: qualifierParts.qualifiers,
|
|
179
|
+
subpath: subpathParts.subpath
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Build a registry package name from PURL components.
|
|
184
|
+
*
|
|
185
|
+
* @param parsed - Parsed package URL components.
|
|
186
|
+
* @returns {string} The namespace-qualified package name.
|
|
187
|
+
*/
|
|
188
|
+
function fullName(parsed) {
|
|
189
|
+
if (parsed.namespace) return `${parsed.namespace}/${parsed.name}`;
|
|
190
|
+
return parsed.name;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Create a registry from a package URL.
|
|
194
|
+
*
|
|
195
|
+
* @param purlStr - Package URL to resolve.
|
|
196
|
+
* @param client - Optional HTTP client.
|
|
197
|
+
* @returns {[Registry, string, string]} The registry, package name, and version.
|
|
198
|
+
*/
|
|
199
|
+
function createFromPURL(purlStr, client) {
|
|
200
|
+
const parsed = parsePURL(purlStr);
|
|
201
|
+
const baseURL = parsed.qualifiers["repository_url"] ?? "";
|
|
202
|
+
return [
|
|
203
|
+
create(parsed.type, baseURL || void 0, client),
|
|
204
|
+
fullName(parsed),
|
|
205
|
+
parsed.version
|
|
206
|
+
];
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Build a package URL from normalized components.
|
|
210
|
+
*
|
|
211
|
+
* @param parts - Package URL components.
|
|
212
|
+
* @returns {string} The canonical package URL.
|
|
213
|
+
*/
|
|
214
|
+
function buildPURL(parts) {
|
|
215
|
+
let purl = `pkg:${parts.type}/`;
|
|
216
|
+
if (parts.namespace) purl += `${parts.namespace.split("/").map((s) => encodeURIComponent(s)).join("/")}/`;
|
|
217
|
+
purl += encodeURIComponent(parts.name);
|
|
218
|
+
if (parts.version) purl += `@${encodeURIComponent(parts.version)}`;
|
|
219
|
+
if (parts.qualifiers && Object.keys(parts.qualifiers).length > 0) {
|
|
220
|
+
const qualifiers = /* @__PURE__ */ new Map();
|
|
221
|
+
for (const [key, value] of Object.entries(parts.qualifiers)) {
|
|
222
|
+
const canonicalKey = key.toLowerCase();
|
|
223
|
+
if (!QUALIFIER_KEY_PATTERN.test(canonicalKey)) throw new InvalidPURLError(purl, `invalid qualifier key "${key}"`);
|
|
224
|
+
if (qualifiers.has(canonicalKey)) throw new InvalidPURLError(purl, `duplicate qualifier key "${canonicalKey}"`);
|
|
225
|
+
qualifiers.set(canonicalKey, value);
|
|
226
|
+
}
|
|
227
|
+
const qs = [...qualifiers].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&");
|
|
228
|
+
purl += `?${qs}`;
|
|
229
|
+
}
|
|
230
|
+
if (parts.subpath) purl += `#${parts.subpath.split("/").map((s) => encodeURIComponent(s)).join("/")}`;
|
|
231
|
+
return purl;
|
|
232
|
+
}
|
|
233
|
+
function rethrowFetchError(error, ecosystem, packageName, version = "") {
|
|
234
|
+
if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError(ecosystem, packageName, version);
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
3
237
|
/** npm registry client. */
|
|
4
238
|
var NpmRegistry = class extends Registry {
|
|
5
239
|
constructor(baseURL, client) {
|
|
@@ -19,7 +253,7 @@ var NpmRegistry = class extends Registry {
|
|
|
19
253
|
const data = await this.client.getJSON(url, signal);
|
|
20
254
|
const latestVersion = data["dist-tags"].latest;
|
|
21
255
|
const latestVersionData = data.versions[latestVersion];
|
|
22
|
-
const licenses = this.extractLicense(data.license
|
|
256
|
+
const licenses = data.license ? this.extractLicense(data.license) : this.extractLicense(latestVersionData?.license);
|
|
23
257
|
const namespace = this.extractNamespace(name);
|
|
24
258
|
return {
|
|
25
259
|
name: data.name,
|
|
@@ -28,14 +262,13 @@ var NpmRegistry = class extends Registry {
|
|
|
28
262
|
documentation: "",
|
|
29
263
|
repository: normalizeRepositoryURL(data.repository || ""),
|
|
30
264
|
licenses,
|
|
31
|
-
keywords: data.keywords
|
|
265
|
+
keywords: data.keywords ?? [],
|
|
32
266
|
namespace,
|
|
33
267
|
latestVersion,
|
|
34
268
|
metadata: {}
|
|
35
269
|
};
|
|
36
270
|
} catch (error) {
|
|
37
|
-
|
|
38
|
-
throw error;
|
|
271
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
39
272
|
}
|
|
40
273
|
}
|
|
41
274
|
async fetchVersions(name, signal) {
|
|
@@ -43,25 +276,21 @@ var NpmRegistry = class extends Registry {
|
|
|
43
276
|
const url = `${this.baseURL}/${encodedName}`;
|
|
44
277
|
try {
|
|
45
278
|
const data = await this.client.getJSON(url, signal);
|
|
46
|
-
|
|
47
|
-
for (const [versionStr, versionData] of Object.entries(data.versions)) {
|
|
279
|
+
return Object.entries(data.versions).map(([versionStr, versionData]) => {
|
|
48
280
|
const licenses = this.extractLicense(versionData.license);
|
|
49
281
|
const publishedAt = data.time?.[versionStr] ? new Date(data.time[versionStr]) : null;
|
|
50
282
|
const status = versionData.deprecated ? "deprecated" : "";
|
|
51
|
-
|
|
52
|
-
versions.push({
|
|
283
|
+
return {
|
|
53
284
|
number: versionStr,
|
|
54
285
|
publishedAt,
|
|
55
286
|
licenses,
|
|
56
|
-
integrity,
|
|
287
|
+
integrity: versionData.dist?.integrity ? versionData.dist.integrity : versionData.dist?.shasum ? `sha1-${versionData.dist.shasum}` : "",
|
|
57
288
|
status,
|
|
58
289
|
metadata: {}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return versions;
|
|
290
|
+
};
|
|
291
|
+
});
|
|
62
292
|
} catch (error) {
|
|
63
|
-
|
|
64
|
-
throw error;
|
|
293
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
65
294
|
}
|
|
66
295
|
}
|
|
67
296
|
async fetchDependencies(name, version, signal) {
|
|
@@ -69,38 +298,15 @@ var NpmRegistry = class extends Registry {
|
|
|
69
298
|
const url = `${this.baseURL}/${encodedName}/${version}`;
|
|
70
299
|
try {
|
|
71
300
|
const versionData = await this.client.getJSON(url, signal);
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
optional: false
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
if (versionData.devDependencies) for (const [depName, requirements] of Object.entries(versionData.devDependencies)) dependencies.push({
|
|
83
|
-
name: depName,
|
|
84
|
-
requirements,
|
|
85
|
-
scope: "development",
|
|
86
|
-
optional: false
|
|
87
|
-
});
|
|
88
|
-
if (versionData.optionalDependencies) for (const [depName, requirements] of Object.entries(versionData.optionalDependencies)) dependencies.push({
|
|
89
|
-
name: depName,
|
|
90
|
-
requirements,
|
|
91
|
-
scope: "runtime",
|
|
92
|
-
optional: true
|
|
93
|
-
});
|
|
94
|
-
if (versionData.peerDependencies) for (const [depName, requirements] of Object.entries(versionData.peerDependencies)) dependencies.push({
|
|
95
|
-
name: depName,
|
|
96
|
-
requirements,
|
|
97
|
-
scope: "runtime",
|
|
98
|
-
optional: false
|
|
99
|
-
});
|
|
100
|
-
return dependencies;
|
|
301
|
+
const optionalNames = Object.keys(versionData.optionalDependencies ?? {});
|
|
302
|
+
return [
|
|
303
|
+
...this.mapDependencies(versionData.dependencies, "runtime", false, optionalNames),
|
|
304
|
+
...this.mapDependencies(versionData.devDependencies, "development", false),
|
|
305
|
+
...this.mapDependencies(versionData.optionalDependencies, "runtime", true),
|
|
306
|
+
...this.mapDependencies(versionData.peerDependencies, "runtime", false)
|
|
307
|
+
];
|
|
101
308
|
} catch (error) {
|
|
102
|
-
|
|
103
|
-
throw error;
|
|
309
|
+
rethrowFetchError(error, this.ecosystem(), name, version);
|
|
104
310
|
}
|
|
105
311
|
}
|
|
106
312
|
async fetchMaintainers(name, signal) {
|
|
@@ -108,55 +314,10 @@ var NpmRegistry = class extends Registry {
|
|
|
108
314
|
const url = `${this.baseURL}/${encodedName}`;
|
|
109
315
|
try {
|
|
110
316
|
const data = await this.client.getJSON(url, signal);
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
if (data.maintainers) for (const maintainer of data.maintainers) {
|
|
114
|
-
const key = this.maintainerKey(maintainer.name, maintainer.email);
|
|
115
|
-
if (!key || seen.has(key)) continue;
|
|
116
|
-
seen.add(key);
|
|
117
|
-
maintainers.push({
|
|
118
|
-
uuid: "",
|
|
119
|
-
login: maintainer.email ? maintainer.email.split("@")[0] : "",
|
|
120
|
-
name: maintainer.name || "",
|
|
121
|
-
email: maintainer.email || "",
|
|
122
|
-
url: maintainer.url || "",
|
|
123
|
-
role: ""
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
const latestTag = data["dist-tags"].latest;
|
|
127
|
-
const latestVersion = data.versions[latestTag];
|
|
128
|
-
if (latestVersion?.author) {
|
|
129
|
-
const key = this.maintainerKey(latestVersion.author.name, latestVersion.author.email);
|
|
130
|
-
if (key && !seen.has(key)) {
|
|
131
|
-
seen.add(key);
|
|
132
|
-
maintainers.push({
|
|
133
|
-
uuid: "",
|
|
134
|
-
login: latestVersion.author.email ? latestVersion.author.email.split("@")[0] : "",
|
|
135
|
-
name: latestVersion.author.name || "",
|
|
136
|
-
email: latestVersion.author.email || "",
|
|
137
|
-
url: latestVersion.author.url || "",
|
|
138
|
-
role: "author"
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (latestVersion?.contributors) for (const contributor of latestVersion.contributors) {
|
|
143
|
-
const key = this.maintainerKey(contributor.name, contributor.email);
|
|
144
|
-
if (key && !seen.has(key)) {
|
|
145
|
-
seen.add(key);
|
|
146
|
-
maintainers.push({
|
|
147
|
-
uuid: "",
|
|
148
|
-
login: contributor.email ? contributor.email.split("@")[0] : "",
|
|
149
|
-
name: contributor.name || "",
|
|
150
|
-
email: contributor.email || "",
|
|
151
|
-
url: contributor.url || "",
|
|
152
|
-
role: "contributor"
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return maintainers;
|
|
317
|
+
const latestVersion = data.versions[data["dist-tags"].latest];
|
|
318
|
+
return this.collectMaintainers(data.maintainers, latestVersion?.author, latestVersion?.contributors);
|
|
157
319
|
} catch (error) {
|
|
158
|
-
|
|
159
|
-
throw error;
|
|
320
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
160
321
|
}
|
|
161
322
|
}
|
|
162
323
|
urls() {
|
|
@@ -185,23 +346,58 @@ var NpmRegistry = class extends Registry {
|
|
|
185
346
|
}
|
|
186
347
|
};
|
|
187
348
|
}
|
|
188
|
-
|
|
349
|
+
mapDependencies(entries, scope, optional, excluded = []) {
|
|
350
|
+
const dependencies = [];
|
|
351
|
+
for (const [name, requirements] of Object.entries(entries ?? {})) {
|
|
352
|
+
if (excluded.includes(name)) continue;
|
|
353
|
+
dependencies.push({
|
|
354
|
+
name,
|
|
355
|
+
requirements,
|
|
356
|
+
scope,
|
|
357
|
+
optional
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
return dependencies;
|
|
361
|
+
}
|
|
362
|
+
collectMaintainers(maintainers, author, contributors) {
|
|
363
|
+
const candidates = [
|
|
364
|
+
...(maintainers ?? []).map((maintainer) => [maintainer, ""]),
|
|
365
|
+
...author ? [[author, "author"]] : [],
|
|
366
|
+
...(contributors ?? []).map((contributor) => [contributor, "contributor"])
|
|
367
|
+
];
|
|
368
|
+
const seen = /* @__PURE__ */ new Set();
|
|
369
|
+
const result = [];
|
|
370
|
+
for (const [maintainer, role] of candidates) {
|
|
371
|
+
const key = this.maintainerKey(maintainer.name, maintainer.email);
|
|
372
|
+
if (!key || seen.has(key)) continue;
|
|
373
|
+
seen.add(key);
|
|
374
|
+
result.push(this.toMaintainer(maintainer, role));
|
|
375
|
+
}
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
toMaintainer(maintainer, role) {
|
|
379
|
+
return {
|
|
380
|
+
uuid: "",
|
|
381
|
+
login: maintainer.email ? maintainer.email.split("@")[0] : "",
|
|
382
|
+
name: maintainer.name || "",
|
|
383
|
+
email: maintainer.email || "",
|
|
384
|
+
url: maintainer.url || "",
|
|
385
|
+
role
|
|
386
|
+
};
|
|
387
|
+
}
|
|
189
388
|
maintainerKey(name, email) {
|
|
190
389
|
if (email) return email;
|
|
191
390
|
if (name) return name;
|
|
192
391
|
return "";
|
|
193
392
|
}
|
|
194
|
-
/** Encode package name for URL (handle scoped packages). */
|
|
195
393
|
encodeName(name) {
|
|
196
394
|
if (name.startsWith("@")) return name.replace("/", "%2F");
|
|
197
395
|
return name;
|
|
198
396
|
}
|
|
199
|
-
/** Extract namespace from scoped package name. */
|
|
200
397
|
extractNamespace(name) {
|
|
201
398
|
if (name.startsWith("@")) return name.split("/")[0] || "";
|
|
202
399
|
return "";
|
|
203
400
|
}
|
|
204
|
-
/** Extract and normalize license. */
|
|
205
401
|
extractLicense(raw) {
|
|
206
402
|
if (!raw) return "";
|
|
207
403
|
if (typeof raw === "string") return normalizeLicense(raw);
|
|
@@ -213,6 +409,9 @@ var NpmRegistry = class extends Registry {
|
|
|
213
409
|
}
|
|
214
410
|
};
|
|
215
411
|
register("npm", "https://registry.npmjs.org", NpmRegistry);
|
|
412
|
+
function preferNonEmpty(value, fallback) {
|
|
413
|
+
return value || fallback;
|
|
414
|
+
}
|
|
216
415
|
/** Crates.io registry client. */
|
|
217
416
|
var CargoRegistry = class extends Registry {
|
|
218
417
|
constructor(baseURL, client) {
|
|
@@ -230,7 +429,7 @@ var CargoRegistry = class extends Registry {
|
|
|
230
429
|
try {
|
|
231
430
|
const data = await this.client.getJSON(url, signal);
|
|
232
431
|
const crateData = data.crate;
|
|
233
|
-
const latestVersion = crateData.max_stable_version
|
|
432
|
+
const latestVersion = preferNonEmpty(crateData.max_stable_version, crateData.max_version);
|
|
234
433
|
const latestVersionData = data.versions.find((v) => v.num === latestVersion);
|
|
235
434
|
return {
|
|
236
435
|
name: crateData.name,
|
|
@@ -253,8 +452,7 @@ var CargoRegistry = class extends Registry {
|
|
|
253
452
|
}
|
|
254
453
|
};
|
|
255
454
|
} catch (error) {
|
|
256
|
-
|
|
257
|
-
throw error;
|
|
455
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
258
456
|
}
|
|
259
457
|
}
|
|
260
458
|
async fetchVersions(name, signal) {
|
|
@@ -280,8 +478,7 @@ var CargoRegistry = class extends Registry {
|
|
|
280
478
|
}
|
|
281
479
|
return versions;
|
|
282
480
|
} catch (error) {
|
|
283
|
-
|
|
284
|
-
throw error;
|
|
481
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
285
482
|
}
|
|
286
483
|
}
|
|
287
484
|
async fetchDependencies(name, version, signal) {
|
|
@@ -303,8 +500,7 @@ var CargoRegistry = class extends Registry {
|
|
|
303
500
|
}
|
|
304
501
|
return dependencies;
|
|
305
502
|
} catch (error) {
|
|
306
|
-
|
|
307
|
-
throw error;
|
|
503
|
+
rethrowFetchError(error, this.ecosystem(), name, version);
|
|
308
504
|
}
|
|
309
505
|
}
|
|
310
506
|
async fetchMaintainers(name, signal) {
|
|
@@ -322,8 +518,7 @@ var CargoRegistry = class extends Registry {
|
|
|
322
518
|
});
|
|
323
519
|
return maintainers;
|
|
324
520
|
} catch (error) {
|
|
325
|
-
|
|
326
|
-
throw error;
|
|
521
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
327
522
|
}
|
|
328
523
|
}
|
|
329
524
|
urls() {
|
|
@@ -357,6 +552,80 @@ const PYPI_FILENAME_SEPARATORS = [
|
|
|
357
552
|
"-",
|
|
358
553
|
"."
|
|
359
554
|
];
|
|
555
|
+
/**
|
|
556
|
+
* PEP 639 projects put the SPDX expression in `license_expression` and leave `license` null.
|
|
557
|
+
*
|
|
558
|
+
* @param info - License fields of the JSON API `info` object.
|
|
559
|
+
* @returns {string} The normalized SPDX expression, or an empty string.
|
|
560
|
+
*/
|
|
561
|
+
function pypiLicense(info) {
|
|
562
|
+
return normalizeLicense(info.license_expression || info.license);
|
|
563
|
+
}
|
|
564
|
+
const QUOTED_NAME_PATTERN = /^"((?:[^"\\]|\\.)*)"\s*<([^>]*)>$/;
|
|
565
|
+
const ANGLE_ADDRESS_PATTERN = /^([^<]*?)\s*<([^>]*)>$/;
|
|
566
|
+
const COMMENT_PATTERN = /\(([^)]*)\)/g;
|
|
567
|
+
const ADDRESS_ITEM_PATTERN = /(?:"(?:[^"\\]|\\.)*"|<[^>]*>|\([^)]*\)|[^,])+/g;
|
|
568
|
+
/**
|
|
569
|
+
* Split an address list on the commas outside quotes, angle brackets and one level of comments.
|
|
570
|
+
*
|
|
571
|
+
* @param value - Comma separated mailboxes.
|
|
572
|
+
* @returns {string[]} The trimmed, non-empty items.
|
|
573
|
+
*/
|
|
574
|
+
function splitAddressList(value) {
|
|
575
|
+
return [...value.matchAll(ADDRESS_ITEM_PATTERN)].map(([item]) => item.trim()).filter(Boolean);
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Read one mailbox; a comment names a bare address, and a bare name has no address.
|
|
579
|
+
*
|
|
580
|
+
* @param item - One item of an address list.
|
|
581
|
+
* @returns {PyPIContact} The name and email found in it.
|
|
582
|
+
*/
|
|
583
|
+
function parseMailbox(item) {
|
|
584
|
+
const quoted = item.match(QUOTED_NAME_PATTERN);
|
|
585
|
+
if (quoted) return {
|
|
586
|
+
name: quoted[1].replaceAll(/\\(.)/g, "$1").trim(),
|
|
587
|
+
email: quoted[2].trim()
|
|
588
|
+
};
|
|
589
|
+
const comments = [];
|
|
590
|
+
const bare = item.replaceAll(COMMENT_PATTERN, (_match, comment) => {
|
|
591
|
+
comments.push(comment.trim());
|
|
592
|
+
return " ";
|
|
593
|
+
}).trim();
|
|
594
|
+
const comment = comments.filter(Boolean).join(" ");
|
|
595
|
+
const angled = bare.match(ANGLE_ADDRESS_PATTERN);
|
|
596
|
+
if (angled) return {
|
|
597
|
+
name: angled[1].trim() || comment,
|
|
598
|
+
email: angled[2].trim()
|
|
599
|
+
};
|
|
600
|
+
return bare.includes("@") ? {
|
|
601
|
+
name: comment,
|
|
602
|
+
email: bare
|
|
603
|
+
} : {
|
|
604
|
+
name: bare || comment,
|
|
605
|
+
email: ""
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* PEP 621 keeps people without an email in the name field, so the label stays its own entry.
|
|
610
|
+
*
|
|
611
|
+
* @param name - The `author` or `maintainer` field.
|
|
612
|
+
* @param emails - The matching `author_email` or `maintainer_email` field.
|
|
613
|
+
* @returns {PyPIContact[]} One contact per person.
|
|
614
|
+
*/
|
|
615
|
+
function pypiContacts(name, emails) {
|
|
616
|
+
const label = name?.trim() ?? "";
|
|
617
|
+
const contacts = splitAddressList(emails ?? "").map(parseMailbox);
|
|
618
|
+
if (!label) return contacts;
|
|
619
|
+
if (contacts.length === 1 && !contacts[0].name) return [{
|
|
620
|
+
name: label,
|
|
621
|
+
email: contacts[0].email
|
|
622
|
+
}];
|
|
623
|
+
if (contacts.some((contact) => contact.name.toLowerCase() === label.toLowerCase())) return contacts;
|
|
624
|
+
return [{
|
|
625
|
+
name: label,
|
|
626
|
+
email: ""
|
|
627
|
+
}, ...contacts];
|
|
628
|
+
}
|
|
360
629
|
/** PyPI registry client. */
|
|
361
630
|
var PyPIRegistry = class extends Registry {
|
|
362
631
|
constructor(baseURL, client) {
|
|
@@ -375,7 +644,7 @@ var PyPIRegistry = class extends Registry {
|
|
|
375
644
|
const url = `${this.baseURL}/pypi/${normalized}/json`;
|
|
376
645
|
try {
|
|
377
646
|
const info = (await this.client.getJSON(url, signal)).info;
|
|
378
|
-
const licenses =
|
|
647
|
+
const licenses = pypiLicense(info);
|
|
379
648
|
const repository = this.extractRepository(info.project_urls);
|
|
380
649
|
const keywords = this.parseKeywords(info.keywords);
|
|
381
650
|
return {
|
|
@@ -391,8 +660,7 @@ var PyPIRegistry = class extends Registry {
|
|
|
391
660
|
metadata: {}
|
|
392
661
|
};
|
|
393
662
|
} catch (error) {
|
|
394
|
-
|
|
395
|
-
throw error;
|
|
663
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
396
664
|
}
|
|
397
665
|
}
|
|
398
666
|
async fetchVersions(name, signal) {
|
|
@@ -400,43 +668,9 @@ var PyPIRegistry = class extends Registry {
|
|
|
400
668
|
const url = `${this.baseURL}/simple/${normalized}/`;
|
|
401
669
|
try {
|
|
402
670
|
const data = await this.client.getJSON(url, signal, { Accept: "application/vnd.pypi.simple.v1+json" });
|
|
403
|
-
|
|
404
|
-
for (const version of data.versions) filesByVersion.set(version, []);
|
|
405
|
-
for (const file of data.files) {
|
|
406
|
-
const version = this.matchFileVersion(file.filename, normalized, data.versions);
|
|
407
|
-
if (version) filesByVersion.get(version).push(file);
|
|
408
|
-
}
|
|
409
|
-
const versions = [];
|
|
410
|
-
for (const [versionStr, files] of filesByVersion) {
|
|
411
|
-
if (files.length === 0) {
|
|
412
|
-
versions.push({
|
|
413
|
-
number: versionStr,
|
|
414
|
-
publishedAt: null,
|
|
415
|
-
licenses: "",
|
|
416
|
-
integrity: "",
|
|
417
|
-
status: "",
|
|
418
|
-
metadata: {}
|
|
419
|
-
});
|
|
420
|
-
continue;
|
|
421
|
-
}
|
|
422
|
-
const file = files.find((f) => f.filename.endsWith(".tar.gz")) ?? files[0];
|
|
423
|
-
const publishedAt = file["upload-time"] ? new Date(file["upload-time"]) : null;
|
|
424
|
-
const integrity = file.hashes?.sha256 ? `sha256-${file.hashes.sha256}` : "";
|
|
425
|
-
const status = file.yanked ? "yanked" : "";
|
|
426
|
-
this.downloadUrls.set(`${normalized}@${versionStr}`, file.url);
|
|
427
|
-
versions.push({
|
|
428
|
-
number: versionStr,
|
|
429
|
-
publishedAt,
|
|
430
|
-
licenses: "",
|
|
431
|
-
integrity,
|
|
432
|
-
status,
|
|
433
|
-
metadata: {}
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
return versions;
|
|
671
|
+
return [...this.indexFilesByVersion(normalized, data.versions, data.files)].map(([version, files]) => this.toVersion(normalized, version, files));
|
|
437
672
|
} catch (error) {
|
|
438
|
-
|
|
439
|
-
throw error;
|
|
673
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
440
674
|
}
|
|
441
675
|
}
|
|
442
676
|
async fetchDependencies(name, version, signal) {
|
|
@@ -451,28 +685,33 @@ var PyPIRegistry = class extends Registry {
|
|
|
451
685
|
}
|
|
452
686
|
return dependencies;
|
|
453
687
|
} catch (error) {
|
|
454
|
-
|
|
455
|
-
throw error;
|
|
688
|
+
rethrowFetchError(error, this.ecosystem(), name, version);
|
|
456
689
|
}
|
|
457
690
|
}
|
|
458
691
|
async fetchMaintainers(name, signal) {
|
|
459
692
|
const normalized = this.normalizeName(name);
|
|
460
693
|
const url = `${this.baseURL}/pypi/${normalized}/json`;
|
|
461
694
|
try {
|
|
462
|
-
const
|
|
695
|
+
const { info } = await this.client.getJSON(url, signal);
|
|
696
|
+
const groups = [["author", pypiContacts(info.author, info.author_email)], ["maintainer", pypiContacts(info.maintainer, info.maintainer_email)]];
|
|
697
|
+
const seen = /* @__PURE__ */ new Set();
|
|
463
698
|
const maintainers = [];
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
699
|
+
for (const [role, contacts] of groups) for (const contact of contacts) {
|
|
700
|
+
const key = (contact.email || contact.name).toLowerCase();
|
|
701
|
+
if (seen.has(key)) continue;
|
|
702
|
+
seen.add(key);
|
|
703
|
+
maintainers.push({
|
|
704
|
+
uuid: "",
|
|
705
|
+
login: contact.email ? contact.email.split("@")[0] : "",
|
|
706
|
+
name: contact.name,
|
|
707
|
+
email: contact.email,
|
|
708
|
+
url: "",
|
|
709
|
+
role
|
|
710
|
+
});
|
|
711
|
+
}
|
|
472
712
|
return maintainers;
|
|
473
713
|
} catch (error) {
|
|
474
|
-
|
|
475
|
-
throw error;
|
|
714
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
476
715
|
}
|
|
477
716
|
}
|
|
478
717
|
urls() {
|
|
@@ -501,7 +740,38 @@ var PyPIRegistry = class extends Registry {
|
|
|
501
740
|
}
|
|
502
741
|
};
|
|
503
742
|
}
|
|
504
|
-
|
|
743
|
+
indexFilesByVersion(normalizedName, versions, files) {
|
|
744
|
+
const filesByVersion = /* @__PURE__ */ new Map();
|
|
745
|
+
for (const version of versions) filesByVersion.set(version, []);
|
|
746
|
+
for (const file of files) {
|
|
747
|
+
const version = this.matchFileVersion(file.filename, normalizedName, versions);
|
|
748
|
+
if (version) filesByVersion.get(version).push(file);
|
|
749
|
+
}
|
|
750
|
+
return filesByVersion;
|
|
751
|
+
}
|
|
752
|
+
toVersion(normalizedName, version, files) {
|
|
753
|
+
if (files.length === 0) return {
|
|
754
|
+
number: version,
|
|
755
|
+
publishedAt: null,
|
|
756
|
+
licenses: "",
|
|
757
|
+
integrity: "",
|
|
758
|
+
status: "",
|
|
759
|
+
metadata: {}
|
|
760
|
+
};
|
|
761
|
+
const file = files.find((file) => file.filename.endsWith(".tar.gz")) ?? files[0];
|
|
762
|
+
const publishedAt = file["upload-time"] ? new Date(file["upload-time"]) : null;
|
|
763
|
+
const integrity = file.hashes?.sha256 ? `sha256-${file.hashes.sha256}` : "";
|
|
764
|
+
const status = file.yanked ? "yanked" : "";
|
|
765
|
+
this.downloadUrls.set(`${normalizedName}@${version}`, file.url);
|
|
766
|
+
return {
|
|
767
|
+
number: version,
|
|
768
|
+
publishedAt,
|
|
769
|
+
licenses: "",
|
|
770
|
+
integrity,
|
|
771
|
+
status,
|
|
772
|
+
metadata: {}
|
|
773
|
+
};
|
|
774
|
+
}
|
|
505
775
|
matchFileVersion(filename, normalizedName, versions) {
|
|
506
776
|
const lower = filename.toLowerCase();
|
|
507
777
|
let prefix;
|
|
@@ -522,11 +792,9 @@ var PyPIRegistry = class extends Registry {
|
|
|
522
792
|
const stripped = afterPrefix.replace(/\.(tar\.(gz|bz2|xz)|zip)$/i, "");
|
|
523
793
|
if (versions.includes(stripped)) return stripped;
|
|
524
794
|
}
|
|
525
|
-
/** Normalize package name per PEP 503. */
|
|
526
795
|
normalizeName(name) {
|
|
527
|
-
return name.toLowerCase().
|
|
796
|
+
return name.toLowerCase().replaceAll(/[-_.]+/g, "-");
|
|
528
797
|
}
|
|
529
|
-
/** Extract repository URL from project_urls. */
|
|
530
798
|
extractRepository(projectUrls) {
|
|
531
799
|
return normalizeRepositoryURL(this.findProjectUrl(projectUrls, [
|
|
532
800
|
"Repository",
|
|
@@ -536,7 +804,6 @@ var PyPIRegistry = class extends Registry {
|
|
|
536
804
|
"Homepage"
|
|
537
805
|
]));
|
|
538
806
|
}
|
|
539
|
-
/** Find a project URL by key, case-insensitive. */
|
|
540
807
|
findProjectUrl(projectUrls, keys) {
|
|
541
808
|
if (!projectUrls) return "";
|
|
542
809
|
const lowered = /* @__PURE__ */ new Map();
|
|
@@ -547,12 +814,30 @@ var PyPIRegistry = class extends Registry {
|
|
|
547
814
|
}
|
|
548
815
|
return "";
|
|
549
816
|
}
|
|
550
|
-
/** Parse comma-separated keywords. */
|
|
551
817
|
parseKeywords(keywords) {
|
|
552
818
|
if (!keywords) return [];
|
|
553
819
|
return keywords.split(",").map((k) => k.trim()).filter(Boolean);
|
|
554
820
|
}
|
|
555
|
-
|
|
821
|
+
parseExtraMarker(marker) {
|
|
822
|
+
const extraMatch = marker.match(/extra\s*==\s*["']([^"']+)["']/);
|
|
823
|
+
if (!extraMatch) return {
|
|
824
|
+
scope: "runtime",
|
|
825
|
+
optional: false
|
|
826
|
+
};
|
|
827
|
+
const extraName = extraMatch[1].toLowerCase();
|
|
828
|
+
if (/^dev(elop(ment)?)?$/.test(extraName)) return {
|
|
829
|
+
scope: "development",
|
|
830
|
+
optional: true
|
|
831
|
+
};
|
|
832
|
+
if (/^test(s|ing)?$/.test(extraName)) return {
|
|
833
|
+
scope: "test",
|
|
834
|
+
optional: true
|
|
835
|
+
};
|
|
836
|
+
return {
|
|
837
|
+
scope: "runtime",
|
|
838
|
+
optional: true
|
|
839
|
+
};
|
|
840
|
+
}
|
|
556
841
|
parsePEP508(depStr) {
|
|
557
842
|
const semiIdx = depStr.indexOf(";");
|
|
558
843
|
const mainPart = semiIdx === -1 ? depStr.trim() : depStr.slice(0, semiIdx).trim();
|
|
@@ -562,26 +847,28 @@ var PyPIRegistry = class extends Registry {
|
|
|
562
847
|
const depName = match[1];
|
|
563
848
|
let versionSpec = match[2].trim();
|
|
564
849
|
if (versionSpec.startsWith("(") && versionSpec.endsWith(")")) versionSpec = versionSpec.slice(1, -1).trim();
|
|
565
|
-
|
|
566
|
-
let optional = false;
|
|
567
|
-
if (markerStr) {
|
|
568
|
-
const extraMatch = markerStr.match(/extra\s*==\s*["']([^"']+)["']/);
|
|
569
|
-
if (extraMatch) {
|
|
570
|
-
optional = true;
|
|
571
|
-
const extraName = extraMatch[1].toLowerCase();
|
|
572
|
-
if (/^dev(elop(ment)?)?$/.test(extraName)) scope = "development";
|
|
573
|
-
else if (/^test(s|ing)?$/.test(extraName)) scope = "test";
|
|
574
|
-
}
|
|
575
|
-
}
|
|
850
|
+
const marker = this.parseExtraMarker(markerStr);
|
|
576
851
|
return {
|
|
577
852
|
name: this.normalizeName(depName),
|
|
578
853
|
requirements: versionSpec,
|
|
579
|
-
scope,
|
|
580
|
-
optional
|
|
854
|
+
scope: marker.scope,
|
|
855
|
+
optional: marker.optional
|
|
581
856
|
};
|
|
582
857
|
}
|
|
583
858
|
};
|
|
584
859
|
register("pypi", "https://pypi.org", PyPIRegistry);
|
|
860
|
+
function orEmpty(value) {
|
|
861
|
+
return value || "";
|
|
862
|
+
}
|
|
863
|
+
function gemLicenses(licenses) {
|
|
864
|
+
return licenses ? combineLicenses(licenses.map((license) => normalizeLicense(license))) : "";
|
|
865
|
+
}
|
|
866
|
+
function gemRepositoryURL(sourceCodeURI, metadata, homepageURI) {
|
|
867
|
+
const metadataURI = metadata?.["source_code_uri"];
|
|
868
|
+
if (sourceCodeURI) return normalizeRepositoryURL(sourceCodeURI);
|
|
869
|
+
if (metadataURI) return normalizeRepositoryURL(metadataURI);
|
|
870
|
+
return normalizeRepositoryURL(homepageURI ?? "");
|
|
871
|
+
}
|
|
585
872
|
/** RubyGems registry client. */
|
|
586
873
|
var RubyGemsRegistry = class extends Registry {
|
|
587
874
|
constructor(baseURL, client) {
|
|
@@ -598,23 +885,22 @@ var RubyGemsRegistry = class extends Registry {
|
|
|
598
885
|
const url = `${this.baseURL}/api/v1/gems/${name}.json`;
|
|
599
886
|
try {
|
|
600
887
|
const data = await this.client.getJSON(url, signal);
|
|
601
|
-
const licenses =
|
|
602
|
-
const repository =
|
|
888
|
+
const licenses = gemLicenses(data.licenses);
|
|
889
|
+
const repository = gemRepositoryURL(data.source_code_uri, data.metadata, data.homepage_uri);
|
|
603
890
|
return {
|
|
604
891
|
name: data.name,
|
|
605
|
-
description: data.description
|
|
606
|
-
homepage: data.homepage_uri
|
|
607
|
-
documentation: data.documentation_uri
|
|
892
|
+
description: orEmpty(data.description),
|
|
893
|
+
homepage: orEmpty(data.homepage_uri),
|
|
894
|
+
documentation: orEmpty(data.documentation_uri),
|
|
608
895
|
repository,
|
|
609
896
|
licenses,
|
|
610
897
|
keywords: [],
|
|
611
898
|
namespace: "",
|
|
612
|
-
latestVersion: data.version
|
|
613
|
-
metadata: data.metadata
|
|
899
|
+
latestVersion: orEmpty(data.version),
|
|
900
|
+
metadata: data.metadata ?? {}
|
|
614
901
|
};
|
|
615
902
|
} catch (error) {
|
|
616
|
-
|
|
617
|
-
throw error;
|
|
903
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
618
904
|
}
|
|
619
905
|
}
|
|
620
906
|
async fetchVersions(name, signal) {
|
|
@@ -636,8 +922,7 @@ var RubyGemsRegistry = class extends Registry {
|
|
|
636
922
|
}
|
|
637
923
|
return versions;
|
|
638
924
|
} catch (error) {
|
|
639
|
-
|
|
640
|
-
throw error;
|
|
925
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
641
926
|
}
|
|
642
927
|
}
|
|
643
928
|
async fetchDependencies(name, version, signal) {
|
|
@@ -659,8 +944,7 @@ var RubyGemsRegistry = class extends Registry {
|
|
|
659
944
|
});
|
|
660
945
|
return dependencies;
|
|
661
946
|
} catch (error) {
|
|
662
|
-
|
|
663
|
-
throw error;
|
|
947
|
+
rethrowFetchError(error, this.ecosystem(), name, version);
|
|
664
948
|
}
|
|
665
949
|
}
|
|
666
950
|
async fetchMaintainers(name, signal) {
|
|
@@ -678,8 +962,7 @@ var RubyGemsRegistry = class extends Registry {
|
|
|
678
962
|
});
|
|
679
963
|
return maintainers;
|
|
680
964
|
} catch (error) {
|
|
681
|
-
|
|
682
|
-
throw error;
|
|
965
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
683
966
|
}
|
|
684
967
|
}
|
|
685
968
|
urls() {
|
|
@@ -735,16 +1018,15 @@ var PackagistRegistry = class extends Registry {
|
|
|
735
1018
|
description: packageData.description || "",
|
|
736
1019
|
homepage: "",
|
|
737
1020
|
documentation: "",
|
|
738
|
-
repository: normalizeRepositoryURL(packageData.repository
|
|
1021
|
+
repository: normalizeRepositoryURL(packageData.repository ? packageData.repository : latestVersionData.source?.url ?? ""),
|
|
739
1022
|
licenses,
|
|
740
|
-
keywords: packageData.keywords
|
|
1023
|
+
keywords: packageData.keywords ?? [],
|
|
741
1024
|
namespace: vendor,
|
|
742
1025
|
latestVersion,
|
|
743
1026
|
metadata: {}
|
|
744
1027
|
};
|
|
745
1028
|
} catch (error) {
|
|
746
|
-
|
|
747
|
-
throw error;
|
|
1029
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
748
1030
|
}
|
|
749
1031
|
}
|
|
750
1032
|
async fetchVersions(name, signal) {
|
|
@@ -767,8 +1049,7 @@ var PackagistRegistry = class extends Registry {
|
|
|
767
1049
|
}
|
|
768
1050
|
return versions;
|
|
769
1051
|
} catch (error) {
|
|
770
|
-
|
|
771
|
-
throw error;
|
|
1052
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
772
1053
|
}
|
|
773
1054
|
}
|
|
774
1055
|
async fetchDependencies(name, version, signal) {
|
|
@@ -796,8 +1077,7 @@ var PackagistRegistry = class extends Registry {
|
|
|
796
1077
|
}
|
|
797
1078
|
return dependencies;
|
|
798
1079
|
} catch (error) {
|
|
799
|
-
|
|
800
|
-
throw error;
|
|
1080
|
+
rethrowFetchError(error, this.ecosystem(), name, version);
|
|
801
1081
|
}
|
|
802
1082
|
}
|
|
803
1083
|
async fetchMaintainers(name, signal) {
|
|
@@ -805,26 +1085,9 @@ var PackagistRegistry = class extends Registry {
|
|
|
805
1085
|
const url = `${this.baseURL}/packages/${vendor}/${pkg}.json`;
|
|
806
1086
|
try {
|
|
807
1087
|
const data = await this.client.getJSON(url, signal);
|
|
808
|
-
|
|
809
|
-
const seen = /* @__PURE__ */ new Set();
|
|
810
|
-
for (const versionData of Object.values(data.package.versions)) if (versionData.authors) for (const author of versionData.authors) {
|
|
811
|
-
const key = `${author.name}:${author.email}`;
|
|
812
|
-
if (!seen.has(key)) {
|
|
813
|
-
seen.add(key);
|
|
814
|
-
maintainers.push({
|
|
815
|
-
uuid: "",
|
|
816
|
-
login: author.email ? author.email.split("@")[0] : "",
|
|
817
|
-
name: author.name || "",
|
|
818
|
-
email: author.email || "",
|
|
819
|
-
url: author.homepage || "",
|
|
820
|
-
role: author.role || ""
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
return maintainers;
|
|
1088
|
+
return this.collectMaintainers(Object.values(data.package.versions));
|
|
825
1089
|
} catch (error) {
|
|
826
|
-
|
|
827
|
-
throw error;
|
|
1090
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
828
1091
|
}
|
|
829
1092
|
}
|
|
830
1093
|
urls() {
|
|
@@ -857,13 +1120,32 @@ var PackagistRegistry = class extends Registry {
|
|
|
857
1120
|
}
|
|
858
1121
|
};
|
|
859
1122
|
}
|
|
860
|
-
|
|
1123
|
+
collectMaintainers(versions) {
|
|
1124
|
+
const maintainers = [];
|
|
1125
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1126
|
+
for (const version of versions) for (const author of version.authors ?? []) {
|
|
1127
|
+
const key = `${author.name}:${author.email}`;
|
|
1128
|
+
if (seen.has(key)) continue;
|
|
1129
|
+
seen.add(key);
|
|
1130
|
+
maintainers.push(this.toMaintainer(author));
|
|
1131
|
+
}
|
|
1132
|
+
return maintainers;
|
|
1133
|
+
}
|
|
1134
|
+
toMaintainer(author) {
|
|
1135
|
+
return {
|
|
1136
|
+
uuid: "",
|
|
1137
|
+
login: author.email ? author.email.split("@")[0] : "",
|
|
1138
|
+
name: author.name || "",
|
|
1139
|
+
email: author.email || "",
|
|
1140
|
+
url: author.homepage || "",
|
|
1141
|
+
role: author.role || ""
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
861
1144
|
parseName(name) {
|
|
862
1145
|
const parts = name.split("/");
|
|
863
1146
|
if (parts.length !== 2 || !parts[0] || !parts[1]) throw new InvalidPURLError(`pkg:composer/${name}`, "invalid Composer package name, expected \"vendor/package\" format");
|
|
864
1147
|
return [parts[0], parts[1]];
|
|
865
1148
|
}
|
|
866
|
-
/** Find the highest non-dev version. */
|
|
867
1149
|
findLatestVersion(versions) {
|
|
868
1150
|
const stable = versions.filter((v) => !v.startsWith("dev-") && !v.endsWith("-dev"));
|
|
869
1151
|
if (stable.length === 0) return versions[0] || "";
|
|
@@ -880,12 +1162,10 @@ var PackagistRegistry = class extends Registry {
|
|
|
880
1162
|
return 0;
|
|
881
1163
|
})[0] || versions[0] || "";
|
|
882
1164
|
}
|
|
883
|
-
/** Extract and normalize licenses. */
|
|
884
1165
|
extractLicenses(raw) {
|
|
885
1166
|
if (!raw) return "";
|
|
886
1167
|
return combineLicenses(Array.isArray(raw) ? raw : [raw]);
|
|
887
1168
|
}
|
|
888
|
-
/** Skip PHP and extension dependencies. */
|
|
889
1169
|
shouldSkipDependency(name) {
|
|
890
1170
|
return name === "php" || name.startsWith("ext-");
|
|
891
1171
|
}
|
|
@@ -1017,17 +1297,14 @@ var AlpmRegistry = class extends Registry {
|
|
|
1017
1297
|
role: "maintainer"
|
|
1018
1298
|
}));
|
|
1019
1299
|
}
|
|
1020
|
-
/** Search official repos by exact package name. Prefers x86_64 results. */
|
|
1021
1300
|
async searchOfficial(name, signal) {
|
|
1022
1301
|
const url = `${this.baseURL}/packages/search/json/?name=${encodeURIComponent(name)}`;
|
|
1023
1302
|
try {
|
|
1024
1303
|
const data = await this.client.getJSON(url, signal);
|
|
1025
1304
|
if (!data.results || data.results.length === 0) throw new NotFoundError("alpm", name);
|
|
1026
|
-
return data.results.find((r) => r.arch === "x86_64")
|
|
1305
|
+
return data.results.find((r) => r.arch === "x86_64") ?? data.results[0];
|
|
1027
1306
|
} catch (error) {
|
|
1028
|
-
|
|
1029
|
-
if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("alpm", name);
|
|
1030
|
-
throw error;
|
|
1307
|
+
rethrowFetchError(error, this.ecosystem(), name);
|
|
1031
1308
|
}
|
|
1032
1309
|
}
|
|
1033
1310
|
async fetchAurPackage(name, signal) {
|
|
@@ -1038,8 +1315,8 @@ var AlpmRegistry = class extends Registry {
|
|
|
1038
1315
|
homepage: result.URL || "",
|
|
1039
1316
|
documentation: "",
|
|
1040
1317
|
repository: "",
|
|
1041
|
-
licenses: combineLicenses(result.License
|
|
1042
|
-
keywords: result.Keywords
|
|
1318
|
+
licenses: combineLicenses(result.License ?? []),
|
|
1319
|
+
keywords: result.Keywords ?? [],
|
|
1043
1320
|
namespace: "aur",
|
|
1044
1321
|
latestVersion: result.Version,
|
|
1045
1322
|
metadata: {
|
|
@@ -1057,7 +1334,7 @@ var AlpmRegistry = class extends Registry {
|
|
|
1057
1334
|
return [{
|
|
1058
1335
|
number: result.Version,
|
|
1059
1336
|
publishedAt: result.LastModified ? /* @__PURE__ */ new Date(result.LastModified * 1e3) : null,
|
|
1060
|
-
licenses: combineLicenses(result.License
|
|
1337
|
+
licenses: combineLicenses(result.License ?? []),
|
|
1061
1338
|
integrity: "",
|
|
1062
1339
|
status: result.OutOfDate ? "deprecated" : "",
|
|
1063
1340
|
metadata: {
|
|
@@ -1069,7 +1346,7 @@ var AlpmRegistry = class extends Registry {
|
|
|
1069
1346
|
async fetchAurDependencies(name, version, signal) {
|
|
1070
1347
|
const result = await this.fetchAurInfo(name, signal);
|
|
1071
1348
|
this.assertVersionMatches(`aur/${name}`, version, result.Version);
|
|
1072
|
-
return this.buildDependencies(result.Depends
|
|
1349
|
+
return this.buildDependencies(result.Depends ?? [], result.MakeDepends ?? [], result.OptDepends ?? [], result.CheckDepends ?? []);
|
|
1073
1350
|
}
|
|
1074
1351
|
async fetchAurMaintainers(name, signal) {
|
|
1075
1352
|
const result = await this.fetchAurInfo(name, signal);
|
|
@@ -1083,7 +1360,6 @@ var AlpmRegistry = class extends Registry {
|
|
|
1083
1360
|
role: "maintainer"
|
|
1084
1361
|
}];
|
|
1085
1362
|
}
|
|
1086
|
-
/** Fetch package info from AUR RPC v5. */
|
|
1087
1363
|
async fetchAurInfo(name, signal) {
|
|
1088
1364
|
const url = `${AUR_BASE_URL}/rpc/v5/info/${encodeURIComponent(name)}`;
|
|
1089
1365
|
try {
|
|
@@ -1091,12 +1367,9 @@ var AlpmRegistry = class extends Registry {
|
|
|
1091
1367
|
if (!data.results || data.results.length === 0) throw new NotFoundError("alpm", `aur/${name}`);
|
|
1092
1368
|
return data.results[0];
|
|
1093
1369
|
} catch (error) {
|
|
1094
|
-
|
|
1095
|
-
if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("alpm", `aur/${name}`);
|
|
1096
|
-
throw error;
|
|
1370
|
+
rethrowFetchError(error, this.ecosystem(), `aur/${name}`);
|
|
1097
1371
|
}
|
|
1098
1372
|
}
|
|
1099
|
-
/** Parse "namespace/name" into components. Defaults to "arch" namespace. */
|
|
1100
1373
|
parseName(fullName) {
|
|
1101
1374
|
const normalized = fullName.trim().toLowerCase();
|
|
1102
1375
|
const slashIdx = normalized.indexOf("/");
|
|
@@ -1115,11 +1388,9 @@ var AlpmRegistry = class extends Registry {
|
|
|
1115
1388
|
assertVersionMatches(name, requested, current) {
|
|
1116
1389
|
if (requested && requested !== current) throw new NotFoundError("alpm", name, requested);
|
|
1117
1390
|
}
|
|
1118
|
-
/** Format ALPM version: epoch:pkgver-pkgrel (epoch omitted when 0). */
|
|
1119
1391
|
formatVersion(result) {
|
|
1120
1392
|
return `${result.epoch && result.epoch > 0 ? `${result.epoch}:` : ""}${result.pkgver}-${result.pkgrel}`;
|
|
1121
1393
|
}
|
|
1122
|
-
/** Build normalized dependency list from ALPM dependency arrays. */
|
|
1123
1394
|
buildDependencies(depends, makedepends, optdepends, checkdepends) {
|
|
1124
1395
|
const dependencies = [];
|
|
1125
1396
|
for (const dep of depends) {
|
|
@@ -1160,7 +1431,6 @@ var AlpmRegistry = class extends Registry {
|
|
|
1160
1431
|
}
|
|
1161
1432
|
return dependencies;
|
|
1162
1433
|
}
|
|
1163
|
-
/** Parse a dependency string like "glibc>=2.33" or "bash". */
|
|
1164
1434
|
parseDep(dep) {
|
|
1165
1435
|
const match = dep.match(/^([a-zA-Z0-9@._+-]+)(.*)$/);
|
|
1166
1436
|
if (!match) return {
|
|
@@ -1172,7 +1442,6 @@ var AlpmRegistry = class extends Registry {
|
|
|
1172
1442
|
requirements: match[2].trim()
|
|
1173
1443
|
};
|
|
1174
1444
|
}
|
|
1175
|
-
/** Parse optional dependency like "perl-locale-gettext: translation support". */
|
|
1176
1445
|
parseOptDep(dep) {
|
|
1177
1446
|
const colonIdx = dep.indexOf(":");
|
|
1178
1447
|
if (colonIdx === -1) {
|
|
@@ -1192,6 +1461,6 @@ var AlpmRegistry = class extends Registry {
|
|
|
1192
1461
|
}
|
|
1193
1462
|
};
|
|
1194
1463
|
register("alpm", "https://archlinux.org", AlpmRegistry);
|
|
1195
|
-
export { CargoRegistry as a, PyPIRegistry as i, PackagistRegistry as n, NpmRegistry as o, RubyGemsRegistry as r, AlpmRegistry as t };
|
|
1464
|
+
export { CargoRegistry as a, createFromPURL as c, normalizeRepositoryURL as d, combineLicenses as f, PyPIRegistry as i, fullName as l, PackagistRegistry as n, NpmRegistry as o, normalizeLicense as p, RubyGemsRegistry as r, buildPURL as s, AlpmRegistry as t, parsePURL as u };
|
|
1196
1465
|
|
|
1197
1466
|
//# sourceMappingURL=registries.mjs.map
|