Skip to content

Commit 609b48f

Browse files
committed
fix: use dedicated useUserPackages composable with correct 404 handling
1 parent 35df41e commit 609b48f

File tree

2 files changed

+97
-2
lines changed

2 files changed

+97
-2
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* Fetch all packages for a given npm user.
3+
*
4+
* Mirrors {@link useOrgPackages} — both use the same npm registry endpoint
5+
* (`/-/org/<name>/package`) which accepts usernames and org names alike.
6+
* The only difference: unknown users return an empty list instead of a 404.
7+
*/
8+
export function useUserPackages(username: MaybeRefOrGetter<string>) {
9+
const route = useRoute()
10+
const { searchProvider } = useSearchProvider()
11+
const searchProviderValue = computed(() => {
12+
const p = normalizeSearchParam(route.query.p)
13+
if (p === 'npm' || searchProvider.value === 'npm') return 'npm'
14+
return 'algolia'
15+
})
16+
const { getPackagesByName } = useAlgoliaSearch()
17+
18+
const asyncData = useLazyAsyncData(
19+
() => `user-packages:${searchProviderValue.value}:${toValue(username)}`,
20+
async (_nuxtApp, { signal }) => {
21+
const user = toValue(username)
22+
if (!user) {
23+
return emptySearchResponse()
24+
}
25+
26+
let packageNames: string[]
27+
try {
28+
const { packages } = await $fetch<{ packages: string[]; count: number }>(
29+
`/api/registry/org/${encodeURIComponent(user)}/packages`,
30+
{ signal },
31+
)
32+
packageNames = packages
33+
} catch {
34+
// Unknown user or network error — show empty state, not a 404
35+
return emptySearchResponse()
36+
}
37+
38+
if (user !== toValue(username)) {
39+
return emptySearchResponse()
40+
}
41+
42+
if (packageNames.length === 0) {
43+
return emptySearchResponse()
44+
}
45+
46+
if (searchProviderValue.value === 'algolia') {
47+
try {
48+
const response = await getPackagesByName(packageNames)
49+
if (user !== toValue(username)) {
50+
return emptySearchResponse()
51+
}
52+
if (response.objects.length > 0) {
53+
return response
54+
}
55+
} catch {
56+
// Fall through to npm registry path
57+
}
58+
}
59+
60+
const metaResults = await mapWithConcurrency(
61+
packageNames,
62+
async name => {
63+
try {
64+
return await $fetch<PackageMetaResponse>(
65+
`/api/registry/package-meta/${encodePackageName(name)}`,
66+
{ signal },
67+
)
68+
} catch {
69+
return null
70+
}
71+
},
72+
10,
73+
)
74+
75+
if (user !== toValue(username)) {
76+
return emptySearchResponse()
77+
}
78+
79+
const results: NpmSearchResult[] = metaResults
80+
.filter((meta): meta is PackageMetaResponse => meta !== null)
81+
.map(metaToSearchResult)
82+
83+
return {
84+
isStale: false,
85+
objects: results,
86+
total: results.length,
87+
time: new Date().toISOString(),
88+
} satisfies NpmSearchResponse
89+
},
90+
{ default: emptySearchResponse },
91+
)
92+
93+
return asyncData
94+
}

app/pages/~[username]/index.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ watch([filterText, sortOption], ([filter, sort]) => {
3737
debouncedUpdateUrl(filter, sort)
3838
})
3939
40-
// Fetch packages — reuses the org endpoint which accepts usernames too
41-
const { data: results, status, error } = useOrgPackages(username)
40+
// Fetch packages from npm registry (same endpoint as org page, but
41+
// unknown users get empty results instead of a 404 error page)
42+
const { data: results, status, error } = useUserPackages(username)
4243
4344
// Get initial page from URL (for scroll restoration on reload)
4445
const initialPage = computed(() => {

0 commit comments

Comments
 (0)