@@ -8,6 +8,11 @@ import type {
88 NpmPerson ,
99 PackageVersionInfo ,
1010} from '#shared/types'
11+ import {
12+ liteClient as algoliasearch ,
13+ type LiteClient ,
14+ type SearchResponse ,
15+ } from 'algoliasearch/lite'
1116import type { ReleaseType } from 'semver'
1217import { maxSatisfying , prerelease , major , minor , diff , gt , compare } from 'semver'
1318import { isExactVersion } from '~/utils/versions'
@@ -41,15 +46,177 @@ async function fetchCachedPackument(name: string): Promise<Packument | null> {
4146 return promise
4247}
4348
49+ const ALGOLIA_SEARCH = true
50+ let searchClient : LiteClient
51+ if ( ALGOLIA_SEARCH ) {
52+ searchClient = algoliasearch ( 'OFCNCOG2CU' , 'f54e21fa3a2a0160595bb058179bfb1e' )
53+ }
54+
55+ type SearchOptions = {
56+ size ?: number
57+ from ?: number
58+ quality ?: number
59+ popularity ?: number
60+ maintenance ?: number
61+ }
62+
63+ interface Owner {
64+ name : string
65+ email ?: string
66+ avatar ?: string
67+ link ?: string
68+ }
69+
70+ interface Repo {
71+ url : string
72+ host : string
73+ user : string
74+ project : string
75+ path : string
76+ head ?: string
77+ branch ?: string
78+ }
79+
80+ interface GithubRepo {
81+ user : string
82+ project : string
83+ path : string
84+ head : string
85+ }
86+
87+ type TsType =
88+ | {
89+ ts : 'definitely-typed'
90+ definitelyTyped : string
91+ }
92+ | {
93+ ts : 'included' | false | { possible : true }
94+ }
95+
96+ type ModuleType = 'cjs' | 'esm' | 'none' | 'unknown'
97+
98+ type StyleType = string | 'none'
99+
100+ type ComputedMeta = {
101+ computedKeywords : string [ ]
102+ computedMetadata : Record < string , unknown >
103+ }
104+
105+ type GetUser = {
106+ name : string
107+ email ?: string
108+ }
109+
110+ type AlgoliaSearchResult = {
111+ objectID : string
112+ rev : string
113+ name : string
114+ downloadsLast30Days : number
115+ downloadsRatio : number
116+ humanDownloadsLast30Days : string
117+ jsDelivrHits : number
118+ popular : boolean
119+ version : string
120+ versions : Record < string , string >
121+ tags : Record < string , string >
122+ description : string | null
123+ dependencies : Record < string , string >
124+ devDependencies : Record < string , string >
125+ originalAuthor ?: GetUser
126+ repository : Repo | null
127+ githubRepo : GithubRepo | null
128+ gitHead : string | null
129+ readme : string
130+ owner : Owner | null
131+ deprecated : boolean | string
132+ isDeprecated : boolean
133+ deprecatedReason : string | null
134+ isSecurityHeld : boolean
135+ homepage : string | null
136+ license : string | null
137+ keywords : string [ ]
138+ computedKeywords : ComputedMeta [ 'computedKeywords' ]
139+ computedMetadata : ComputedMeta [ 'computedMetadata' ]
140+ created : number
141+ modified : number
142+ lastPublisher : Owner | null
143+ owners : Owner [ ]
144+ bin : Record < string , string >
145+ dependents : number
146+ types : TsType
147+ moduleTypes : ModuleType [ ]
148+ styleTypes : StyleType [ ]
149+ humanDependents : string
150+ changelogFilename : string | null
151+ lastCrawl : string
152+ _revision : number
153+ _searchInternal : {
154+ alternativeNames : string [ ]
155+ popularAlternativeNames : string [ ]
156+ }
157+ }
158+
44159async function searchNpmPackages (
45160 query : string ,
46- options : {
47- size ?: number
48- from ?: number
49- quality ?: number
50- popularity ?: number
51- maintenance ?: number
52- } = { } ,
161+ options : SearchOptions = { } ,
162+ ) : Promise < NpmSearchResponse > {
163+ if ( ALGOLIA_SEARCH ) {
164+ return searchClient
165+ . search ( [
166+ {
167+ indexName : 'npm-search' ,
168+ params : {
169+ query,
170+ hitsPerPage : options . size || 20 ,
171+ page : options . from ? Math . floor ( options . from / ( options . size || 20 ) ) : 0 ,
172+ filters : '' ,
173+ analyticsTags : [ 'npmx.dev' ] ,
174+ } ,
175+ } ,
176+ ] )
177+ . then ( ( { results } ) => {
178+ const response = results [ 0 ] as SearchResponse < AlgoliaSearchResult >
179+ return {
180+ objects : response . hits . map < NpmSearchResult > ( hit => ( {
181+ package : {
182+ name : hit . name ,
183+ version : hit . version ,
184+ description : hit . description || '' ,
185+ date : new Date ( hit . modified ) . toISOString ( ) ,
186+ links : {
187+ npm : `https://www.npmjs.com/package/${ hit . name } ` ,
188+ homepage : hit . homepage || undefined ,
189+ repository : hit . repository ?. url || undefined ,
190+ } ,
191+ maintainers : hit . owners
192+ ? hit . owners . map ( owner => ( {
193+ name : owner . name ,
194+ email : owner . email ,
195+ } ) )
196+ : [ ] ,
197+ } ,
198+ score : {
199+ final : 0 ,
200+ detail : {
201+ quality : hit . popular ? 1 : 0 ,
202+ popularity : hit . downloadsRatio ,
203+ maintenance : 0 ,
204+ } ,
205+ } ,
206+ searchScore : 0 ,
207+ updated : new Date ( hit . modified ) . toISOString ( ) ,
208+ } ) ) ,
209+ total : response . nbHits ! ,
210+ time : new Date ( ) . toISOString ( ) ,
211+ }
212+ } )
213+ }
214+ return await searchNpmPackagesViaRegistry ( query , options )
215+ }
216+
217+ async function searchNpmPackagesViaRegistry (
218+ query : string ,
219+ options : SearchOptions ,
53220) : Promise < NpmSearchResponse > {
54221 const params = new URLSearchParams ( )
55222 params . set ( 'text' , query )
0 commit comments