|
1 | | -import type { Packument } from '#shared/types' |
| 1 | +import type { Packument, NpmSearchResponse } from '#shared/types' |
2 | 2 | import { encodePackageName, fetchLatestVersion } from '#shared/utils/npm' |
3 | 3 | import { maxSatisfying, prerelease } from 'semver' |
4 | 4 | import { CACHE_MAX_AGE_FIVE_MINUTES } from '#shared/utils/constants' |
@@ -99,3 +99,43 @@ export async function resolveDependencyVersions( |
99 | 99 | } |
100 | 100 | return resolved |
101 | 101 | } |
| 102 | + |
| 103 | +/** |
| 104 | + * Find a user's email address from its username |
| 105 | + * by exploring metadata in its public packages |
| 106 | + */ |
| 107 | +export const fetchUserEmail = defineCachedFunction( |
| 108 | + async (username: string): Promise<string | null> => { |
| 109 | + const handle = username.trim() |
| 110 | + if (!handle) return null |
| 111 | + |
| 112 | + // Fetch packages with the user's handle as a maintainer |
| 113 | + const params = new URLSearchParams({ |
| 114 | + text: `maintainer:${handle}`, |
| 115 | + size: '20', |
| 116 | + }) |
| 117 | + const response = await $fetch<NpmSearchResponse>(`${NPM_REGISTRY}/-/v1/search?${params}`) |
| 118 | + const lowerHandle = handle.toLowerCase() |
| 119 | + |
| 120 | + // Search for the user's email in packages metadata |
| 121 | + for (const result of response.objects) { |
| 122 | + const maintainers = result.package.maintainers ?? [] |
| 123 | + const match = maintainers.find( |
| 124 | + person => |
| 125 | + person.username?.toLowerCase() === lowerHandle || |
| 126 | + person.name?.toLowerCase() === lowerHandle, |
| 127 | + ) |
| 128 | + if (match?.email) { |
| 129 | + return match.email |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + return null |
| 134 | + }, |
| 135 | + { |
| 136 | + maxAge: CACHE_MAX_AGE_ONE_DAY, |
| 137 | + swr: true, |
| 138 | + name: 'npm-user-email', |
| 139 | + getKey: (username: string) => `npm-user-email:${username.trim().toLowerCase()}`, |
| 140 | + }, |
| 141 | +) |
0 commit comments