query-registry
Version:
Query the npm registry for packuments, manifests, packages and download counts
568 lines (537 loc) • 19.5 kB
JavaScript
// 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
};