■ ■ ■ ■ ■ ■
packages/worker/src/npmRegistry/api.ts
1 | 1 | | import nano from 'nano'; |
| 2 | + | import { createHash } from 'crypto'; |
2 | 3 | | import fetch from 'node-fetch'; |
3 | 4 | | |
4 | 5 | | // We use the relicate API since the `registry.npmjs.com` |
5 | 6 | | // does not support `local_seq` parameter and the `/changes` endpoint. |
6 | 7 | | const REGISTRY_URL = 'https://replicate.npmjs.com'; |
7 | 8 | | const NPM_API_ORIGIN = 'https://api.npmjs.org'; |
| 9 | + | const makeAvatarUrl = (email: string) => |
| 10 | + | `https://s.gravatar.com/avatar/${createHash('md5').update(email).digest('hex')}`; |
8 | 11 | | |
9 | 12 | | type DocumentScope = { |
10 | 13 | | description?: string; |
| 14 | + | readme?: string; |
| 15 | + | maintainers?: Array<{ name: string; email: string }>; |
| 16 | + | keywords?: string[]; |
11 | 17 | | license?: string; |
12 | 18 | | homepage?: string; |
13 | 19 | | repository?: |
| skipped 11 lines |
25 | 31 | | 'dist-tags': { |
26 | 32 | | latest: string; |
27 | 33 | | }; |
28 | | - | versions: Record<string, Record<string, unknown>>; |
| 34 | + | versions?: Record< |
| 35 | + | string, |
| 36 | + | { |
| 37 | + | dependencies?: Record<string, string>; |
| 38 | + | peerDependencies?: Record<string, string>; |
| 39 | + | dist: { |
| 40 | + | unpackedSize?: number; |
| 41 | + | }; |
| 42 | + | } |
| 43 | + | >; |
29 | 44 | | }; |
30 | 45 | | |
31 | 46 | | const registry = nano(REGISTRY_URL).scope<DocumentScope>('registry'); |
32 | 47 | | |
33 | 48 | | export async function fetchPackageMetadata(name: string) { |
34 | 49 | | const document = await registry.get(name, { local_seq: true }); |
35 | | - | const versionList = Object.keys(document.versions); |
| 50 | + | const versionList = Object.keys(document.versions ?? {}); |
36 | 51 | | |
37 | 52 | | let homepageUrl, repositoryUrl; |
38 | 53 | | |
| skipped 16 lines |
55 | 70 | | // ); |
56 | 71 | | |
57 | 72 | | return { |
58 | | - | description: document.description ?? undefined, |
59 | | - | license: document.license ?? undefined, |
| 73 | + | description: document.description, |
| 74 | + | fullDescription: document.readme, |
| 75 | + | maintainers: (document.maintainers ?? []).map((author) => ({ |
| 76 | + | name: author.name, |
| 77 | + | email: author.email, |
| 78 | + | avatar: makeAvatarUrl(author.email), |
| 79 | + | })), |
| 80 | + | keywords: document.keywords ?? [], |
| 81 | + | license: document.license, |
60 | 82 | | homepageUrl, |
61 | 83 | | repositoryUrl, |
62 | 84 | | updateSeq: Number(document._local_seq), |
63 | 85 | | updatedAt: new Date(document.time.modified), |
64 | 86 | | latestVersion: document['dist-tags'].latest, |
65 | | - | versionList, |
| 87 | + | versionSpecificValues: versionList.reduce((acc, el) => { |
| 88 | + | acc[el] = { |
| 89 | + | dependencies: { |
| 90 | + | ...(document.versions?.[el].dependencies ?? {}), |
| 91 | + | ...(document.versions?.[el].peerDependencies ?? {}), |
| 92 | + | }, |
| 93 | + | unpackedSize: document.versions?.[el].dist.unpackedSize, |
| 94 | + | }; |
| 95 | + | return acc; |
| 96 | + | }, {} as Record<string, { dependencies: Record<string, string>; unpackedSize?: number }>), |
66 | 97 | | }; |
67 | 98 | | } |
68 | 99 | | |
| skipped 76 lines |