query-registry
Advanced tools
Comparing version 2.6.0 to 3.0.0-0
@@ -0,8 +1,568 @@ | ||
// src/index.ts | ||
import { PackageJson as PackageJson4 } from "zod-package-json"; | ||
'use strict' | ||
// src/cache.ts | ||
import QuickLRU from "quick-lru"; | ||
var cache = new QuickLRU({ | ||
// 100 items. | ||
maxSize: 100, | ||
// 5 minutes. | ||
maxAge: 5 * 60 * 1e3 | ||
}); | ||
if (process.env.NODE_ENV === 'production') { | ||
module.exports = require('./query-registry.cjs.production.min.js') | ||
} else { | ||
module.exports = require('./query-registry.cjs.development.js') | ||
} | ||
// 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 an empty object. | ||
*/ | ||
engines: z3.record(z3.string()).catch({}).optional(), | ||
/** | ||
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 an empty string. | ||
*/ | ||
license: z3.string().catch("").optional(), | ||
/** | ||
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 coercion it correctly becomes a string. | ||
*/ | ||
homepage: z3.coerce.string().optional(), | ||
/** | ||
Deprecation message. | ||
@remarks | ||
In some old packages (like `react@16.14.0`) the `deprecated` property is a boolean | ||
and with coercion it becomes either `"true"` or `"false"`. | ||
*/ | ||
deprecated: z3.coerce.string().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. | ||
@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().optional(), | ||
/** 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({ | ||
/** | ||
Either `unscoped` for unscoped packages (e.g., `foo` -> `unscoped`) or | ||
the package's scope for scoped packages (e.g., `@foo/bar` -> `foo`). | ||
*/ | ||
scope: z15.string(), | ||
/** | ||
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(), | ||
/** Author of the package. */ | ||
author: z15.object({ | ||
username: z15.string().optional(), | ||
name: z15.string().optional(), | ||
email: z15.string().optional(), | ||
url: z15.string().optional() | ||
}).optional(), | ||
/** User who published the `latest` version of the package. */ | ||
publisher: z15.object({ | ||
username: z15.string(), | ||
email: z15.string() | ||
}), | ||
/** 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(), | ||
/** Flag attributes for the package. */ | ||
flags: z15.object({ | ||
/** True if the package semver version number is `<1.0.0`. */ | ||
unstable: z15.coerce.boolean().optional(), | ||
/** True if the package is insecure or has vulnerable dependencies. */ | ||
insecure: z15.coerce.boolean().optional() | ||
}).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 | ||
}; |
168
package.json
{ | ||
"name": "query-registry", | ||
"description": "Query the npm registry for packuments, manifests, packages and download counts", | ||
"version": "2.6.0", | ||
"author": "Edoardo Scibona (velut)", | ||
"license": "MIT", | ||
"source": "./src/index.ts", | ||
"main": "./dist/index.js", | ||
"module": "./dist/query-registry.esm.js", | ||
"types": "./dist/index.d.ts", | ||
"sideEffects": false, | ||
"exports": { | ||
".": { | ||
"require": "./dist/index.js", | ||
"import": "./dist/query-registry.esm.js", | ||
"default": "./dist/index.js" | ||
}, | ||
"./package.json": "./package.json" | ||
}, | ||
"files": [ | ||
"src", | ||
"dist" | ||
], | ||
"engines": { | ||
"node": ">=12" | ||
}, | ||
"scripts": { | ||
"start": "dts watch --target node", | ||
"build:check": "tsc --noEmit", | ||
"build": "tsc --noEmit && dts build --target node", | ||
"test": "dts test --runInBand", | ||
"test:cov": "dts test --runInBand --coverage", | ||
"test:watch": "dts test --runInBand --watchAll --coverage", | ||
"test:ci": "dts test --runInBand --ci --coverage", | ||
"lint": "dts lint src test", | ||
"lint:fix": "dts lint src test --fix", | ||
"prepare": "husky install && dts build --target node", | ||
"pre-push": "yarn && yarn lint && yarn build && yarn test:cov", | ||
"release": "np" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/velut/node-query-registry.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/velut/node-query-registry/issues" | ||
}, | ||
"keywords": [ | ||
"query", | ||
"registry", | ||
"npm", | ||
"client", | ||
"api", | ||
"packument", | ||
"package", | ||
"downloads", | ||
"search", | ||
"manifest", | ||
"typescript" | ||
], | ||
"dependencies": { | ||
"isomorphic-unfetch": "^3.1.0", | ||
"make-error": "^1.3.6", | ||
"tiny-lru": "^8.0.2", | ||
"url-join": "4.0.1", | ||
"validate-npm-package-name": "^4.0.0" | ||
}, | ||
"devDependencies": { | ||
"@commitlint/cli": "17.0.3", | ||
"@commitlint/config-conventional": "17.0.3", | ||
"@pollyjs/adapter-node-http": "6.0.5", | ||
"@pollyjs/core": "6.0.5", | ||
"@pollyjs/persister-fs": "6.0.5", | ||
"@types/debug": "4.1.7", | ||
"@types/git-url-parse": "9.0.1", | ||
"@types/pollyjs__adapter-node-http": "2.0.1", | ||
"@types/pollyjs__core": "4.3.3", | ||
"@types/pollyjs__persister-fs": "2.0.1", | ||
"@types/setup-polly-jest": "0.5.1", | ||
"@types/url-join": "4.0.1", | ||
"@types/validate-npm-package-name": "4.0.0", | ||
"debug": "4.3.4", | ||
"dts-cli": "1.5.2", | ||
"eslint-plugin-prettier": "4.2.1", | ||
"husky": "8.0.1", | ||
"nock": "13.2.9", | ||
"np": "7.6.2", | ||
"prettier": "2.7.1", | ||
"setup-polly-jest": "0.11.0", | ||
"ts-jest": "28.0.7", | ||
"tslib": "2.4.0", | ||
"typescript": "4.7.4" | ||
} | ||
"name": "query-registry", | ||
"version": "3.0.0-0", | ||
"description": "Query the npm registry for packuments, manifests, packages and download counts", | ||
"license": "MIT", | ||
"author": { | ||
"name": "Edoardo Scibona", | ||
"url": "https://github.com/velut" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/velut/query-registry.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/velut/query-registry/issues" | ||
}, | ||
"keywords": [ | ||
"query", | ||
"registry", | ||
"npm", | ||
"client", | ||
"api", | ||
"packument", | ||
"package", | ||
"downloads", | ||
"search", | ||
"manifest", | ||
"typescript" | ||
], | ||
"sideEffects": false, | ||
"type": "module", | ||
"types": "./dist/index.d.ts", | ||
"exports": { | ||
".": { | ||
"types": "./dist/index.d.ts", | ||
"import": "./dist/index.js" | ||
}, | ||
"./package.json": "./package.json" | ||
}, | ||
"files": [ | ||
"dist" | ||
], | ||
"engines": { | ||
"node": ">=20" | ||
}, | ||
"scripts": { | ||
"check": "tsc --noEmit", | ||
"build": "tsc --noEmit && tsup", | ||
"attw": "attw --pack . --ignore-rules cjs-resolves-to-esm", | ||
"test": "vitest", | ||
"test:update-db": "bun run scripts/update-test-db.ts", | ||
"test:ci": "vitest run --coverage --bail 1", | ||
"lint": "prettier --check .", | ||
"format": "prettier --write .", | ||
"pre-push": "bun install && bun run lint && bun run build && bun run test:ci && bun run attw", | ||
"release": "np" | ||
}, | ||
"dependencies": { | ||
"query-string": "^9.0.0", | ||
"quick-lru": "^7.0.0", | ||
"url-join": "^5.0.0", | ||
"validate-npm-package-name": "^5.0.0", | ||
"zod": "^3.22.5", | ||
"zod-package-json": "^1.0.1" | ||
}, | ||
"devDependencies": { | ||
"@arethetypeswrong/cli": "^0.15.3", | ||
"@types/bun": "^1.1.0", | ||
"@types/validate-npm-package-name": "^4.0.2", | ||
"@vitest/coverage-v8": "^1.5.0", | ||
"fflate": "^0.8.2", | ||
"np": "^10.0.5", | ||
"prettier": "^3.2.5", | ||
"tsup": "^8.0.2", | ||
"typescript": "^5.4.5", | ||
"vitest": "^1.5.0" | ||
} | ||
} |
173
README.md
# query-registry | ||
[![Build status](https://img.shields.io/github/workflow/status/velut/node-query-registry/CI)](https://github.com/velut/node-query-registry/actions?query=workflow%3ACI) | ||
[![Coverage](https://img.shields.io/codecov/c/gh/velut/node-query-registry)](https://codecov.io/gh/velut/node-query-registry) | ||
[![Build status](https://img.shields.io/github/actions/workflow/status/velut/query-registry/main.yml?branch=main)](https://github.com/velut/query-registry/actions?query=workflow%3ACI) | ||
[![Coverage](https://img.shields.io/codecov/c/gh/velut/query-registry)](https://codecov.io/gh/velut/query-registry) | ||
[![jsDocs.io](https://img.shields.io/badge/jsDocs.io-reference-blue)](https://www.jsdocs.io/package/query-registry) | ||
![Language](https://img.shields.io/github/languages/top/velut/node-query-registry) | ||
![Language](https://img.shields.io/github/languages/top/velut/query-registry) | ||
[![npm bundle size](https://img.shields.io/bundlephobia/min/query-registry)](https://bundlephobia.com/result?p=query-registry) | ||
[![npm](https://img.shields.io/npm/v/query-registry)](https://www.npmjs.com/package/query-registry) | ||
[![License](https://img.shields.io/github/license/velut/node-query-registry)](https://github.com/velut/node-query-registry/blob/main/LICENSE) | ||
[![License](https://img.shields.io/github/license/velut/query-registry)](https://github.com/velut/query-registry/blob/main/LICENSE) | ||
This package exports several functions to query the [npm registry](https://www.npmjs.com) (or one of its mirrors) through one of its [endpoints](https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md). | ||
`query-registry` is an API wrapper for the [npm registry API](https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md). | ||
## Features | ||
- Provides functions to: | ||
- Get registry metadata | ||
- Get packuments (package documents) and their abbreviated form | ||
- Get package manifests | ||
- Get download counts (packages and registry) | ||
- Search packages | ||
- Usable in the browser | ||
- Fully typed API and response data | ||
- Supports mirrors of the npm registry | ||
- Supports caching network requests | ||
- Well documented and tested | ||
- Provides functions to: | ||
- Get registry metadata. | ||
- Get registry public keys. | ||
- Get packuments (package documents) with full package metadata. | ||
- Get abbreviated packuments with installation data only. | ||
- Get package manifests for each version of a package. | ||
- Get download counts for the registry and for packages. | ||
- Search packages by name and other specific criteria. | ||
- Works in the browser. | ||
- Validates registry responses with [zod](https://www.npmjs.com/package/zod). | ||
- Automatically caches registry responses for a short time. | ||
- Supports third-party npm-compatible registries. | ||
## API & Package Info | ||
## Useful resources | ||
- [**Explore the API on jsDocs.io**](https://www.jsdocs.io/package/query-registry) | ||
- View package contents on [**unpkg**](https://unpkg.com/query-registry/) | ||
- View repository on [**GitHub**](https://github.com/velut/node-query-registry) | ||
- Read the changelog on [**GitHub**](https://github.com/velut/node-query-registry/blob/main/CHANGELOG.md) | ||
- [**Explore the API on jsDocs.io**](https://www.jsdocs.io/package/query-registry) | ||
- View package contents on [**unpkg**](https://unpkg.com/query-registry/) | ||
- View repository on [**GitHub**](https://github.com/velut/query-registry) | ||
- Read the changelog on [**GitHub**](https://github.com/velut/query-registry/blob/main/CHANGELOG.md) | ||
@@ -39,3 +40,3 @@ ## Install | ||
``` | ||
npm i query-registry | ||
npm add query-registry | ||
``` | ||
@@ -49,90 +50,120 @@ | ||
## Usage Examples | ||
Using `pnpm`: | ||
Get the metadata for the npm registry: | ||
``` | ||
pnpm add query-registry | ||
``` | ||
Using `bun`: | ||
``` | ||
bun add query-registry | ||
``` | ||
## Usage examples | ||
### Registry | ||
Get the metadata about the npm registry itself, if available: | ||
```typescript | ||
import { getRegistryMetadata } from 'query-registry'; | ||
import { getRegistryMetadata } from "query-registry"; | ||
(async () => { | ||
const metadata = await getRegistryMetadata(); | ||
const metadata = await getRegistryMetadata(); | ||
``` | ||
// Output: 'registry' | ||
console.log(metadata.db_name); | ||
})(); | ||
Get the public signing keys for the npm registry: | ||
```typescript | ||
import { getRegistrySigningKeys } from "query-registry"; | ||
const { keys } = await getRegistrySigningKeys(); | ||
``` | ||
Get the latest manifest for package `query-registry` from the npm registry: | ||
### Packuments (Package documents) | ||
Get the abbreviated packument containing only the necessary data to install the `react` package: | ||
```typescript | ||
import { getPackageManifest } from 'query-registry'; | ||
import { getAbbreviatedPackument } from "query-registry"; | ||
(async () => { | ||
const manifest = await getPackageManifest({ name: 'query-registry' }); | ||
const abbrPackument = await getAbbreviatedPackument("react"); | ||
``` | ||
// Output: 'query-registry' | ||
console.log(manifest.name); | ||
})(); | ||
Get the full packument containing all the data available about the `react` package: | ||
```typescript | ||
import { getPackument } from "query-registry"; | ||
const packument = await getPackument("react"); | ||
``` | ||
Get the abbreviated packument for package `query-registry` from the npm registry: | ||
### Package manifests | ||
Get the manifest containing the original `package.json` data plus additional registry metadata for the `latest` version of the `react` package: | ||
```typescript | ||
import { getAbbreviatedPackument } from 'query-registry'; | ||
import { getPackageManifest } from "query-registry"; | ||
(async () => { | ||
const packument = await getAbbreviatedPackument({ name: 'query-registry' }); | ||
const manifest = await getPackageManifest("react"); | ||
``` | ||
// Output: 'query-registry' | ||
console.log(packument.name); | ||
})(); | ||
Get the manifest for `react@18.2.0` (semver version): | ||
```typescript | ||
import { getPackageManifest } from "query-registry"; | ||
const manifest = await getPackageManifest("react", "18.2.0"); | ||
``` | ||
Get the weekly downloads for package `query-registry` from the npm registry: | ||
Get the manifest for `react@next` (distribution tag): | ||
```typescript | ||
import { getPackageDownloads } from 'query-registry'; | ||
import { getPackageManifest } from "query-registry"; | ||
(async () => { | ||
const downloads = await getPackageDownloads({ name: 'query-registry' }); | ||
const manifest = await getPackageManifest("react", "next"); | ||
``` | ||
// Output: 'query-registry' | ||
console.log(downloads.package); | ||
### Search packages | ||
// Output: 'number' | ||
console.log(typeof downloads.downloads); | ||
})(); | ||
Search packages related to `react` (e.g., `react`, `react-dom`, ...): | ||
```typescript | ||
import { searchPackages } from "query-registry"; | ||
const results = await searchPackages({ text: "react" }); | ||
``` | ||
Get the search results for text query `query-registry` from the npm registry: | ||
### Download counts | ||
Get the total number of downloads for package `react` for the last month: | ||
```typescript | ||
import { searchPackages } from 'query-registry'; | ||
import { getPackageDownloads } from "query-registry"; | ||
(async () => { | ||
const results = await searchPackages({ | ||
query: { text: 'query-registry' }, | ||
}); | ||
// Output: 'query-registry' | ||
console.log(results.objects[0].package.name); | ||
})(); | ||
const { downloads } = await getPackageDownloads("react", "last-month"); | ||
``` | ||
## Debug | ||
There are also these other download counts functions available: `getBulkDailyPackageDownloads`, `getBulkPackageDownloads`, `getDailyPackageDownloads`, `getDailyRegistryDownloads` and `getPackageVersionsDownloads`. | ||
Debug messages are available in non production environments when the `DEBUG` environment variable is set to `query-registry`: | ||
### Cache | ||
```bash | ||
DEBUG="query-registry" | ||
Clear the internal cache. | ||
```typescript | ||
import { cache } from "query-registry"; | ||
cache.clear(); | ||
``` | ||
For more information, see the [debug package](https://www.npmjs.com/package/debug). | ||
See the [quick-lru](https://www.npmjs.com/package/quick-lru) package for the cache API. | ||
## License | ||
MIT License | ||
``` | ||
MIT | ||
``` | ||
Copyright (c) 2021 Edoardo Scibona | ||
Copyright (c) 2024 Edoardo Scibona | ||
See LICENSE file. | ||
See [LICENSE](./LICENSE) file. |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
10
168
0
Yes
196590
6
5
4656
+ Addedquery-string@^9.0.0
+ Addedquick-lru@^7.0.0
+ Addedzod@^3.22.5
+ Addedzod-package-json@^1.0.1
+ Addeddecode-uri-component@0.4.1(transitive)
+ Addedfilter-obj@5.1.0(transitive)
+ Addedquery-string@9.0.0(transitive)
+ Addedquick-lru@7.0.0(transitive)
+ Addedsplit-on-first@3.0.0(transitive)
+ Addedurl-join@5.0.0(transitive)
+ Addedvalidate-npm-package-name@5.0.1(transitive)
+ Addedzod@3.23.6(transitive)
+ Addedzod-package-json@1.0.2(transitive)
- Removedisomorphic-unfetch@^3.1.0
- Removedmake-error@^1.3.6
- Removedtiny-lru@^8.0.2
- Removedbuiltins@5.1.0(transitive)
- Removedisomorphic-unfetch@3.1.0(transitive)
- Removedlru-cache@6.0.0(transitive)
- Removedmake-error@1.3.6(transitive)
- Removednode-fetch@2.7.0(transitive)
- Removedsemver@7.6.0(transitive)
- Removedtiny-lru@8.0.2(transitive)
- Removedtr46@0.0.3(transitive)
- Removedunfetch@4.2.0(transitive)
- Removedurl-join@4.0.1(transitive)
- Removedvalidate-npm-package-name@4.0.0(transitive)
- Removedwebidl-conversions@3.0.1(transitive)
- Removedwhatwg-url@5.0.0(transitive)
- Removedyallist@4.0.0(transitive)
Updatedurl-join@^5.0.0