[READ-ONLY] a fast, modern browser for the npm registry

fix: correct route query parsing for multiple params (#692)

authored by

Alex Savelyev and committed by
GitHub
3bb9c8aa 7071ba51

+24 -15
+3 -4
app/components/Header/SearchBox.vue
··· 1 1 <script setup lang="ts"> 2 2 import { debounce } from 'perfect-debounce' 3 + import { normalizeSearchParam } from '#shared/utils/url' 3 4 4 5 withDefaults( 5 6 defineProps<{ ··· 22 23 }) 23 24 24 25 // Local input value (updates immediately as user types) 25 - const searchQuery = shallowRef( 26 - (Array.isArray(route.query.q) ? route.query.q[0] : route.query.q) ?? '', 27 - ) 26 + const searchQuery = shallowRef(normalizeSearchParam(route.query.q)) 28 27 29 28 // Pages that have their own local filter using ?q 30 29 const pagesWithLocalFilter = new Set(['~username', 'org']) ··· 64 63 if (pagesWithLocalFilter.has(route.name as string)) { 65 64 return 66 65 } 67 - const value = (urlQuery as string) ?? '' 66 + const value = normalizeSearchParam(urlQuery) 68 67 if (searchQuery.value !== value) { 69 68 searchQuery.value = value 70 69 }
+3 -2
app/pages/@[org].vue
··· 1 1 <script setup lang="ts"> 2 2 import type { FilterChip, SortOption } from '#shared/types/preferences' 3 3 import { debounce } from 'perfect-debounce' 4 + import { normalizeSearchParam } from '#shared/utils/url' 4 5 5 6 definePageMeta({ 6 7 name: 'org', ··· 51 52 } = useStructuredFilters({ 52 53 packages, 53 54 initialFilters: { 54 - text: (route.query.q as string) ?? '', 55 + text: normalizeSearchParam(route.query.q), 55 56 }, 56 - initialSort: (route.query.sort as SortOption) ?? 'updated-desc', 57 + initialSort: (normalizeSearchParam(route.query.sort) as SortOption) ?? 'updated-desc', 57 58 }) 58 59 59 60 // Pagination state
+3 -2
app/pages/search.vue
··· 4 4 import { debounce } from 'perfect-debounce' 5 5 import { isValidNewPackageName, checkPackageExists } from '~/utils/package-name' 6 6 import { isPlatformSpecificPackage } from '~/utils/platform-packages' 7 + import { normalizeSearchParam } from '#shared/utils/url' 7 8 8 9 const route = useRoute() 9 10 const router = useRouter() ··· 29 30 }, 500) 30 31 31 32 // The actual search query (from URL, used for API calls) 32 - const query = computed(() => (route.query.q as string) ?? '') 33 + const query = computed(() => normalizeSearchParam(route.query.q)) 33 34 34 35 // Track if page just loaded (for hiding "Searching..." during view transition) 35 36 const hasInteracted = shallowRef(false) ··· 53 54 54 55 // Get initial page from URL (for scroll restoration on reload) 55 56 const initialPage = computed(() => { 56 - const p = Number.parseInt(route.query.page as string, 10) 57 + const p = Number.parseInt(normalizeSearchParam(route.query.page), 10) 57 58 return Number.isNaN(p) ? 1 : Math.max(1, p) 58 59 }) 59 60
+6 -7
app/pages/~[username]/index.vue
··· 1 1 <script setup lang="ts"> 2 2 import { debounce } from 'perfect-debounce' 3 + import { normalizeSearchParam } from '#shared/utils/url' 3 4 4 5 const route = useRoute('~username') 5 6 const router = useRouter() ··· 13 14 14 15 // Get initial page from URL (for scroll restoration on reload) 15 16 const initialPage = computed(() => { 16 - const p = Number.parseInt(route.query.page as string, 10) 17 + const p = Number.parseInt(normalizeSearchParam(route.query.page), 10) 17 18 return Number.isNaN(p) ? 1 : Math.max(1, p) 18 19 }) 19 20 ··· 32 33 type SortOption = 'downloads' | 'updated' | 'name-asc' | 'name-desc' 33 34 34 35 // Filter and sort state (from URL) 35 - const filterText = shallowRef( 36 - (Array.isArray(route.query.q) ? route.query.q[0] : route.query.q) ?? '', 37 - ) 36 + const filterText = shallowRef(normalizeSearchParam(route.query.q)) 38 37 const sortOption = shallowRef<SortOption>( 39 - ((Array.isArray(route.query.sort) ? route.query.sort[0] : route.query.sort) as SortOption) || 40 - 'downloads', 38 + (normalizeSearchParam(route.query.sort) as SortOption) || 'downloads', 41 39 ) 42 40 43 41 // Track if we've loaded all results (one-way flag, doesn't reset) 44 42 // Initialize to true if URL already has filter/sort params 45 43 const hasLoadedAll = shallowRef( 46 - Boolean(route.query.q) || (route.query.sort && route.query.sort !== 'downloads'), 44 + Boolean(route.query.q) || 45 + (route.query.sort && normalizeSearchParam(route.query.sort) !== 'downloads'), 47 46 ) 48 47 49 48 // Update URL when filter/sort changes (debounced)
+9
shared/utils/url.ts
··· 1 + import type { LocationQueryValue } from 'vue-router' 1 2 import { withoutProtocol, withoutTrailingSlash } from 'ufo' 2 3 3 4 /** ··· 25 26 export function areUrlsEquivalent(url1: string, url2: string): boolean { 26 27 return normalizeUrlForComparison(url1) === normalizeUrlForComparison(url2) 27 28 } 29 + 30 + export function normalizeSearchParam(query?: LocationQueryValue | LocationQueryValue[]): string { 31 + if (!query) return '' 32 + 33 + if (typeof query === 'string') return query 34 + 35 + return normalizeSearchParam(query[0]) 36 + }