diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index eaaafefec..adbe92bdf 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -16,9 +16,7 @@ import pkg from './package'; import stars from './stars'; import profile from './v1/profile'; import token from './v1/token'; -import v1Search from './api/v1/search' - -const { match, validateName, validatePackage, encodeScopePackage, antiLoop } = require('../middleware'); +import v1Search from './v1/search' export default function(config: Config, auth: IAuth, storage: IStorageHandler) { /* eslint new-cap:off */ diff --git a/packages/api/src/v1/search.ts b/packages/api/src/v1/search.ts index 539a56e18..1b1995213 100644 --- a/packages/api/src/v1/search.ts +++ b/packages/api/src/v1/search.ts @@ -1,105 +1,98 @@ -import semver from 'semver'; +import semver from 'semver' import { Package } from '@verdaccio/types'; -function compileTextSearch(textSearch: string): (pkg: Package) => boolean { - const personMatch = (person, search) => { - if (typeof person === 'string') { - return person.includes(search); - } - - if (typeof person === 'object') { - for (const field of Object.values(person)) { - if (typeof field === 'string' && field.includes(search)) { - return true; +function compileTextSearch(textSearch: string): ((pkg: Package) => boolean) { + const personMatch = (person, search) => { + if(typeof person === 'string') + {return person.includes(search);} + + if(typeof person === 'object') + {for(const field of Object.values(person)) + {if(typeof field === 'string' && field.includes(search)) + {return true;}}} + + return false; } - } - } + const matcher = function(q) { + const match = q.match(/author:(.*)/) + if(match !== null) + {return (pkg) => personMatch(pkg.author, match[1])} - return false; - }; - const matcher = function (q) { - const match = q.match(/author:(.*)/); - if (match !== null) { - return (pkg) => personMatch(pkg.author, match[1]); - } + // TODO: maintainer, keywords, not/is unstable insecure, boost-exact + // TODO implement some scoring system for freetext + return (pkg) => { + return ['name', 'displayName', 'description'] + .map(k => pkg[k]) + .filter(x => x !== undefined) + .some(txt => txt.includes(q)) + }; + } - // TODO: maintainer, keywords, not/is unstable insecure, boost-exact - // TODO implement some scoring system for freetext - return (pkg) => { - return ['name', 'displayName', 'description'] - .map((k) => pkg[k]) - .filter((x) => x !== undefined) - .some((txt) => txt.includes(q)); - }; - }; - - const textMatchers = (textSearch || '').split(' ').map(matcher); - return (pkg) => textMatchers.every((m) => m(pkg)); + const textMatchers = (textSearch || '').split(' ').map(matcher); + return (pkg) => textMatchers.every(m => m(pkg)); } -export default function (route, auth, storage): void { - route.get('/-/v1/search', (req, res) => { - // TODO: implement proper result scoring weighted by quality, popularity and maintenance query parameters - let [text, size, from /* , quality, popularity, maintenance */] = [ - 'text', - 'size', - 'from' /* , 'quality', 'popularity', 'maintenance' */ - ].map((k) => req.query[k]); +export default function(route, auth, storage): void { + route.get('/-/v1/search', (req, res)=>{ + // TODO: implement proper result scoring weighted by quality, popularity and maintenance query parameters + let [text, size, from /* , quality, popularity, maintenance */] = + ['text', 'size', 'from' /* , 'quality', 'popularity', 'maintenance' */] + .map(k => req.query[k]) + + size = parseInt(size) || 20; + from = parseInt(from) || 0; + + const isInteresting = compileTextSearch(text); - size = parseInt(size) || 20; - from = parseInt(from) || 0; + const resultStream = storage.search(0, {req: {query: {local: true}}}); + const resultBuf = [] as any; + let completed = false; - const isInteresting = compileTextSearch(text); + const sendResponse = (): void => { + completed = true; + resultStream.destroy() - const resultStream = storage.search(0, { req: { query: { local: true } } }); - const resultBuf = [] as any; - let completed = false; + const final = resultBuf.slice(from, size).map(pkg => { + return { + package: pkg, + flags: { + unstable: + Object.keys(pkg.versions) + .some(v => semver.satisfies(v, '^1.0.0')) + ? undefined + : true + }, + score: { + final: 1, + detail: { + quality: 1, + popularity: 1, + maintenance: 0 + } + }, + searchScore: 100000 + } + }) + const response = { + objects: final, + total: final.length, + time: new Date().toUTCString() + } - const sendResponse = (): void => { - completed = true; - resultStream.destroy(); + res.status(200) + .json(response) + } - const final = resultBuf.slice(from, size).map((pkg) => { - return { - package: pkg, - flags: { - unstable: Object.keys(pkg.versions).some((v) => semver.satisfies(v, '^1.0.0')) - ? undefined - : true - }, - score: { - final: 1, - detail: { - quality: 1, - popularity: 1, - maintenance: 0 - } - }, - searchScore: 100000 - }; - }); - const response = { - objects: final, - total: final.length, - time: new Date().toUTCString() - }; - - res.status(200).json(response); - }; - - resultStream.on('data', (pkg) => { - if (!isInteresting(pkg)) { - return; - } - resultBuf.push(pkg); - if (!completed && resultBuf.length >= size + from) { - sendResponse(); - } - }); - resultStream.on('end', () => { - if (!completed) { - sendResponse(); - } - }); - }); -} + resultStream.on('data', (pkg)=>{ + if(!isInteresting(pkg)) + {return;} + resultBuf.push(pkg) + if(!completed && resultBuf.length >= size + from) + {sendResponse();} + }) + resultStream.on('end', ()=>{ + if(!completed) + {sendResponse()} + }) + }) +} \ No newline at end of file