UNPKG

query-registry

Version:

Query the npm registry for packuments, manifests, packages and download counts

568 lines (537 loc) 19.5 kB
// src/index.ts import { PackageJson as PackageJson4 } from "zod-package-json"; // src/cache.ts import QuickLRU from "quick-lru"; var cache = new QuickLRU({ // 100 items. maxSize: 100, // 5 minutes. maxAge: 5 * 60 * 1e3 }); // src/download-period.ts import { z } from "zod"; var DownloadPeriod = z.union([ z.literal("last-day"), z.literal("last-week"), z.literal("last-month"), z.literal("last-year"), z.string().regex(/^\d{4}-\d{2}-\d{2}(:\d{4}-\d{2}-\d{2})?$/) ]); // src/get-abbreviated-packument.ts import urlJoin2 from "url-join"; import { z as z4 } from "zod"; // src/assert-valid-package-name.ts import validatePackageName from "validate-npm-package-name"; var assertValidPackageName = (name) => { const { validForOldPackages, validForNewPackages, warnings, errors } = validatePackageName(name); const isValid = validForOldPackages || validForNewPackages; if (!isValid) { throw new Error("invalid package name", { cause: { name, warnings, errors } }); } }; // src/dist-tags.ts import { z as z2 } from "zod"; var DistTags = z2.object({ /** Latest semver version number. */ latest: z2.string(), // The following tags have no special meaning for the npm registry // but they are commonly used by packages. next: z2.string().optional(), alpha: z2.string().optional(), beta: z2.string().optional(), rc: z2.string().optional(), canary: z2.string().optional(), dev: z2.string().optional() }).catchall(z2.string()); // src/fetch-data.ts var fetchData = async (schema, url, headers) => { const cacheKey = JSON.stringify({ url, headers }); const cachedJson = cache.get(cacheKey); if (cachedJson) { return schema.parse(cachedJson); } const response = await fetch(url, { headers }); const json = await response.json(); cache.set(cacheKey, json); return schema.parse(json); }; // src/get-package-manifest.ts import urlJoin from "url-join"; import { z as z3 } from "zod"; import { PackageJson } from "zod-package-json"; // src/npm-registry.ts var npmRegistryUrl = "https://registry.npmjs.org"; var npmRegistryDownloadsApiUrl = "https://api.npmjs.org"; // src/get-package-manifest.ts var Dist = z3.object({ /** Tarball URL. */ tarball: z3.string(), /** SHA1 sum of the tarball. */ shasum: z3.string(), /** String in the format `<hashAlgorithm>-<base64-hash>`. */ integrity: z3.string().optional(), /** Number of files in the tarball. */ fileCount: z3.number().optional(), /** Total unpacked size in bytes of the files in the tarball. */ unpackedSize: z3.number().optional(), /** PGP signature in the format `<package>@<version>:<integrity>`. @deprecated {@link https://docs.npmjs.com/about-registry-signatures#migrating-from-pgp-to-ecdsa-signatures} */ "npm-signature": z3.string().optional(), /** ECDSA registry signatures. @see {@link https://docs.npmjs.com/about-registry-signatures} */ signatures: z3.array( z3.object({ keyid: z3.string(), sig: z3.string() }) ).optional() }); var PackageManifest = PackageJson.extend({ /** Package version ID in the format `<name>@<version>` (e.g., `foo@1.0.0`). */ _id: z3.string(), /** Distribution metadata generated by the registry. */ dist: Dist, /** Text extracted from the README file. */ readme: z3.string().optional(), /** Name of the README file. */ readmeFilename: z3.string().optional(), /** Commit corresponding to the published package version. */ gitHead: z3.string().optional(), /** True if the package contains a shrinkwrap file. */ _hasShrinkwrap: z3.boolean().optional(), /** Node.js version used to publish the package. */ _nodeVersion: z3.string().optional(), /** npm CLI version used to publish the package. */ _npmVersion: z3.string().optional(), /** npm user who published the specific version of the package. */ _npmUser: PackageJson.shape.author.optional(), /** Internal npm registry data. */ _npmOperationalInternal: z3.object({ host: z3.string().optional(), tmp: z3.string().optional() }).optional(), /** Runtime systems supported by the package. @remarks In some old packages (like `lodash@0.1.0`) the `engines` property is an array of strings instead of an object and with catch it becomes `undefined`. */ engines: z3.record(z3.string()).optional().catch(void 0), /** SPDX license expression or a custom license. @remarks In some old packages (like `eslint@0.0.6`) the `license` property is an object and with catch `license` becomes `undefined`. */ license: z3.string().optional().catch(void 0), /** URL of the package's homepage. @remarks In some old packages (like `fs-extra@0.0.1`) the `homepage` property is an array of strings and with catch it becomes `undefined`. */ homepage: z3.string().optional().catch(void 0), /** Deprecation status/message. @remarks In some packages (like `react@16.14.0`) the `deprecated` property is a boolean instead of a deprecation message. */ deprecated: z3.union([z3.string(), z3.boolean()]).optional() }); var getPackageManifest = async (name, versionOrTag = "latest", registry = npmRegistryUrl) => { assertValidPackageName(name); return fetchData(PackageManifest, urlJoin(registry, name, versionOrTag)); }; // src/get-abbreviated-packument.ts var AbbreviatedPackument = z4.object({ /** Package name. */ name: z4.string(), /** Timestamp of when the package was last modified in ISO 8601 format (e.g., `2021-11-23T19:12:24.006Z`). */ modified: z4.string(), /** Mapping of distribution tags to semver version numbers e.g., `{ "latest": "1.0.0" }`). */ "dist-tags": DistTags, /** Mapping of semver version numbers to the required metadata for installing a package version. */ versions: z4.record( z4.string(), PackageManifest.pick({ name: true, version: true, dist: true, deprecated: true, dependencies: true, optionalDependencies: true, devDependencies: true, bundleDependencies: true, peerDependencies: true, peerDependenciesMeta: true, bin: true, directories: true, engines: true, cpu: true, os: true, _hasShrinkwrap: true }).extend({ /** True if the package contains an `install` script. */ hasInstallScript: z4.boolean().optional() }) ) }); var getAbbreviatedPackument = async (name, registry = npmRegistryUrl) => { assertValidPackageName(name); return fetchData(AbbreviatedPackument, urlJoin2(registry, name), { Accept: "application/vnd.npm.install-v1+json" }); }; // src/get-bulk-daily-package-downloads.ts import urlJoin5 from "url-join"; import { z as z7 } from "zod"; // src/get-daily-package-downloads.ts import urlJoin4 from "url-join"; import { z as z6 } from "zod"; // src/get-daily-registry-downloads.ts import urlJoin3 from "url-join"; import { z as z5 } from "zod"; var DailyRegistryDownloads = z5.object({ /** Date of the first day (inclusive) in the format `YYYY-MM-DD`. */ start: z5.string(), /** Date of the last day (inclusive) in the format `YYYY-MM-DD`. */ end: z5.string(), /** Download counts for each day. */ downloads: z5.array( z5.object({ /** Total number of downloads for the day. */ downloads: z5.number(), /** Date of the day in the format `YYYY-MM-DD`. */ day: z5.string() }) ) }); var getDailyRegistryDownloads = async (period, registry = npmRegistryDownloadsApiUrl) => fetchData(DailyRegistryDownloads, urlJoin3(registry, `/downloads/range/${period}`)); // src/get-daily-package-downloads.ts var DailyPackageDownloads = DailyRegistryDownloads.extend({ /** Package name. */ package: z6.string() }); var getDailyPackageDownloads = async (name, period, registry = npmRegistryDownloadsApiUrl) => { assertValidPackageName(name); return fetchData(DailyPackageDownloads, urlJoin4(registry, `/downloads/range/${period}/${name}`)); }; // src/get-bulk-daily-package-downloads.ts var BulkDailyPackageDownloads = z7.record(z7.union([z7.null(), DailyPackageDownloads])); var getBulkDailyPackageDownloads = async (names, period, registry = npmRegistryDownloadsApiUrl) => { for (const name of names) { assertValidPackageName(name); } return fetchData( BulkDailyPackageDownloads, urlJoin5(registry, `/downloads/range/${period}/${names.join(",")}`) ); }; // src/get-bulk-package-downloads.ts import urlJoin8 from "url-join"; import { z as z10 } from "zod"; // src/get-package-downloads.ts import urlJoin7 from "url-join"; import { z as z9 } from "zod"; // src/get-registry-downloads.ts import urlJoin6 from "url-join"; import { z as z8 } from "zod"; var RegistryDownloads = z8.object({ /** Total number of downloads. */ downloads: z8.number(), /** Date of the first day (inclusive) in the format `YYYY-MM-DD`. */ start: z8.string(), /** Date of the last day (inclusive) in the format `YYYY-MM-DD`. */ end: z8.string() }); var getRegistryDownloads = async (period, registry = npmRegistryDownloadsApiUrl) => fetchData(RegistryDownloads, urlJoin6(registry, `/downloads/point/${period}`)); // src/get-package-downloads.ts var PackageDownloads = RegistryDownloads.extend({ /** Package name. */ package: z9.string() }); var getPackageDownloads = async (name, period, registry = npmRegistryDownloadsApiUrl) => { assertValidPackageName(name); return fetchData(PackageDownloads, urlJoin7(registry, `/downloads/point/${period}/${name}`)); }; // src/get-bulk-package-downloads.ts var BulkPackageDownloads = z10.record(z10.union([z10.null(), PackageDownloads])); var getBulkPackageDownloads = async (names, period, registry = npmRegistryDownloadsApiUrl) => { for (const name of names) { assertValidPackageName(name); } return fetchData( BulkPackageDownloads, urlJoin8(registry, `/downloads/point/${period}/${names.join(",")}`) ); }; // src/get-package-versions-downloads.ts import urlJoin9 from "url-join"; import { z as z11 } from "zod"; var PackageVersionsDownloads = z11.object({ /** Package name. */ package: z11.string(), /** Mapping of semver version numbers to total number of downloads. */ downloads: z11.record(z11.number()) }); var getPackageVersionsDownloads = async (name, registry = npmRegistryDownloadsApiUrl) => { assertValidPackageName(name); return fetchData( PackageVersionsDownloads, urlJoin9(registry, `/versions/${encodeURIComponent(name)}/last-week`) ); }; // src/get-packument.ts import urlJoin10 from "url-join"; import { z as z12 } from "zod"; import { PackageJson as PackageJson2 } from "zod-package-json"; var Time = z12.object({ /** Timestamp of when the package was created in ISO 8601 format (e.g., `2021-11-23T19:12:24.006Z`). */ created: z12.string(), /** Timestamp of when the package was last modified in ISO 8601 format (e.g., `2021-11-23T19:12:24.006Z`). */ modified: z12.string() }).catchall(z12.string()); var Packument = PackageJson2.pick({ author: true, bugs: true, contributors: true, description: true, homepage: true, keywords: true, license: true, maintainers: true, repository: true }).extend({ /** Package name used as the ID in CouchDB. */ _id: z12.string(), /** Package name. */ name: z12.string(), /** Mapping of distribution tags to semver version numbers e.g., `{ "latest": "1.0.0" }`). */ "dist-tags": DistTags, /** Mapping of semver version numbers to timestamps in ISO 8601 format representing the publishing time (e.g., `{ "1.0.0": "2021-11-23T19:12:24.006Z" }`). Also includes the timestamps of when the package was `created` and last `modified`. */ time: Time, /** Mapping of semver version numbers to package manifests. @see {@link PackageManifest} */ versions: z12.record(PackageManifest), /** Revision ID of the document in CouchDB. */ _rev: z12.coerce.string().optional(), /** Mapping of npm usernames of users who starred the package to `true`. */ users: z12.record(z12.boolean()).optional(), /** Text extracted from the README file. */ readme: z12.string().optional(), /** Name of the README file. */ readmeFilename: z12.string().optional() }); var getPackument = async (name, registry = npmRegistryUrl) => { assertValidPackageName(name); return fetchData(Packument, urlJoin10(registry, name)); }; // src/get-registry-metadata.ts import { z as z13 } from "zod"; var RegistryMetadata = z13.object({ /** Database name, usually `registry` */ db_name: z13.string().optional(), doc_count: z13.number().optional(), doc_del_count: z13.number().optional(), update_seq: z13.number().optional(), purge_seq: z13.number().optional(), compact_running: z13.boolean().optional(), disk_size: z13.number().optional(), data_size: z13.number().optional(), instance_start_time: z13.string().optional(), disk_format_version: z13.number().optional(), committed_update_seq: z13.number().optional(), compacted_seq: z13.number().optional(), uuid: z13.string().optional(), other: z13.object({ data_size: z13.number().optional() }).optional(), sizes: z13.object({ file: z13.number().optional(), active: z13.number().optional(), external: z13.number().optional() }).optional() }); var getRegistryMetadata = async (registry = npmRegistryUrl) => fetchData(RegistryMetadata, registry); // src/get-registry-signing-keys.ts import urlJoin11 from "url-join"; import { z as z14 } from "zod"; var RegistrySigningKeys = z14.object({ keys: z14.array( z14.object({ /** String in the simplified extended ISO 8601 format (e.g., `YYYY-MM-DDTHH:mm:ss.sssZ`) or `null`. */ expires: z14.string().nullable(), /** SHA256 fingerprint of the public key. */ keyid: z14.string(), /** Key type; only `ecdsa-sha2-nistp256` is currently supported by the npm CLI. */ keytype: z14.string(), /** Key scheme; only `ecdsa-sha2-nistp256` is currently supported by the npm CLI. */ scheme: z14.string(), /** Public key encoded in base64. */ key: z14.string() }) ) }); var getRegistrySigningKeys = async (registry = npmRegistryUrl) => fetchData(RegistrySigningKeys, urlJoin11(registry, "-/npm/v1/keys")); // src/search-packages.ts import queryString from "query-string"; import urlJoin12 from "url-join"; import { z as z15 } from "zod"; import { PackageJson as PackageJson3 } from "zod-package-json"; var SearchCriteria = z15.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: z15.string(), /** Number of results to return (the npm registry accepts a maximum of `250` with a default of `20`). */ size: z15.number().optional(), /** Return results from this offset. */ from: z15.number().optional(), /** Package quality weight (from `0.0` to `1.0`). */ quality: z15.number().optional(), /** Package popularity weight (from `0.0` to `1.0`). */ popularity: z15.number().optional(), /** Package maintenance weight (from `0.0` to `1.0`). */ maintenance: z15.number().optional() }); var SearchResult = z15.object({ /** Package metadata. */ package: PackageJson3.pick({ name: true, version: true, description: true, keywords: true }).extend({ /** Timestamp of when the `latest` version of the package was published in ISO 8601 format (e.g., `2021-11-23T19:12:24.006Z`). */ date: z15.string(), /** User who published the `latest` version of the package. */ publisher: z15.object({ username: z15.string(), email: z15.string() }).optional(), /** Maintainers of the `latest` version of the package. */ maintainers: z15.array( z15.object({ username: z15.string(), email: z15.string() }) ), /** Links to resources associated to the package. */ links: z15.object({ /** Page for the package on npmjs.com. */ npm: z15.string().optional(), /** Homepage for the package. */ homepage: z15.string().optional(), /** Repository for the package. */ repository: z15.string().optional(), /** Issue tracker for the package. */ bugs: z15.string().optional() }) }), /** Final and detailed search score values. */ score: z15.object({ /** Final search score value (from `0.0` to `1.0`), computed from the detailed scores. */ final: z15.number(), /** Detailed search score values. */ detail: z15.object({ /** Quality search score value (from `0.0` to `1.0`). */ quality: z15.number(), /** Popularity search score value (from `0.0` to `1.0`). */ popularity: z15.number(), /** Maintenance search score value (from `0.0` to `1.0`). */ maintenance: z15.number() }) }), /** Search score value; may be different from `score.final`. */ searchScore: z15.number(), /** Download counts for the package. */ downloads: z15.object({ monthly: z15.number(), weekly: z15.number() }), /** Number of dependents for the package. */ dependents: z15.number(), /** Time at which the metadata was updated. */ updated: z15.string(), /** Flag attributes for the package. */ flags: z15.object({ /** True if the package is insecure or has vulnerable dependencies. */ insecure: z15.coerce.boolean(), /** True if the package semver version number is `<1.0.0`. */ unstable: z15.coerce.boolean().optional() }), /** SPDX license expression. */ license: z15.string().optional() }); var SearchResults = z15.object({ objects: z15.array(SearchResult), /** Total number of corresponding search results available; may be higher than the number of `objects` returned. */ total: z15.number(), /** Date at which the search happened. */ time: z15.string() }); var searchPackages = async (criteria, registry = npmRegistryUrl) => fetchData(SearchResults, urlJoin12(registry, "-/v1/search", `?${queryString.stringify(criteria)}`)); export { AbbreviatedPackument, BulkDailyPackageDownloads, BulkPackageDownloads, DailyPackageDownloads, DailyRegistryDownloads, DownloadPeriod, PackageDownloads, PackageJson4 as PackageJson, PackageManifest, PackageVersionsDownloads, Packument, RegistryDownloads, RegistryMetadata, RegistrySigningKeys, SearchCriteria, SearchResults, cache, getAbbreviatedPackument, getBulkDailyPackageDownloads, getBulkPackageDownloads, getDailyPackageDownloads, getDailyRegistryDownloads, getPackageDownloads, getPackageManifest, getPackageVersionsDownloads, getPackument, getRegistryDownloads, getRegistryMetadata, getRegistrySigningKeys, npmRegistryDownloadsApiUrl, npmRegistryUrl, searchPackages };