(names: string[])
| 89 | |
| 90 | // Fetch function - only fetches packages not already in cache |
| 91 | async function fetchPackages(names: string[]) { |
| 92 | if (names.length === 0) { |
| 93 | status.value = 'idle' |
| 94 | return |
| 95 | } |
| 96 | |
| 97 | // Handle "no dependency" column - add to cache immediately |
| 98 | if (names.includes(NO_DEPENDENCY_ID) && !cache.value.has(NO_DEPENDENCY_ID)) { |
| 99 | const newCache = new Map(cache.value) |
| 100 | newCache.set(NO_DEPENDENCY_ID, createNoDependencyData()) |
| 101 | cache.value = newCache |
| 102 | } |
| 103 | |
| 104 | // Only fetch packages not already cached (excluding "no dep" which has no remote data) |
| 105 | const namesToFetch = names.filter(name => name !== NO_DEPENDENCY_ID && !cache.value.has(name)) |
| 106 | |
| 107 | if (namesToFetch.length === 0) { |
| 108 | status.value = 'success' |
| 109 | return |
| 110 | } |
| 111 | |
| 112 | status.value = 'pending' |
| 113 | error.value = null |
| 114 | |
| 115 | // Mark packages as loading |
| 116 | loadingPackages.value = new Set(namesToFetch) |
| 117 | |
| 118 | try { |
| 119 | // First pass: fetch fast data (package info, downloads, analysis, vulns) |
| 120 | const results = await Promise.all( |
| 121 | namesToFetch.map(async (name): Promise<PackageComparisonData | null> => { |
| 122 | try { |
| 123 | // Fetch basic package info first (required) |
| 124 | const { data: pkgData } = await $npmRegistry<Packument>(`/${encodePackageName(name)}`) |
| 125 | const latestVersion = pkgData['dist-tags']?.latest |
| 126 | if (!latestVersion) return null |
| 127 | |
| 128 | // Fetch fast additional data in parallel (optional - failures are ok) |
| 129 | const repoInfo = parseRepositoryInfo(pkgData.repository) |
| 130 | const isGitHub = repoInfo?.provider === 'github' |
| 131 | const [downloads, analysis, vulns, likes, ghStars, ghIssues] = await Promise.all([ |
| 132 | $fetch<{ downloads: number }>( |
| 133 | `https://api.npmjs.org/downloads/point/last-week/${encodePackageName(name)}`, |
| 134 | ).catch(() => null), |
| 135 | $fetch<PackageAnalysisResponse>( |
| 136 | `/api/registry/analysis/${encodePackageName(name)}`, |
| 137 | ).catch(() => null), |
| 138 | $fetch<VulnerabilityTreeResult>( |
| 139 | `/api/registry/vulnerabilities/${encodePackageName(name)}`, |
| 140 | ).catch(() => null), |
| 141 | $fetch<PackageLikes>(`/api/social/likes/${encodePackageName(name)}`).catch( |
| 142 | () => null, |
| 143 | ), |
| 144 | isGitHub |
| 145 | ? $fetch<{ repo: { stars: number } }>( |
| 146 | `https://ungh.cc/repos/${repoInfo.owner}/${repoInfo.repo}`, |
| 147 | ) |
| 148 | .then(res => (typeof res?.repo?.stars === 'number' ? res.repo.stars : null)) |
no test coverage detected