Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/llms-txt-hub / apps/web/components/search/use-search.ts |
| Date | Not specified in source |
| Repository | Janady13/llms-txt-hub |
| Revision | e0f4bf978d4df168369c7601d9c5f5ea89e84377 |
| Branch | main |
| Path or archive member | apps/web/components/search/use-search.ts |
| Original SHA-256 | e5fe6e54589dafb185fc299dd87c819cc9c02dd2105ad07c14da4576a015fe5a |
| Git blob SHA | 597cd4b549d16c2d58355ec0738c79cf08176350 |
| Reading-copy SHA-256 | e5fe6e54589dafb185fc299dd87c819cc9c02dd2105ad07c14da4576a015fe5a |
Recovered source transcript
Download complete source text ↓129 lines · 3.4 KB
/**
* Custom hook for search functionality
*/
import { logger } from '@thedaviddias/logging'
import { useEffect, useState } from 'react'
import type { SearchIndexEntry, WebsiteMetadata } from '@/components/search/search-utils'
import {
filterAndSortEntries,
transformAndSanitizeEntries,
tryLenientProcessing,
validateEntries
} from '@/components/search/use-search-helpers'
/**
* Custom hook to manage search functionality
*
* @param query - Search query string
* @returns Object containing search results, loading state, and error
*/
export function useSearch(query: string) {
const [results, setResults] = useState<WebsiteMetadata[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [searchIndex, setSearchIndex] = useState<SearchIndexEntry[]>([])
const [indexLoaded, setIndexLoaded] = useState(false)
// Load search index once on mount
useEffect(() => {
let isMounted = true
/**
* Load the search index from JSON file
*
* @returns Promise<void>
*/
async function loadSearchIndex() {
try {
const response = await fetch('/search/search-index.json', {
headers: {
'Cache-Control': 'max-age=300'
}
})
if (!response.ok) {
throw new Error(`Failed to fetch search index: ${response.status}`)
}
const data = await response.json()
const index = Array.isArray(data) ? data : []
if (isMounted) {
setSearchIndex(index)
setIndexLoaded(true)
}
} catch (error) {
logger.error('Error loading search index:', { data: error, tags: { type: 'component' } })
if (isMounted) {
setError(
`Failed to load search index: ${error instanceof Error ? error.message : 'Unknown error'}`
)
setIndexLoaded(true)
}
}
}
loadSearchIndex()
return () => {
isMounted = false
}
}, [])
// Search and filter results
useEffect(() => {
/**
* Fetch and filter search results
*
* @returns Promise<void>
*/
async function fetchSearchResults() {
if (!query) {
setResults([])
setLoading(false)
return
}
if (!indexLoaded) {
setLoading(true)
return
}
setLoading(true)
setError(null)
try {
// Filter, sort, and validate results
const filtered = filterAndSortEntries(searchIndex, query)
const valid = validateEntries(filtered)
// Use lenient processing if no valid results
if (valid.length === 0) {
const lenientResults = tryLenientProcessing(filtered)
setResults(lenientResults)
return
}
// Transform and sanitize results
const results = transformAndSanitizeEntries(valid)
setResults(results)
} catch (error) {
logger.error('Error fetching or processing search results:', {
data: error,
tags: { type: 'component' }
})
setError(error instanceof Error ? error.message : 'An unknown error occurred')
setResults([])
} finally {
setLoading(false)
}
}
// Debounce search
const debounceTimer = setTimeout(fetchSearchResults, 300)
return () => clearTimeout(debounceTimer)
}, [query, indexLoaded, searchIndex])
return { results, loading, error }
}