@agntn/registries 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +303 -0
  3. package/dist/_chunks/cached-registry.d.mts +35 -0
  4. package/dist/_chunks/cached-registry.d.mts.map +1 -0
  5. package/dist/_chunks/errors.d.mts +90 -0
  6. package/dist/_chunks/errors.d.mts.map +1 -0
  7. package/dist/_chunks/helpers.mjs +95 -0
  8. package/dist/_chunks/helpers.mjs.map +1 -0
  9. package/dist/_chunks/index.d.mts +142 -0
  10. package/dist/_chunks/index.d.mts.map +1 -0
  11. package/dist/_chunks/index2.d.mts +50 -0
  12. package/dist/_chunks/index2.d.mts.map +1 -0
  13. package/dist/_chunks/index3.d.mts +18 -0
  14. package/dist/_chunks/index3.d.mts.map +1 -0
  15. package/dist/_chunks/lockfile.d.mts +50 -0
  16. package/dist/_chunks/lockfile.d.mts.map +1 -0
  17. package/dist/_chunks/paths.d.mts +11 -0
  18. package/dist/_chunks/paths.d.mts.map +1 -0
  19. package/dist/_chunks/purl.mjs +192 -0
  20. package/dist/_chunks/purl.mjs.map +1 -0
  21. package/dist/_chunks/registries.mjs +1197 -0
  22. package/dist/_chunks/registries.mjs.map +1 -0
  23. package/dist/_chunks/registry.d.mts +39 -0
  24. package/dist/_chunks/registry.d.mts.map +1 -0
  25. package/dist/_chunks/registry.mjs +201 -0
  26. package/dist/_chunks/registry.mjs.map +1 -0
  27. package/dist/_chunks/storage.d.mts +22 -0
  28. package/dist/_chunks/storage.d.mts.map +1 -0
  29. package/dist/_chunks/types.d.mts +73 -0
  30. package/dist/_chunks/types.d.mts.map +1 -0
  31. package/dist/ai.d.mts +35 -0
  32. package/dist/ai.d.mts.map +1 -0
  33. package/dist/ai.mjs +50 -0
  34. package/dist/ai.mjs.map +1 -0
  35. package/dist/cache/cached-registry.d.mts +4 -0
  36. package/dist/cache/cached-registry.mjs +92 -0
  37. package/dist/cache/cached-registry.mjs.map +1 -0
  38. package/dist/cache/index.d.mts +8 -0
  39. package/dist/cache/index.mjs +12 -0
  40. package/dist/cache/index.mjs.map +1 -0
  41. package/dist/cache/lockfile.d.mts +2 -0
  42. package/dist/cache/lockfile.mjs +68 -0
  43. package/dist/cache/lockfile.mjs.map +1 -0
  44. package/dist/cache/paths.d.mts +2 -0
  45. package/dist/cache/paths.mjs +26 -0
  46. package/dist/cache/paths.mjs.map +1 -0
  47. package/dist/cache/storage.d.mts +2 -0
  48. package/dist/cache/storage.mjs +40 -0
  49. package/dist/cache/storage.mjs.map +1 -0
  50. package/dist/cli.d.mts +1 -0
  51. package/dist/cli.mjs +19 -0
  52. package/dist/cli.mjs.map +1 -0
  53. package/dist/commands/cache.d.mts +7 -0
  54. package/dist/commands/cache.d.mts.map +1 -0
  55. package/dist/commands/cache.mjs +80 -0
  56. package/dist/commands/cache.mjs.map +1 -0
  57. package/dist/commands/deps.d.mts +23 -0
  58. package/dist/commands/deps.d.mts.map +1 -0
  59. package/dist/commands/deps.mjs +72 -0
  60. package/dist/commands/deps.mjs.map +1 -0
  61. package/dist/commands/info.d.mts +26 -0
  62. package/dist/commands/info.d.mts.map +1 -0
  63. package/dist/commands/info.mjs +54 -0
  64. package/dist/commands/info.mjs.map +1 -0
  65. package/dist/commands/maintainers.d.mts +23 -0
  66. package/dist/commands/maintainers.d.mts.map +1 -0
  67. package/dist/commands/maintainers.mjs +46 -0
  68. package/dist/commands/maintainers.mjs.map +1 -0
  69. package/dist/commands/shared.d.mts +27 -0
  70. package/dist/commands/shared.d.mts.map +1 -0
  71. package/dist/commands/shared.mjs +69 -0
  72. package/dist/commands/shared.mjs.map +1 -0
  73. package/dist/commands/versions.d.mts +31 -0
  74. package/dist/commands/versions.d.mts.map +1 -0
  75. package/dist/commands/versions.mjs +58 -0
  76. package/dist/commands/versions.mjs.map +1 -0
  77. package/dist/core/index.d.mts +4 -0
  78. package/dist/core/index.mjs +3 -0
  79. package/dist/index.d.mts +11 -0
  80. package/dist/index.mjs +10 -0
  81. package/dist/registries/index.d.mts +4 -0
  82. package/dist/registries/index.mjs +4 -0
  83. package/dist/types.d.mts +3 -0
  84. package/dist/types.mjs +1 -0
  85. package/package.json +102 -0
@@ -0,0 +1,1197 @@
1
+ import { a as register, c as HTTPError, l as InvalidPURLError, t as Registry, u as NotFoundError } from "./registry.mjs";
2
+ import { a as normalizeRepositoryURL, o as combineLicenses, s as normalizeLicense, t as buildPURL } from "./purl.mjs";
3
+ /** npm registry client. */
4
+ var NpmRegistry = class extends Registry {
5
+ constructor(baseURL, client) {
6
+ super();
7
+ this.baseURL = baseURL;
8
+ this.client = client;
9
+ }
10
+ baseURL;
11
+ client;
12
+ ecosystem() {
13
+ return "npm";
14
+ }
15
+ async fetchPackage(name, signal) {
16
+ const encodedName = this.encodeName(name);
17
+ const url = `${this.baseURL}/${encodedName}`;
18
+ try {
19
+ const data = await this.client.getJSON(url, signal);
20
+ const latestVersion = data["dist-tags"].latest;
21
+ const latestVersionData = data.versions[latestVersion];
22
+ const licenses = this.extractLicense(data.license || latestVersionData?.license);
23
+ const namespace = this.extractNamespace(name);
24
+ return {
25
+ name: data.name,
26
+ description: data.description || "",
27
+ homepage: data.homepage || "",
28
+ documentation: "",
29
+ repository: normalizeRepositoryURL(data.repository || ""),
30
+ licenses,
31
+ keywords: data.keywords || [],
32
+ namespace,
33
+ latestVersion,
34
+ metadata: {}
35
+ };
36
+ } catch (error) {
37
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("npm", name);
38
+ throw error;
39
+ }
40
+ }
41
+ async fetchVersions(name, signal) {
42
+ const encodedName = this.encodeName(name);
43
+ const url = `${this.baseURL}/${encodedName}`;
44
+ try {
45
+ const data = await this.client.getJSON(url, signal);
46
+ const versions = [];
47
+ for (const [versionStr, versionData] of Object.entries(data.versions)) {
48
+ const licenses = this.extractLicense(versionData.license);
49
+ const publishedAt = data.time?.[versionStr] ? new Date(data.time[versionStr]) : null;
50
+ const status = versionData.deprecated ? "deprecated" : "";
51
+ const integrity = versionData.dist?.integrity ? versionData.dist.integrity : versionData.dist?.shasum ? `sha1-${versionData.dist.shasum}` : "";
52
+ versions.push({
53
+ number: versionStr,
54
+ publishedAt,
55
+ licenses,
56
+ integrity,
57
+ status,
58
+ metadata: {}
59
+ });
60
+ }
61
+ return versions;
62
+ } catch (error) {
63
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("npm", name);
64
+ throw error;
65
+ }
66
+ }
67
+ async fetchDependencies(name, version, signal) {
68
+ const encodedName = this.encodeName(name);
69
+ const url = `${this.baseURL}/${encodedName}/${version}`;
70
+ try {
71
+ const versionData = await this.client.getJSON(url, signal);
72
+ const dependencies = [];
73
+ if (versionData.dependencies) for (const [depName, requirements] of Object.entries(versionData.dependencies)) {
74
+ if (versionData.optionalDependencies && Object.hasOwn(versionData.optionalDependencies, depName)) continue;
75
+ dependencies.push({
76
+ name: depName,
77
+ requirements,
78
+ scope: "runtime",
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;
101
+ } catch (error) {
102
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("npm", name, version);
103
+ throw error;
104
+ }
105
+ }
106
+ async fetchMaintainers(name, signal) {
107
+ const encodedName = this.encodeName(name);
108
+ const url = `${this.baseURL}/${encodedName}`;
109
+ try {
110
+ const data = await this.client.getJSON(url, signal);
111
+ const maintainers = [];
112
+ const seen = /* @__PURE__ */ new Set();
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;
157
+ } catch (error) {
158
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("npm", name);
159
+ throw error;
160
+ }
161
+ }
162
+ urls() {
163
+ return {
164
+ registry: (name, version) => {
165
+ const base = `https://www.npmjs.com/package/${name}`;
166
+ return version ? `${base}/v/${version}` : base;
167
+ },
168
+ download: (name, version) => {
169
+ return `https://registry.npmjs.org/${this.encodeName(name)}/-/${name.includes("/") ? name.split("/")[1] : name}-${version}.tgz`;
170
+ },
171
+ documentation: (name, _version) => {
172
+ return `https://www.npmjs.com/package/${name}`;
173
+ },
174
+ readme: (name, version) => {
175
+ return `https://cdn.jsdelivr.net/npm/${name}${version ? `@${version}` : ""}/README.md`;
176
+ },
177
+ purl: (name, version) => {
178
+ const namespace = this.extractNamespace(name);
179
+ return buildPURL({
180
+ type: "npm",
181
+ namespace,
182
+ name: namespace ? name.slice(namespace.length + 1) : name,
183
+ version
184
+ });
185
+ }
186
+ };
187
+ }
188
+ /** Build a stable dedup key. Prefers email (unique per account), falls back to name. */
189
+ maintainerKey(name, email) {
190
+ if (email) return email;
191
+ if (name) return name;
192
+ return "";
193
+ }
194
+ /** Encode package name for URL (handle scoped packages). */
195
+ encodeName(name) {
196
+ if (name.startsWith("@")) return name.replace("/", "%2F");
197
+ return name;
198
+ }
199
+ /** Extract namespace from scoped package name. */
200
+ extractNamespace(name) {
201
+ if (name.startsWith("@")) return name.split("/")[0] || "";
202
+ return "";
203
+ }
204
+ /** Extract and normalize license. */
205
+ extractLicense(raw) {
206
+ if (!raw) return "";
207
+ if (typeof raw === "string") return normalizeLicense(raw);
208
+ if (typeof raw === "object" && raw !== null) {
209
+ const obj = raw;
210
+ if (typeof obj["type"] === "string") return normalizeLicense(obj["type"]);
211
+ }
212
+ return "";
213
+ }
214
+ };
215
+ register("npm", "https://registry.npmjs.org", NpmRegistry);
216
+ /** Crates.io registry client. */
217
+ var CargoRegistry = class extends Registry {
218
+ constructor(baseURL, client) {
219
+ super();
220
+ this.baseURL = baseURL;
221
+ this.client = client;
222
+ }
223
+ baseURL;
224
+ client;
225
+ ecosystem() {
226
+ return "cargo";
227
+ }
228
+ async fetchPackage(name, signal) {
229
+ const url = `${this.baseURL}/api/v1/crates/${name}`;
230
+ try {
231
+ const data = await this.client.getJSON(url, signal);
232
+ const crateData = data.crate;
233
+ const latestVersion = crateData.max_stable_version || crateData.max_version;
234
+ const latestVersionData = data.versions.find((v) => v.num === latestVersion);
235
+ return {
236
+ name: crateData.name,
237
+ description: crateData.description || "",
238
+ homepage: crateData.homepage || "",
239
+ documentation: crateData.documentation || "",
240
+ repository: normalizeRepositoryURL(crateData.repository || ""),
241
+ licenses: normalizeLicense(latestVersionData ? latestVersionData.license : data.versions[0]?.license ?? ""),
242
+ keywords: crateData.keywords,
243
+ namespace: "",
244
+ latestVersion,
245
+ metadata: {
246
+ downloads: crateData.downloads,
247
+ recentDownloads: crateData.recent_downloads,
248
+ categories: crateData.categories,
249
+ newestVersion: crateData.newest_version,
250
+ defaultVersion: crateData.max_version,
251
+ updatedAt: crateData.updated_at,
252
+ createdAt: crateData.created_at
253
+ }
254
+ };
255
+ } catch (error) {
256
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("cargo", name);
257
+ throw error;
258
+ }
259
+ }
260
+ async fetchVersions(name, signal) {
261
+ const url = `${this.baseURL}/api/v1/crates/${name}`;
262
+ try {
263
+ const data = await this.client.getJSON(url, signal);
264
+ const versions = [];
265
+ for (const versionData of data.versions) {
266
+ const publishedAt = new Date(versionData.created_at);
267
+ const status = versionData.yanked ? "yanked" : "";
268
+ versions.push({
269
+ number: versionData.num,
270
+ publishedAt,
271
+ licenses: normalizeLicense(versionData.license),
272
+ integrity: `sha256-${versionData.checksum}`,
273
+ status,
274
+ metadata: {
275
+ crateSize: versionData.crate_size,
276
+ features: versionData.features,
277
+ downloads: versionData.downloads
278
+ }
279
+ });
280
+ }
281
+ return versions;
282
+ } catch (error) {
283
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("cargo", name);
284
+ throw error;
285
+ }
286
+ }
287
+ async fetchDependencies(name, version, signal) {
288
+ const url = `${this.baseURL}/api/v1/crates/${name}/${version}/dependencies`;
289
+ try {
290
+ const data = await this.client.getJSON(url, signal);
291
+ const dependencies = [];
292
+ for (const dep of data.dependencies) {
293
+ let scope;
294
+ if (dep.kind === "dev") scope = "development";
295
+ else if (dep.kind === "build") scope = "build";
296
+ else scope = "runtime";
297
+ dependencies.push({
298
+ name: dep.crate_id,
299
+ requirements: dep.req,
300
+ scope,
301
+ optional: dep.optional
302
+ });
303
+ }
304
+ return dependencies;
305
+ } catch (error) {
306
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("cargo", name, version);
307
+ throw error;
308
+ }
309
+ }
310
+ async fetchMaintainers(name, signal) {
311
+ const url = `${this.baseURL}/api/v1/crates/${name}/owner_user`;
312
+ try {
313
+ const data = await this.client.getJSON(url, signal);
314
+ const maintainers = [];
315
+ for (const user of data.users) maintainers.push({
316
+ uuid: user.id.toString(),
317
+ login: user.login,
318
+ name: user.name,
319
+ email: "",
320
+ url: user.url,
321
+ role: ""
322
+ });
323
+ return maintainers;
324
+ } catch (error) {
325
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("cargo", name);
326
+ throw error;
327
+ }
328
+ }
329
+ urls() {
330
+ return {
331
+ registry: (name, version) => {
332
+ const base = `https://crates.io/crates/${name}`;
333
+ return version ? `${base}/${version}` : base;
334
+ },
335
+ download: (name, version) => {
336
+ return `https://crates.io/api/v1/crates/${name}/${version}/download`;
337
+ },
338
+ documentation: (name, version) => {
339
+ return version ? `https://docs.rs/${name}/${version}` : `https://docs.rs/${name}`;
340
+ },
341
+ readme: (name, version) => {
342
+ return version ? `https://crates.io/api/v1/crates/${name}/${version}/readme` : `https://crates.io/crates/${name}`;
343
+ },
344
+ purl: (name, version) => {
345
+ return buildPURL({
346
+ type: "cargo",
347
+ name,
348
+ version
349
+ });
350
+ }
351
+ };
352
+ }
353
+ };
354
+ register("cargo", "https://crates.io", CargoRegistry);
355
+ const PYPI_FILENAME_SEPARATORS = [
356
+ "_",
357
+ "-",
358
+ "."
359
+ ];
360
+ /** PyPI registry client. */
361
+ var PyPIRegistry = class extends Registry {
362
+ constructor(baseURL, client) {
363
+ super();
364
+ this.baseURL = baseURL;
365
+ this.client = client;
366
+ }
367
+ baseURL;
368
+ client;
369
+ downloadUrls = /* @__PURE__ */ new Map();
370
+ ecosystem() {
371
+ return "pypi";
372
+ }
373
+ async fetchPackage(name, signal) {
374
+ const normalized = this.normalizeName(name);
375
+ const url = `${this.baseURL}/pypi/${normalized}/json`;
376
+ try {
377
+ const info = (await this.client.getJSON(url, signal)).info;
378
+ const licenses = normalizeLicense(info.license);
379
+ const repository = this.extractRepository(info.project_urls);
380
+ const keywords = this.parseKeywords(info.keywords);
381
+ return {
382
+ name: info.name,
383
+ description: info.summary || info.description || "",
384
+ homepage: this.findProjectUrl(info.project_urls, ["Homepage"]),
385
+ documentation: this.findProjectUrl(info.project_urls, ["Documentation"]),
386
+ repository,
387
+ licenses,
388
+ keywords,
389
+ namespace: "",
390
+ latestVersion: info.version,
391
+ metadata: {}
392
+ };
393
+ } catch (error) {
394
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("pypi", name);
395
+ throw error;
396
+ }
397
+ }
398
+ async fetchVersions(name, signal) {
399
+ const normalized = this.normalizeName(name);
400
+ const url = `${this.baseURL}/simple/${normalized}/`;
401
+ try {
402
+ const data = await this.client.getJSON(url, signal, { Accept: "application/vnd.pypi.simple.v1+json" });
403
+ const filesByVersion = /* @__PURE__ */ new Map();
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;
437
+ } catch (error) {
438
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("pypi", name);
439
+ throw error;
440
+ }
441
+ }
442
+ async fetchDependencies(name, version, signal) {
443
+ const normalized = this.normalizeName(name);
444
+ const url = `${this.baseURL}/pypi/${normalized}/${version}/json`;
445
+ try {
446
+ const data = await this.client.getJSON(url, signal);
447
+ const dependencies = [];
448
+ if (data.info.requires_dist) for (const depStr of data.info.requires_dist) {
449
+ const dep = this.parsePEP508(depStr);
450
+ if (dep) dependencies.push(dep);
451
+ }
452
+ return dependencies;
453
+ } catch (error) {
454
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("pypi", name, version);
455
+ throw error;
456
+ }
457
+ }
458
+ async fetchMaintainers(name, signal) {
459
+ const normalized = this.normalizeName(name);
460
+ const url = `${this.baseURL}/pypi/${normalized}/json`;
461
+ try {
462
+ const data = await this.client.getJSON(url, signal);
463
+ const maintainers = [];
464
+ if (data.info.author || data.info.author_email) maintainers.push({
465
+ uuid: "",
466
+ login: data.info.author_email ? data.info.author_email.split("@")[0] : "",
467
+ name: data.info.author || "",
468
+ email: data.info.author_email || "",
469
+ url: "",
470
+ role: "author"
471
+ });
472
+ return maintainers;
473
+ } catch (error) {
474
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("pypi", name);
475
+ throw error;
476
+ }
477
+ }
478
+ urls() {
479
+ return {
480
+ registry: (name, version) => {
481
+ const base = `https://pypi.org/project/${this.normalizeName(name)}`;
482
+ return version ? `${base}/${version}` : base;
483
+ },
484
+ download: (name, version) => {
485
+ const normalized = this.normalizeName(name);
486
+ return this.downloadUrls.get(`${normalized}@${version}`) ?? `https://pypi.org/project/${normalized}/${version}/`;
487
+ },
488
+ documentation: (name, _version) => {
489
+ return `https://pypi.org/project/${this.normalizeName(name)}`;
490
+ },
491
+ readme: (name, version) => {
492
+ const normalized = this.normalizeName(name);
493
+ return version ? `https://pypi.org/project/${normalized}/${version}/` : `https://pypi.org/project/${normalized}/`;
494
+ },
495
+ purl: (name, version) => {
496
+ return buildPURL({
497
+ type: "pypi",
498
+ name: this.normalizeName(name),
499
+ version
500
+ });
501
+ }
502
+ };
503
+ }
504
+ /** Match a filename to a version from the known versions list. */
505
+ matchFileVersion(filename, normalizedName, versions) {
506
+ const lower = filename.toLowerCase();
507
+ let prefix;
508
+ for (const separator of PYPI_FILENAME_SEPARATORS) {
509
+ const candidate = normalizedName.replaceAll("-", separator) + "-";
510
+ if (lower.startsWith(candidate)) {
511
+ prefix = candidate;
512
+ break;
513
+ }
514
+ }
515
+ if (!prefix) return void 0;
516
+ const afterPrefix = filename.slice(prefix.length);
517
+ const dashIdx = afterPrefix.indexOf("-");
518
+ if (dashIdx !== -1) {
519
+ const candidate = afterPrefix.slice(0, dashIdx);
520
+ if (versions.includes(candidate)) return candidate;
521
+ }
522
+ const stripped = afterPrefix.replace(/\.(tar\.(gz|bz2|xz)|zip)$/i, "");
523
+ if (versions.includes(stripped)) return stripped;
524
+ }
525
+ /** Normalize package name per PEP 503. */
526
+ normalizeName(name) {
527
+ return name.toLowerCase().replace(/[-_.]+/g, "-");
528
+ }
529
+ /** Extract repository URL from project_urls. */
530
+ extractRepository(projectUrls) {
531
+ return normalizeRepositoryURL(this.findProjectUrl(projectUrls, [
532
+ "Repository",
533
+ "Source",
534
+ "Source Code",
535
+ "GitHub",
536
+ "Homepage"
537
+ ]));
538
+ }
539
+ /** Find a project URL by key, case-insensitive. */
540
+ findProjectUrl(projectUrls, keys) {
541
+ if (!projectUrls) return "";
542
+ const lowered = /* @__PURE__ */ new Map();
543
+ for (const [k, v] of Object.entries(projectUrls)) lowered.set(k.toLowerCase(), v);
544
+ for (const key of keys) {
545
+ const value = lowered.get(key.toLowerCase());
546
+ if (value) return value;
547
+ }
548
+ return "";
549
+ }
550
+ /** Parse comma-separated keywords. */
551
+ parseKeywords(keywords) {
552
+ if (!keywords) return [];
553
+ return keywords.split(",").map((k) => k.trim()).filter(Boolean);
554
+ }
555
+ /** Parse PEP 508 dependency string into a normalized Dependency. */
556
+ parsePEP508(depStr) {
557
+ const semiIdx = depStr.indexOf(";");
558
+ const mainPart = semiIdx === -1 ? depStr.trim() : depStr.slice(0, semiIdx).trim();
559
+ const markerStr = semiIdx === -1 ? "" : depStr.slice(semiIdx + 1).trim();
560
+ const match = mainPart.match(/^([a-zA-Z0-9._-]+)\s*(?:\[.*?\])?\s*(.*)$/);
561
+ if (!match) return null;
562
+ const depName = match[1];
563
+ let versionSpec = match[2].trim();
564
+ if (versionSpec.startsWith("(") && versionSpec.endsWith(")")) versionSpec = versionSpec.slice(1, -1).trim();
565
+ let scope = "runtime";
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
+ }
576
+ return {
577
+ name: this.normalizeName(depName),
578
+ requirements: versionSpec,
579
+ scope,
580
+ optional
581
+ };
582
+ }
583
+ };
584
+ register("pypi", "https://pypi.org", PyPIRegistry);
585
+ /** RubyGems registry client. */
586
+ var RubyGemsRegistry = class extends Registry {
587
+ constructor(baseURL, client) {
588
+ super();
589
+ this.baseURL = baseURL;
590
+ this.client = client;
591
+ }
592
+ baseURL;
593
+ client;
594
+ ecosystem() {
595
+ return "gem";
596
+ }
597
+ async fetchPackage(name, signal) {
598
+ const url = `${this.baseURL}/api/v1/gems/${name}.json`;
599
+ try {
600
+ const data = await this.client.getJSON(url, signal);
601
+ const licenses = data.licenses ? combineLicenses(data.licenses.map((l) => normalizeLicense(l))) : "";
602
+ const repository = normalizeRepositoryURL(data.source_code_uri || data.metadata?.source_code_uri || data.homepage_uri || "");
603
+ return {
604
+ name: data.name,
605
+ description: data.description || "",
606
+ homepage: data.homepage_uri || "",
607
+ documentation: data.documentation_uri || "",
608
+ repository,
609
+ licenses,
610
+ keywords: [],
611
+ namespace: "",
612
+ latestVersion: data.version || "",
613
+ metadata: data.metadata || {}
614
+ };
615
+ } catch (error) {
616
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("gem", name);
617
+ throw error;
618
+ }
619
+ }
620
+ async fetchVersions(name, signal) {
621
+ const url = `${this.baseURL}/api/v1/versions/${name}.json`;
622
+ try {
623
+ const data = await this.client.getJSON(url, signal);
624
+ const versions = [];
625
+ for (const versionData of data) {
626
+ const publishedAt = versionData.created_at ? new Date(versionData.created_at) : null;
627
+ const status = versionData.yanked ? "yanked" : "";
628
+ versions.push({
629
+ number: versionData.number,
630
+ publishedAt,
631
+ licenses: "",
632
+ integrity: versionData.sha ? `sha256-${versionData.sha}` : "",
633
+ status,
634
+ metadata: {}
635
+ });
636
+ }
637
+ return versions;
638
+ } catch (error) {
639
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("gem", name);
640
+ throw error;
641
+ }
642
+ }
643
+ async fetchDependencies(name, version, signal) {
644
+ const url = `${this.baseURL}/api/v2/rubygems/${name}/versions/${version}.json`;
645
+ try {
646
+ const data = await this.client.getJSON(url, signal);
647
+ const dependencies = [];
648
+ if (data.dependencies?.runtime) for (const dep of data.dependencies.runtime) dependencies.push({
649
+ name: dep.name,
650
+ requirements: dep.requirements,
651
+ scope: "runtime",
652
+ optional: false
653
+ });
654
+ if (data.dependencies?.development) for (const dep of data.dependencies.development) dependencies.push({
655
+ name: dep.name,
656
+ requirements: dep.requirements,
657
+ scope: "development",
658
+ optional: false
659
+ });
660
+ return dependencies;
661
+ } catch (error) {
662
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("gem", name, version);
663
+ throw error;
664
+ }
665
+ }
666
+ async fetchMaintainers(name, signal) {
667
+ const url = `${this.baseURL}/api/v1/gems/${name}/owners.json`;
668
+ try {
669
+ const data = await this.client.getJSON(url, signal);
670
+ const maintainers = [];
671
+ for (const owner of data) maintainers.push({
672
+ uuid: "",
673
+ login: owner.handle,
674
+ name: owner.handle,
675
+ email: owner.email || "",
676
+ url: "",
677
+ role: ""
678
+ });
679
+ return maintainers;
680
+ } catch (error) {
681
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("gem", name);
682
+ throw error;
683
+ }
684
+ }
685
+ urls() {
686
+ return {
687
+ registry: (name, version) => {
688
+ const base = `https://rubygems.org/gems/${name}`;
689
+ return version ? `${base}/versions/${version}` : base;
690
+ },
691
+ download: (name, version) => {
692
+ return `https://rubygems.org/downloads/${name}-${version}.gem`;
693
+ },
694
+ documentation: (name, version) => {
695
+ return `https://www.rubydoc.info/gems/${name}${version ? `/${version}` : ""}`;
696
+ },
697
+ readme: (name, version) => {
698
+ const base = `https://rubygems.org/gems/${name}`;
699
+ return version ? `${base}/versions/${version}` : base;
700
+ },
701
+ purl: (name, version) => {
702
+ return buildPURL({
703
+ type: "gem",
704
+ name,
705
+ version
706
+ });
707
+ }
708
+ };
709
+ }
710
+ };
711
+ register("gem", "https://rubygems.org", RubyGemsRegistry);
712
+ /** Packagist registry client. */
713
+ var PackagistRegistry = class extends Registry {
714
+ constructor(baseURL, client) {
715
+ super();
716
+ this.baseURL = baseURL;
717
+ this.client = client;
718
+ }
719
+ baseURL;
720
+ client;
721
+ ecosystem() {
722
+ return "composer";
723
+ }
724
+ async fetchPackage(name, signal) {
725
+ const [vendor, pkg] = this.parseName(name);
726
+ const url = `${this.baseURL}/packages/${vendor}/${pkg}.json`;
727
+ try {
728
+ const packageData = (await this.client.getJSON(url, signal)).package;
729
+ const versions = Object.keys(packageData.versions);
730
+ const latestVersion = this.findLatestVersion(versions);
731
+ const latestVersionData = packageData.versions[latestVersion];
732
+ const licenses = this.extractLicenses(latestVersionData.license);
733
+ return {
734
+ name: packageData.name,
735
+ description: packageData.description || "",
736
+ homepage: "",
737
+ documentation: "",
738
+ repository: normalizeRepositoryURL(packageData.repository || latestVersionData.source?.url || ""),
739
+ licenses,
740
+ keywords: packageData.keywords || [],
741
+ namespace: vendor,
742
+ latestVersion,
743
+ metadata: {}
744
+ };
745
+ } catch (error) {
746
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("composer", name);
747
+ throw error;
748
+ }
749
+ }
750
+ async fetchVersions(name, signal) {
751
+ const [vendor, pkg] = this.parseName(name);
752
+ const url = `${this.baseURL}/packages/${vendor}/${pkg}.json`;
753
+ try {
754
+ const data = await this.client.getJSON(url, signal);
755
+ const versions = [];
756
+ for (const [versionStr, versionData] of Object.entries(data.package.versions)) {
757
+ const licenses = this.extractLicenses(versionData.license);
758
+ const publishedAt = versionData.time ? new Date(versionData.time) : null;
759
+ versions.push({
760
+ number: versionStr,
761
+ publishedAt,
762
+ licenses,
763
+ integrity: versionData.dist?.shasum ? `sha1-${versionData.dist.shasum}` : "",
764
+ status: "",
765
+ metadata: {}
766
+ });
767
+ }
768
+ return versions;
769
+ } catch (error) {
770
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("composer", name);
771
+ throw error;
772
+ }
773
+ }
774
+ async fetchDependencies(name, version, signal) {
775
+ const [vendor, pkg] = this.parseName(name);
776
+ const url = `${this.baseURL}/packages/${vendor}/${pkg}.json`;
777
+ try {
778
+ const versionData = (await this.client.getJSON(url, signal)).package.versions[version];
779
+ if (!versionData) throw new NotFoundError("composer", name, version);
780
+ const dependencies = [];
781
+ if (versionData.require) {
782
+ for (const [depName, requirements] of Object.entries(versionData.require)) if (!this.shouldSkipDependency(depName)) dependencies.push({
783
+ name: depName,
784
+ requirements,
785
+ scope: "runtime",
786
+ optional: false
787
+ });
788
+ }
789
+ if (versionData["require-dev"]) {
790
+ for (const [depName, requirements] of Object.entries(versionData["require-dev"])) if (!this.shouldSkipDependency(depName)) dependencies.push({
791
+ name: depName,
792
+ requirements,
793
+ scope: "development",
794
+ optional: false
795
+ });
796
+ }
797
+ return dependencies;
798
+ } catch (error) {
799
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("composer", name, version);
800
+ throw error;
801
+ }
802
+ }
803
+ async fetchMaintainers(name, signal) {
804
+ const [vendor, pkg] = this.parseName(name);
805
+ const url = `${this.baseURL}/packages/${vendor}/${pkg}.json`;
806
+ try {
807
+ const data = await this.client.getJSON(url, signal);
808
+ const maintainers = [];
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;
825
+ } catch (error) {
826
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("composer", name);
827
+ throw error;
828
+ }
829
+ }
830
+ urls() {
831
+ return {
832
+ registry: (name, version) => {
833
+ const [vendor, pkg] = this.parseName(name);
834
+ const base = `https://packagist.org/packages/${vendor}/${pkg}`;
835
+ return version ? `${base}#${version}` : base;
836
+ },
837
+ download: (name, version) => {
838
+ const [vendor, pkg] = this.parseName(name);
839
+ return `https://repo.packagist.org/p/${vendor}/${pkg}/${version}.json`;
840
+ },
841
+ documentation: (name, _version) => {
842
+ const [vendor, pkg] = this.parseName(name);
843
+ return `https://packagist.org/packages/${vendor}/${pkg}`;
844
+ },
845
+ readme: (name, _version) => {
846
+ const [vendor, pkg] = this.parseName(name);
847
+ return `https://packagist.org/packages/${vendor}/${pkg}`;
848
+ },
849
+ purl: (name, version) => {
850
+ const [vendor, pkg] = this.parseName(name);
851
+ return buildPURL({
852
+ type: "composer",
853
+ namespace: vendor,
854
+ name: pkg,
855
+ version
856
+ });
857
+ }
858
+ };
859
+ }
860
+ /** Parse "vendor/package" format. */
861
+ parseName(name) {
862
+ const parts = name.split("/");
863
+ if (parts.length !== 2 || !parts[0] || !parts[1]) throw new InvalidPURLError(`pkg:composer/${name}`, "invalid Composer package name, expected \"vendor/package\" format");
864
+ return [parts[0], parts[1]];
865
+ }
866
+ /** Find the highest non-dev version. */
867
+ findLatestVersion(versions) {
868
+ const stable = versions.filter((v) => !v.startsWith("dev-") && !v.endsWith("-dev"));
869
+ if (stable.length === 0) return versions[0] || "";
870
+ return stable.sort((a, b) => {
871
+ const aClean = a.replace(/^v/, "");
872
+ const bClean = b.replace(/^v/, "");
873
+ const aParts = aClean.split(".").map((p) => Number.parseInt(p, 10));
874
+ const bParts = bClean.split(".").map((p) => Number.parseInt(p, 10));
875
+ for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
876
+ const aPart = aParts[i] ?? 0;
877
+ const bPart = bParts[i] ?? 0;
878
+ if (aPart !== bPart) return bPart - aPart;
879
+ }
880
+ return 0;
881
+ })[0] || versions[0] || "";
882
+ }
883
+ /** Extract and normalize licenses. */
884
+ extractLicenses(raw) {
885
+ if (!raw) return "";
886
+ return combineLicenses(Array.isArray(raw) ? raw : [raw]);
887
+ }
888
+ /** Skip PHP and extension dependencies. */
889
+ shouldSkipDependency(name) {
890
+ return name === "php" || name.startsWith("ext-");
891
+ }
892
+ };
893
+ register("composer", "https://packagist.org", PackagistRegistry);
894
+ const AUR_BASE_URL = "https://aur.archlinux.org";
895
+ /** ALPM registry client — routes to Arch Linux official repos or AUR based on namespace. */
896
+ var AlpmRegistry = class extends Registry {
897
+ constructor(baseURL, client) {
898
+ super();
899
+ this.baseURL = baseURL;
900
+ this.client = client;
901
+ }
902
+ baseURL;
903
+ client;
904
+ ecosystem() {
905
+ return "alpm";
906
+ }
907
+ async fetchPackage(name, signal) {
908
+ const { namespace, pkgName } = this.parseName(name);
909
+ this.assertSupportedNamespace(namespace, name);
910
+ if (namespace === "aur") return this.fetchAurPackage(pkgName, signal);
911
+ return this.fetchOfficialPackage(pkgName, signal);
912
+ }
913
+ async fetchVersions(name, signal) {
914
+ const { namespace, pkgName } = this.parseName(name);
915
+ this.assertSupportedNamespace(namespace, name);
916
+ if (namespace === "aur") return this.fetchAurVersions(pkgName, signal);
917
+ return this.fetchOfficialVersions(pkgName, signal);
918
+ }
919
+ async fetchDependencies(name, version, signal) {
920
+ const { namespace, pkgName } = this.parseName(name);
921
+ this.assertSupportedNamespace(namespace, name);
922
+ if (namespace === "aur") return this.fetchAurDependencies(pkgName, version, signal);
923
+ return this.fetchOfficialDependencies(pkgName, version, signal);
924
+ }
925
+ async fetchMaintainers(name, signal) {
926
+ const { namespace, pkgName } = this.parseName(name);
927
+ this.assertSupportedNamespace(namespace, name);
928
+ if (namespace === "aur") return this.fetchAurMaintainers(pkgName, signal);
929
+ return this.fetchOfficialMaintainers(pkgName, signal);
930
+ }
931
+ urls() {
932
+ return {
933
+ registry: (name, _version) => {
934
+ const { namespace, pkgName } = this.parseName(name);
935
+ if (namespace === "aur") return `https://aur.archlinux.org/packages/${pkgName}`;
936
+ return `https://archlinux.org/packages/?name=${pkgName}`;
937
+ },
938
+ download: (name, _version) => {
939
+ const { namespace, pkgName } = this.parseName(name);
940
+ if (namespace === "aur") return `https://aur.archlinux.org/cgit/aur.git/snapshot/${pkgName}.tar.gz`;
941
+ return `https://archive.archlinux.org/packages/${pkgName.charAt(0)}/${pkgName}/`;
942
+ },
943
+ documentation: (name, _version) => {
944
+ const { namespace, pkgName } = this.parseName(name);
945
+ if (namespace === "aur") return `https://aur.archlinux.org/packages/${pkgName}`;
946
+ return `https://wiki.archlinux.org/title/${pkgName}`;
947
+ },
948
+ readme: (_name, _version) => {
949
+ return "";
950
+ },
951
+ purl: (name, version) => {
952
+ const { namespace, pkgName } = this.parseName(name);
953
+ return buildPURL({
954
+ type: "alpm",
955
+ namespace,
956
+ name: pkgName,
957
+ version
958
+ });
959
+ }
960
+ };
961
+ }
962
+ async fetchOfficialPackage(name, signal) {
963
+ const result = await this.searchOfficial(name, signal);
964
+ return {
965
+ name: result.pkgname,
966
+ description: result.pkgdesc || "",
967
+ homepage: result.url || "",
968
+ documentation: "",
969
+ repository: "",
970
+ licenses: combineLicenses(result.licenses),
971
+ keywords: result.groups,
972
+ namespace: "arch",
973
+ latestVersion: this.formatVersion(result),
974
+ metadata: {
975
+ repo: result.repo,
976
+ arch: result.arch,
977
+ pkgbase: result.pkgbase,
978
+ compressedSize: result.compressed_size,
979
+ installedSize: result.installed_size,
980
+ buildDate: result.build_date,
981
+ lastUpdate: result.last_update,
982
+ flagDate: result.flag_date,
983
+ packager: result.packager,
984
+ provides: result.provides,
985
+ conflicts: result.conflicts,
986
+ replaces: result.replaces
987
+ }
988
+ };
989
+ }
990
+ async fetchOfficialVersions(name, signal) {
991
+ const result = await this.searchOfficial(name, signal);
992
+ return [{
993
+ number: this.formatVersion(result),
994
+ publishedAt: result.last_update ? new Date(result.last_update) : null,
995
+ licenses: combineLicenses(result.licenses),
996
+ integrity: "",
997
+ status: result.flag_date ? "deprecated" : "",
998
+ metadata: {
999
+ repo: result.repo,
1000
+ arch: result.arch
1001
+ }
1002
+ }];
1003
+ }
1004
+ async fetchOfficialDependencies(name, version, signal) {
1005
+ const result = await this.searchOfficial(name, signal);
1006
+ const currentVersion = this.formatVersion(result);
1007
+ this.assertVersionMatches(name, version, currentVersion);
1008
+ return this.buildDependencies(result.depends, result.makedepends, result.optdepends, result.checkdepends);
1009
+ }
1010
+ async fetchOfficialMaintainers(name, signal) {
1011
+ return (await this.searchOfficial(name, signal)).maintainers.map((m) => ({
1012
+ uuid: "",
1013
+ login: m,
1014
+ name: m,
1015
+ email: "",
1016
+ url: "",
1017
+ role: "maintainer"
1018
+ }));
1019
+ }
1020
+ /** Search official repos by exact package name. Prefers x86_64 results. */
1021
+ async searchOfficial(name, signal) {
1022
+ const url = `${this.baseURL}/packages/search/json/?name=${encodeURIComponent(name)}`;
1023
+ try {
1024
+ const data = await this.client.getJSON(url, signal);
1025
+ if (!data.results || data.results.length === 0) throw new NotFoundError("alpm", name);
1026
+ return data.results.find((r) => r.arch === "x86_64") || data.results[0];
1027
+ } catch (error) {
1028
+ if (error instanceof NotFoundError) throw error;
1029
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("alpm", name);
1030
+ throw error;
1031
+ }
1032
+ }
1033
+ async fetchAurPackage(name, signal) {
1034
+ const result = await this.fetchAurInfo(name, signal);
1035
+ return {
1036
+ name: result.Name,
1037
+ description: result.Description || "",
1038
+ homepage: result.URL || "",
1039
+ documentation: "",
1040
+ repository: "",
1041
+ licenses: combineLicenses(result.License || []),
1042
+ keywords: result.Keywords || [],
1043
+ namespace: "aur",
1044
+ latestVersion: result.Version,
1045
+ metadata: {
1046
+ votes: result.NumVotes,
1047
+ popularity: result.Popularity,
1048
+ outOfDate: result.OutOfDate,
1049
+ pkgbase: result.PackageBase,
1050
+ firstSubmitted: result.FirstSubmitted,
1051
+ lastModified: result.LastModified
1052
+ }
1053
+ };
1054
+ }
1055
+ async fetchAurVersions(name, signal) {
1056
+ const result = await this.fetchAurInfo(name, signal);
1057
+ return [{
1058
+ number: result.Version,
1059
+ publishedAt: result.LastModified ? /* @__PURE__ */ new Date(result.LastModified * 1e3) : null,
1060
+ licenses: combineLicenses(result.License || []),
1061
+ integrity: "",
1062
+ status: result.OutOfDate ? "deprecated" : "",
1063
+ metadata: {
1064
+ votes: result.NumVotes,
1065
+ popularity: result.Popularity
1066
+ }
1067
+ }];
1068
+ }
1069
+ async fetchAurDependencies(name, version, signal) {
1070
+ const result = await this.fetchAurInfo(name, signal);
1071
+ this.assertVersionMatches(`aur/${name}`, version, result.Version);
1072
+ return this.buildDependencies(result.Depends || [], result.MakeDepends || [], result.OptDepends || [], result.CheckDepends || []);
1073
+ }
1074
+ async fetchAurMaintainers(name, signal) {
1075
+ const result = await this.fetchAurInfo(name, signal);
1076
+ if (!result.Maintainer) return [];
1077
+ return [{
1078
+ uuid: "",
1079
+ login: result.Maintainer,
1080
+ name: result.Maintainer,
1081
+ email: "",
1082
+ url: "",
1083
+ role: "maintainer"
1084
+ }];
1085
+ }
1086
+ /** Fetch package info from AUR RPC v5. */
1087
+ async fetchAurInfo(name, signal) {
1088
+ const url = `${AUR_BASE_URL}/rpc/v5/info/${encodeURIComponent(name)}`;
1089
+ try {
1090
+ const data = await this.client.getJSON(url, signal);
1091
+ if (!data.results || data.results.length === 0) throw new NotFoundError("alpm", `aur/${name}`);
1092
+ return data.results[0];
1093
+ } catch (error) {
1094
+ if (error instanceof NotFoundError) throw error;
1095
+ if (error instanceof HTTPError && error.isNotFound()) throw new NotFoundError("alpm", `aur/${name}`);
1096
+ throw error;
1097
+ }
1098
+ }
1099
+ /** Parse "namespace/name" into components. Defaults to "arch" namespace. */
1100
+ parseName(fullName) {
1101
+ const normalized = fullName.trim().toLowerCase();
1102
+ const slashIdx = normalized.indexOf("/");
1103
+ if (slashIdx === -1) return {
1104
+ namespace: "arch",
1105
+ pkgName: normalized
1106
+ };
1107
+ return {
1108
+ namespace: normalized.slice(0, slashIdx),
1109
+ pkgName: normalized.slice(slashIdx + 1)
1110
+ };
1111
+ }
1112
+ assertSupportedNamespace(namespace, requestedName) {
1113
+ if (namespace !== "arch" && namespace !== "aur") throw new NotFoundError("alpm", requestedName);
1114
+ }
1115
+ assertVersionMatches(name, requested, current) {
1116
+ if (requested && requested !== current) throw new NotFoundError("alpm", name, requested);
1117
+ }
1118
+ /** Format ALPM version: epoch:pkgver-pkgrel (epoch omitted when 0). */
1119
+ formatVersion(result) {
1120
+ return `${result.epoch && result.epoch > 0 ? `${result.epoch}:` : ""}${result.pkgver}-${result.pkgrel}`;
1121
+ }
1122
+ /** Build normalized dependency list from ALPM dependency arrays. */
1123
+ buildDependencies(depends, makedepends, optdepends, checkdepends) {
1124
+ const dependencies = [];
1125
+ for (const dep of depends) {
1126
+ const parsed = this.parseDep(dep);
1127
+ dependencies.push({
1128
+ name: parsed.name,
1129
+ requirements: parsed.requirements,
1130
+ scope: "runtime",
1131
+ optional: false
1132
+ });
1133
+ }
1134
+ for (const dep of makedepends) {
1135
+ const parsed = this.parseDep(dep);
1136
+ dependencies.push({
1137
+ name: parsed.name,
1138
+ requirements: parsed.requirements,
1139
+ scope: "build",
1140
+ optional: false
1141
+ });
1142
+ }
1143
+ for (const dep of optdepends) {
1144
+ const parsed = this.parseOptDep(dep);
1145
+ dependencies.push({
1146
+ name: parsed.name,
1147
+ requirements: parsed.requirements,
1148
+ scope: "optional",
1149
+ optional: true
1150
+ });
1151
+ }
1152
+ for (const dep of checkdepends) {
1153
+ const parsed = this.parseDep(dep);
1154
+ dependencies.push({
1155
+ name: parsed.name,
1156
+ requirements: parsed.requirements,
1157
+ scope: "test",
1158
+ optional: false
1159
+ });
1160
+ }
1161
+ return dependencies;
1162
+ }
1163
+ /** Parse a dependency string like "glibc>=2.33" or "bash". */
1164
+ parseDep(dep) {
1165
+ const match = dep.match(/^([a-zA-Z0-9@._+-]+)(.*)$/);
1166
+ if (!match) return {
1167
+ name: dep,
1168
+ requirements: ""
1169
+ };
1170
+ return {
1171
+ name: match[1],
1172
+ requirements: match[2].trim()
1173
+ };
1174
+ }
1175
+ /** Parse optional dependency like "perl-locale-gettext: translation support". */
1176
+ parseOptDep(dep) {
1177
+ const colonIdx = dep.indexOf(":");
1178
+ if (colonIdx === -1) {
1179
+ const parsed = this.parseDep(dep.trim());
1180
+ return {
1181
+ name: parsed.name,
1182
+ requirements: parsed.requirements,
1183
+ description: ""
1184
+ };
1185
+ }
1186
+ const parsed = this.parseDep(dep.slice(0, colonIdx).trim());
1187
+ return {
1188
+ name: parsed.name,
1189
+ requirements: parsed.requirements,
1190
+ description: dep.slice(colonIdx + 1).trim()
1191
+ };
1192
+ }
1193
+ };
1194
+ 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 };
1196
+
1197
+ //# sourceMappingURL=registries.mjs.map