query-registry
Version:
Query the npm registry for packuments, manifests, packages and download counts
131 lines (127 loc) • 5.17 kB
JavaScript
import queryString from "query-string";
import urlJoin from "url-join";
import * as z from "zod";
import { PackageJson } from "zod-package-json";
import { fetchData } from "./fetch-data.js";
import { npmRegistryUrl } from "./npm-registry.js";
export const SearchCriteria = z.object({
/**
Query text (Required).
@remarks
The following special text attributes can be used to refine results:
- `author:<name>`: show packages from the given author (e.g., `author:someone`)
- `maintainer:<name>`: show packages with the given maintainer (e.g., `maintainer:someone`)
- `keywords:<keyword list>`: show packages matching the given keyword(s);
separators `,`, `+` and `,-` act respectively as `OR`, `AND` and `NOT`
(e.g., use `keywords:foo,bar+baz,-quux` to include keywords `foo` OR (`bar` AND `baz`) but NOT `quux`)
- `not:unstable`: exclude unstable packages (semver version `<1.0.0`)
- `not:insecure`: exclude insecure packages
- `is:unstable`: include only unstable packages (semver version `<1.0.0`)
- `is:insecure`: include only insecure packages
- `boost-exact:<true/false>`: boost packages with exact name match (default: `true`)
*/
text: z.string(),
/** Number of results to return (the npm registry accepts a maximum of `250` with a default of `20`). */
size: z.number().optional(),
/** Return results from this offset. */
from: z.number().optional(),
/** Package quality weight (from `0.0` to `1.0`). */
quality: z.number().optional(),
/** Package popularity weight (from `0.0` to `1.0`). */
popularity: z.number().optional(),
/** Package maintenance weight (from `0.0` to `1.0`). */
maintenance: z.number().optional(),
});
const SearchResult = z.object({
/** Package metadata. */
package: z.object({
...PackageJson.pick({
name: true,
version: true,
description: true,
keywords: true,
}).shape,
/**
Timestamp of when the `latest` version of the package was published
in ISO 8601 format (e.g., `2021-11-23T19:12:24.006Z`).
*/
date: z.string(),
/** User who published the `latest` version of the package. */
publisher: z
.object({
username: z.string(),
email: z.string(),
})
.optional(),
/** Maintainers of the `latest` version of the package. */
maintainers: z.array(z.object({
username: z.string(),
email: z.string(),
})),
/** Links to resources associated to the package. */
links: z.object({
/** Page for the package on npmjs.com. */
npm: z.string().optional(),
/** Homepage for the package. */
homepage: z.string().optional(),
/** Repository for the package. */
repository: z.string().optional(),
/** Issue tracker for the package. */
bugs: z.string().optional(),
}),
}),
/** Final and detailed search score values. */
score: z.object({
/** Final search score value (from `0.0` to `1.0`), computed from the detailed scores. */
final: z.number(),
/** Detailed search score values. */
detail: z.object({
/** Quality search score value (from `0.0` to `1.0`). */
quality: z.number(),
/** Popularity search score value (from `0.0` to `1.0`). */
popularity: z.number(),
/** Maintenance search score value (from `0.0` to `1.0`). */
maintenance: z.number(),
}),
}),
/** Search score value; may be different from `score.final`. */
searchScore: z.number(),
/** Download counts for the package. */
downloads: z.object({
monthly: z.number(),
weekly: z.number(),
}),
/** Number of dependents for the package. */
dependents: z.coerce.number(),
/** Time at which the metadata was updated. */
updated: z.string(),
/** Flag attributes for the package. */
flags: z.object({
/** True if the package is insecure or has vulnerable dependencies. */
insecure: z.coerce.boolean(),
/** True if the package semver version number is `<1.0.0`. */
unstable: z.coerce.boolean().optional(),
}),
/** SPDX license expression. */
license: z.string().optional(),
});
export const SearchResults = z.object({
objects: z.array(SearchResult),
/**
Total number of corresponding search results available;
may be higher than the number of `objects` returned.
*/
total: z.number(),
/** Date at which the search happened. */
time: z.string(),
});
/**
`searchPackages` returns the packages corresponding to a given query.
@param criteria - one or more search criteria
@param registry - URL of the registry (default: npm registry)
@see {@link SearchCriteria}
@see {@link SearchResults}
*/
export async function searchPackages(criteria, registry = npmRegistryUrl) {
return await fetchData(SearchResults, urlJoin(registry, "-/v1/search", `?${queryString.stringify(criteria)}`));
}