Skip to content

Commit 0aa6f03

Browse files
committed
fix: speed up loading of org/user suggestions
1 parent 8296a24 commit 0aa6f03

File tree

1 file changed

+49
-17
lines changed

1 file changed

+49
-17
lines changed

app/pages/search.vue

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -316,14 +316,13 @@ interface ValidatedSuggestion {
316316
/** Cache for existence checks to avoid repeated API calls */
317317
const existenceCache = ref<Record<string, boolean | 'pending'>>({})
318318
319-
interface NpmSearchResponse {
320-
total: number
321-
objects: Array<{ package: { name: string } }>
322-
}
319+
const { search: algoliaSearch } = useAlgoliaSearch()
320+
const { isAlgolia } = useSearchProvider()
323321
324322
/**
325-
* Check if an org exists by searching for packages with @orgname scope
326-
* Uses the search API which has CORS enabled
323+
* Check if an org exists by searching for scoped packages (@orgname/...).
324+
* When Algolia is active, searches for `@name/` scoped packages via text query.
325+
* Falls back to npm registry search API otherwise.
327326
*/
328327
async function checkOrgExists(name: string): Promise<boolean> {
329328
const cacheKey = `org:${name.toLowerCase()}`
@@ -333,12 +332,24 @@ async function checkOrgExists(name: string): Promise<boolean> {
333332
}
334333
existenceCache.value[cacheKey] = 'pending'
335334
try {
336-
// Search for packages in the @org scope
337-
const response = await $fetch<NpmSearchResponse>(`${NPM_REGISTRY}/-/v1/search`, {
338-
query: { text: `@${name}`, size: 5 },
339-
})
340-
// Verify at least one result actually starts with @orgname/
341335
const scopePrefix = `@${name.toLowerCase()}/`
336+
337+
if (isAlgolia.value) {
338+
// Algolia: search for scoped packages — use the scope as a text query
339+
// and verify a result actually starts with @name/
340+
const response = await algoliaSearch(`@${name}`, { size: 5 })
341+
const exists = response.objects.some(obj =>
342+
obj.package.name.toLowerCase().startsWith(scopePrefix),
343+
)
344+
existenceCache.value[cacheKey] = exists
345+
return exists
346+
}
347+
348+
// npm registry: search for packages in the @org scope
349+
const response = await $fetch<{ total: number; objects: Array<{ package: { name: string } }> }>(
350+
`${NPM_REGISTRY}/-/v1/search`,
351+
{ query: { text: `@${name}`, size: 5 } },
352+
)
342353
const exists = response.objects.some(obj =>
343354
obj.package.name.toLowerCase().startsWith(scopePrefix),
344355
)
@@ -351,8 +362,10 @@ async function checkOrgExists(name: string): Promise<boolean> {
351362
}
352363
353364
/**
354-
* Check if a user exists by searching for packages they maintain
355-
* Uses the search API which has CORS enabled
365+
* Check if a user exists by searching for packages they maintain.
366+
* Always uses the npm registry `maintainer:` search because Algolia's
367+
* `owner.name` field represents the org/account, not individual maintainers,
368+
* and cannot reliably distinguish users from orgs.
356369
*/
357370
async function checkUserExists(name: string): Promise<boolean> {
358371
const cacheKey = `user:${name.toLowerCase()}`
@@ -418,8 +431,8 @@ const parsedQuery = computed<ParsedQuery>(() => {
418431
const validatedSuggestions = ref<ValidatedSuggestion[]>([])
419432
const suggestionsLoading = shallowRef(false)
420433
421-
/** Debounced function to validate suggestions */
422-
const validateSuggestions = debounce(async (parsed: ParsedQuery) => {
434+
/** Validate suggestions (check org/user existence) */
435+
async function validateSuggestionsImpl(parsed: ParsedQuery) {
423436
if (!parsed.type || !parsed.name) {
424437
validatedSuggestions.value = []
425438
return
@@ -458,17 +471,36 @@ const validateSuggestions = debounce(async (parsed: ParsedQuery) => {
458471
}
459472
460473
validatedSuggestions.value = suggestions
461-
}, 200)
474+
}
475+
476+
// Debounce lightly for npm (extra API calls are slower), skip debounce for Algolia (fast)
477+
const validateSuggestionsDebounced = debounce(validateSuggestionsImpl, 100)
462478
463479
// Validate suggestions when query changes
464480
watch(
465481
parsedQuery,
466482
parsed => {
467-
validateSuggestions(parsed)
483+
if (isAlgolia.value) {
484+
// Algolia existence checks are fast - fire immediately
485+
validateSuggestionsImpl(parsed)
486+
} else {
487+
validateSuggestionsDebounced(parsed)
488+
}
468489
},
469490
{ immediate: true },
470491
)
471492
493+
// Re-validate suggestions and clear caches when provider changes
494+
watch(isAlgolia, () => {
495+
// Clear existence cache since results may differ between providers
496+
existenceCache.value = {}
497+
// Re-validate with current query
498+
const parsed = parsedQuery.value
499+
if (parsed.type) {
500+
validateSuggestionsImpl(parsed)
501+
}
502+
})
503+
472504
/** Check if there's an exact package match in results */
473505
const hasExactPackageMatch = computed(() => {
474506
const q = query.value.trim().toLowerCase()

0 commit comments

Comments
 (0)