Skip to content

Commit 73c4d84

Browse files
committed
fix: correct npm search data transferring server to client
1 parent 552240b commit 73c4d84

File tree

1 file changed

+115
-110
lines changed

1 file changed

+115
-110
lines changed

app/composables/npm/useSearch.ts

Lines changed: 115 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export function useSearch(
5252
const suggestionsLoading = shallowRef(false)
5353
const packageAvailability = shallowRef<{ name: string; available: boolean } | null>(null)
5454
const existenceCache = shallowRef<Record<string, boolean>>({})
55-
let suggestionRequestId = 0
55+
const suggestionRequestId = shallowRef(0)
5656

5757
/**
5858
* Determine which extra checks to include in the Algolia multi-search.
@@ -225,23 +225,6 @@ export function useSearch(
225225
{ default: emptySearchPayload },
226226
)
227227

228-
watch(
229-
[() => asyncData.data.value.suggestions, () => suggestions.value],
230-
([payloadSuggestions, localSuggestions]) => {
231-
if (!payloadSuggestions.length && !localSuggestions.length) return
232-
suggestions.value = payloadSuggestions.length ? payloadSuggestions : localSuggestions
233-
},
234-
{ immediate: true },
235-
)
236-
237-
watch(
238-
[() => asyncData.data.value?.packageAvailability, () => packageAvailability.value],
239-
([payloadPackageAvailability, localPackageAvailability]) => {
240-
packageAvailability.value = payloadPackageAvailability || localPackageAvailability
241-
},
242-
{ immediate: true },
243-
)
244-
245228
async function fetchMore(targetSize: number): Promise<void> {
246229
const q = toValue(query).trim()
247230
const provider = searchProvider.value
@@ -346,118 +329,140 @@ export function useSearch(
346329
return asyncData.data.value?.searchResponse ?? null
347330
})
348331

349-
if (import.meta.client && asyncData.data.value?.searchResponse.isStale) {
350-
onMounted(() => {
351-
asyncData.refresh()
352-
})
353-
}
354-
355332
const hasMore = computed(() => {
356333
if (!cache.value) return true
357334
return cache.value.objects.length < cache.value.total
358335
})
359336

360-
// npm suggestion checking (Algolia handles suggestions inside the search handler above)
361-
if (config.suggestions) {
362-
async function validateSuggestionsNpm(q: string) {
363-
const requestId = ++suggestionRequestId
364-
const { intent, name } = parseSuggestionIntent(q)
365-
366-
const trimmed = q.trim()
367-
if (isValidNewPackageName(trimmed)) {
368-
checkPackageExists(trimmed)
369-
.then(exists => {
370-
if (trimmed === toValue(query).trim()) {
371-
packageAvailability.value = { name: trimmed, available: !exists }
372-
}
373-
})
374-
.catch(() => {
375-
packageAvailability.value = null
376-
})
377-
} else {
378-
packageAvailability.value = null
379-
}
337+
async function validateSuggestionsNpm(q: string) {
338+
const requestId = ++suggestionRequestId.value
339+
const { intent, name } = parseSuggestionIntent(q)
340+
let availability: { name: string; available: boolean } | null = null
380341

381-
if (!intent || !name) {
382-
suggestions.value = []
383-
suggestionsLoading.value = false
384-
return
385-
}
342+
const trimmed = q.trim()
343+
if (isValidNewPackageName(trimmed)) {
344+
checkPackageExists(trimmed)
345+
.then(exists => {
346+
if (trimmed === toValue(query).trim()) {
347+
availability = { name: trimmed, available: !exists }
348+
packageAvailability.value = availability
349+
}
350+
})
351+
.catch(() => {
352+
availability = null
353+
})
354+
} else {
355+
availability = null
356+
}
386357

387-
suggestionsLoading.value = true
388-
const result: SearchSuggestion[] = []
389-
const lowerName = name.toLowerCase()
358+
if (!intent || !name) {
359+
suggestionsLoading.value = false
360+
return { suggestions: [], packageAvailability: availability }
361+
}
390362

391-
try {
392-
const wantOrg = intent === 'org' || intent === 'both'
393-
const wantUser = intent === 'user' || intent === 'both'
394-
395-
const promises: Promise<void>[] = []
396-
397-
if (wantOrg && existenceCache.value[`org:${lowerName}`] === undefined) {
398-
promises.push(
399-
checkOrgNpm(name)
400-
.then(exists => {
401-
existenceCache.value = { ...existenceCache.value, [`org:${lowerName}`]: exists }
402-
})
403-
.catch(() => {
404-
existenceCache.value = { ...existenceCache.value, [`org:${lowerName}`]: false }
405-
}),
406-
)
407-
}
363+
suggestionsLoading.value = true
364+
const result: SearchSuggestion[] = []
365+
const lowerName = name.toLowerCase()
408366

409-
if (wantUser && existenceCache.value[`user:${lowerName}`] === undefined) {
410-
promises.push(
411-
checkUserNpm(name)
412-
.then(exists => {
413-
existenceCache.value = { ...existenceCache.value, [`user:${lowerName}`]: exists }
414-
})
415-
.catch(() => {
416-
existenceCache.value = { ...existenceCache.value, [`user:${lowerName}`]: false }
417-
}),
418-
)
419-
}
367+
try {
368+
const wantOrg = intent === 'org' || intent === 'both'
369+
const wantUser = intent === 'user' || intent === 'both'
420370

421-
if (promises.length > 0) {
422-
await Promise.all(promises)
423-
}
371+
const promises: Promise<void>[] = []
424372

425-
if (requestId !== suggestionRequestId) return
373+
if (wantOrg && existenceCache.value[`org:${lowerName}`] === undefined) {
374+
promises.push(
375+
checkOrgNpm(name)
376+
.then(exists => {
377+
existenceCache.value = { ...existenceCache.value, [`org:${lowerName}`]: exists }
378+
})
379+
.catch(() => {
380+
existenceCache.value = { ...existenceCache.value, [`org:${lowerName}`]: false }
381+
}),
382+
)
383+
}
426384

427-
const isOrg = wantOrg && existenceCache.value[`org:${lowerName}`]
428-
const isUser = wantUser && existenceCache.value[`user:${lowerName}`]
385+
if (wantUser && existenceCache.value[`user:${lowerName}`] === undefined) {
386+
promises.push(
387+
checkUserNpm(name)
388+
.then(exists => {
389+
existenceCache.value = { ...existenceCache.value, [`user:${lowerName}`]: exists }
390+
})
391+
.catch(() => {
392+
existenceCache.value = { ...existenceCache.value, [`user:${lowerName}`]: false }
393+
}),
394+
)
395+
}
429396

430-
if (isOrg) {
431-
result.push({ type: 'org', name, exists: true })
432-
}
433-
if (isUser && !isOrg) {
434-
result.push({ type: 'user', name, exists: true })
435-
}
436-
} finally {
437-
if (requestId === suggestionRequestId) {
438-
suggestionsLoading.value = false
439-
}
397+
if (promises.length > 0) {
398+
await Promise.all(promises)
440399
}
441400

442-
if (requestId === suggestionRequestId) {
443-
suggestions.value = result
401+
if (requestId !== suggestionRequestId.value)
402+
return { suggestions: [], packageAvailability: availability }
403+
404+
const isOrg = wantOrg && existenceCache.value[`org:${lowerName}`]
405+
const isUser = wantUser && existenceCache.value[`user:${lowerName}`]
406+
407+
if (isOrg) {
408+
result.push({ type: 'org', name, exists: true })
409+
}
410+
if (isUser && !isOrg) {
411+
result.push({ type: 'user', name, exists: true })
412+
}
413+
} finally {
414+
if (requestId === suggestionRequestId.value) {
415+
suggestionsLoading.value = false
444416
}
445417
}
446418

447-
watch(
448-
() => toValue(query),
449-
q => {
450-
if (searchProvider.value !== 'algolia') {
451-
validateSuggestionsNpm(q)
452-
}
453-
},
454-
{ immediate: true },
455-
)
419+
if (requestId === suggestionRequestId.value) {
420+
suggestions.value = result
421+
return { suggestions: result, packageAvailability: availability }
422+
}
423+
424+
return { suggestions: [], packageAvailability: availability }
425+
}
426+
427+
const npmSuggestions = useLazyAsyncData(
428+
() => `npm-suggestions:${searchProvider.value}:${toValue(query)}`,
429+
async () => {
430+
const q = toValue(query).trim()
431+
if (searchProvider.value === 'algolia' || !q)
432+
return { suggestions: [], packageAvailability: null }
433+
const { intent, name } = parseSuggestionIntent(q)
434+
if (!intent || !name) return { suggestions: [], packageAvailability: null }
435+
return validateSuggestionsNpm(q)
436+
},
437+
{ default: () => ({ suggestions: [], packageAvailability: null }) },
438+
)
456439

457-
watch(searchProvider, () => {
458-
if (searchProvider.value !== 'algolia') {
459-
validateSuggestionsNpm(toValue(query))
440+
watch(
441+
[() => asyncData.data.value.suggestions, () => npmSuggestions.data.value.suggestions],
442+
([algoliaSuggestions, npmSuggestions]) => {
443+
if (algoliaSuggestions.length || npmSuggestions.length) {
444+
suggestions.value = algoliaSuggestions.length ? algoliaSuggestions : npmSuggestions
460445
}
446+
},
447+
{ immediate: true },
448+
)
449+
450+
watch(
451+
[
452+
() => asyncData.data.value?.packageAvailability,
453+
() => npmSuggestions.data.value.packageAvailability,
454+
],
455+
([algoliaPackageAvailability, npmPackageAvailability]) => {
456+
if (algoliaPackageAvailability || npmPackageAvailability) {
457+
packageAvailability.value = algoliaPackageAvailability || npmPackageAvailability
458+
}
459+
},
460+
{ immediate: true },
461+
)
462+
463+
if (import.meta.client && asyncData.data.value?.searchResponse.isStale) {
464+
onMounted(() => {
465+
asyncData.refresh()
461466
})
462467
}
463468

0 commit comments

Comments
 (0)