1 | ;
|
2 |
|
3 | Object.defineProperty(exports, '__esModule', { value: true });
|
4 |
|
5 | var validatePackageName = require('validate-npm-package-name');
|
6 | var makeError = require('make-error');
|
7 | var unfetch = require('isomorphic-unfetch');
|
8 | var lru = require('tiny-lru');
|
9 | var gitUrlParse = require('git-url-parse');
|
10 |
|
11 | function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
12 |
|
13 | function _interopNamespace(e) {
|
14 | if (e && e.__esModule) return e;
|
15 | var n = Object.create(null);
|
16 | if (e) {
|
17 | Object.keys(e).forEach(function (k) {
|
18 | if (k !== 'default') {
|
19 | var d = Object.getOwnPropertyDescriptor(e, k);
|
20 | Object.defineProperty(n, k, d.get ? d : {
|
21 | enumerable: true,
|
22 | get: function () { return e[k]; }
|
23 | });
|
24 | }
|
25 | });
|
26 | }
|
27 | n["default"] = e;
|
28 | return n;
|
29 | }
|
30 |
|
31 | var validatePackageName__default = /*#__PURE__*/_interopDefaultLegacy(validatePackageName);
|
32 | var makeError__default = /*#__PURE__*/_interopDefaultLegacy(makeError);
|
33 | var unfetch__default = /*#__PURE__*/_interopDefaultLegacy(unfetch);
|
34 | var lru__default = /*#__PURE__*/_interopDefaultLegacy(lru);
|
35 | var gitUrlParse__default = /*#__PURE__*/_interopDefaultLegacy(gitUrlParse);
|
36 |
|
37 | /**
|
38 | * npm registry
|
39 | *
|
40 | * @see {@link https://registry.npmjs.org}
|
41 | */
|
42 | const npmRegistry = 'https://registry.npmjs.org';
|
43 | /**
|
44 | * npm registry mirror by Cloudflare
|
45 | *
|
46 | * @remarks
|
47 | * This registry has CORS enabled and can be used to retrieve
|
48 | * package manifests and packuments in the browser.
|
49 | *
|
50 | * @see {@link https://npmjs.cf}
|
51 | * @see {@link https://registry.npmjs.cf}
|
52 | */
|
53 |
|
54 | const cloudflareRegistry = 'https://registry.npmjs.cf';
|
55 | /**
|
56 | * npm registry mirror by Yarn
|
57 | *
|
58 | * @see {@link https://registry.yarnpkg.com}
|
59 | */
|
60 |
|
61 | const yarnRegistry = 'https://registry.yarnpkg.com';
|
62 | /**
|
63 | * Mirrors of the npm registry.
|
64 | *
|
65 | * @see {@link cloudflareRegistry}
|
66 | * @see {@link yarnRegistry}
|
67 | */
|
68 |
|
69 | const npmRegistryMirrors = [cloudflareRegistry, yarnRegistry];
|
70 | /**
|
71 | * Downloads API for the npm registry
|
72 | *
|
73 | * @see {@link https://api.npmjs.org}
|
74 | */
|
75 |
|
76 | const npmRegistryDownloadsAPI = 'https://api.npmjs.org';
|
77 |
|
78 | function normalizeRawAbbreviatedPackument({
|
79 | rawAbbreviatedPackument
|
80 | }) {
|
81 | const {
|
82 | 'dist-tags': distTags,
|
83 | name: id,
|
84 | modified: modifiedAt
|
85 | } = rawAbbreviatedPackument;
|
86 | return { ...rawAbbreviatedPackument,
|
87 | id,
|
88 | distTags,
|
89 | modifiedAt
|
90 | };
|
91 | }
|
92 |
|
93 | /**
|
94 | * `FetchError` represents an error that happened when fetching a URL.
|
95 | *
|
96 | * The `instanceof` operator can be used to check for this error.
|
97 | */
|
98 |
|
99 | class FetchError extends makeError.BaseError {
|
100 | constructor(
|
101 | /** URL originally fetched */
|
102 | url,
|
103 | /** Response received */
|
104 | response) {
|
105 | super(`fetch: request to ${url} failed with status ${response.statusText}`);
|
106 | this.url = void 0;
|
107 | this.response = void 0;
|
108 | this.url = url;
|
109 | this.response = response;
|
110 | }
|
111 |
|
112 | }
|
113 | /**
|
114 | * `InvalidPackageNameError` is thrown when the name of a package
|
115 | * is not valid according to the npm registry naming rules.
|
116 | *
|
117 | * The `instanceof` operator can be used to check for this error.
|
118 | *
|
119 | * @see {@link https://www.npmjs.com/package/validate-npm-package-name}
|
120 | */
|
121 |
|
122 | const InvalidPackageNameError = /*#__PURE__*/makeError__default["default"]('InvalidPackageNameError');
|
123 | /**
|
124 | * `InvalidPackageVersionError` is thrown when a package's version does not exist.
|
125 | *
|
126 | * The `instanceof` operator can be used to check for this error.
|
127 | */
|
128 |
|
129 | const InvalidPackageVersionError = /*#__PURE__*/makeError__default["default"]('InvalidPackageVersionError');
|
130 |
|
131 | async function log(formatter, ...args) {
|
132 | {
|
133 | try {
|
134 | const {
|
135 | debug
|
136 | } = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('debug')); });
|
137 | const logger = debug('query-registry');
|
138 | logger(formatter, args);
|
139 | } catch {}
|
140 | }
|
141 | }
|
142 |
|
143 | function assertValidPackageName({
|
144 | name
|
145 | }) {
|
146 | const {
|
147 | validForOldPackages,
|
148 | validForNewPackages
|
149 | } = validatePackageName__default["default"](name);
|
150 | const valid = validForOldPackages || validForNewPackages;
|
151 |
|
152 | if (!valid) {
|
153 | log('assertValidPackageName: invalid package name: %O', {
|
154 | name
|
155 | });
|
156 | throw new InvalidPackageNameError(`invalid package name: '${name}'`);
|
157 | }
|
158 | }
|
159 |
|
160 | const maxItems = 250;
|
161 | const fiveMinutesTTL = 5 * 60 * 1000;
|
162 | const cache = /*#__PURE__*/lru__default["default"](maxItems, fiveMinutesTTL);
|
163 | async function fetch({
|
164 | url,
|
165 | headers,
|
166 | cached = true
|
167 | }) {
|
168 | const cacheKey = `headers=${JSON.stringify(headers)};url=${url}`;
|
169 | const cachedJSON = cache.get(cacheKey);
|
170 |
|
171 | if (cached && cachedJSON) {
|
172 | log('fetch: returning cached response: %O', {
|
173 | cacheKey,
|
174 | url,
|
175 | cachedJSON
|
176 | });
|
177 | return cachedJSON;
|
178 | }
|
179 |
|
180 | const response = await unfetch__default["default"](url, {
|
181 | headers
|
182 | });
|
183 |
|
184 | if (!response.ok) {
|
185 | log('fetch: request failed: %O', {
|
186 | url,
|
187 | headers,
|
188 | status: response.statusText,
|
189 | response
|
190 | });
|
191 | throw new FetchError(url, response);
|
192 | }
|
193 |
|
194 | const json = await response.json();
|
195 |
|
196 | if (cached) {
|
197 | cache.set(cacheKey, json);
|
198 | }
|
199 |
|
200 | log('fetch: returning fresh response: %O', {
|
201 | url,
|
202 | json
|
203 | });
|
204 | return json;
|
205 | }
|
206 |
|
207 | async function fetchFromRegistry({
|
208 | endpoint,
|
209 | headers,
|
210 | query,
|
211 | registry = npmRegistry,
|
212 | mirrors = npmRegistryMirrors,
|
213 | cached
|
214 | }) {
|
215 | const urls = [registry, ...mirrors].map(host => {
|
216 | const url = new URL(endpoint, host);
|
217 | url.search = query != null ? query : '';
|
218 | return url.href;
|
219 | });
|
220 | let lastError;
|
221 |
|
222 | for (const url of urls) {
|
223 | try {
|
224 | const json = await fetch({
|
225 | url,
|
226 | headers,
|
227 | cached
|
228 | });
|
229 | return json;
|
230 | } catch (err) {
|
231 | // Keep last fetch error
|
232 | lastError = err;
|
233 | }
|
234 | }
|
235 |
|
236 | log('fetchFromRegistry: cannot retrieve data from registry or mirrors: %O', {
|
237 | endpoint,
|
238 | headers,
|
239 | query,
|
240 | registry,
|
241 | mirrors,
|
242 | lastError
|
243 | });
|
244 | throw lastError;
|
245 | }
|
246 |
|
247 | /**
|
248 | * `getRawAbbreviatedPackument` returns the abbreviated packument (package document)
|
249 | * containing only the metadata necessary to install a package present on the registry.
|
250 | *
|
251 | * Note: the abbreviated packument is returned as retrieved from the registry.
|
252 | *
|
253 | * @param name - package name
|
254 | * @param registry - URL of the registry (default: npm registry)
|
255 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
256 | * @param cached - accept cached responses (default: `true`)
|
257 | *
|
258 | * @example
|
259 | * Get the abbreviated packument for package `query-registry` from the npm registry:
|
260 | *
|
261 | * ```typescript
|
262 | * import { getRawAbbreviatedPackument } from 'query-registry';
|
263 | *
|
264 | * (async () => {
|
265 | * const packument = await getRawAbbreviatedPackument({ name: 'query-registry' });
|
266 | *
|
267 | * // Output: 'query-registry'
|
268 | * console.log(packument.name);
|
269 | * })();
|
270 | * ```
|
271 | *
|
272 | * @see {@link RawAbbreviatedPackument}
|
273 | * @see {@link npmRegistry}
|
274 | * @see {@link npmRegistryMirrors}
|
275 | */
|
276 |
|
277 | async function getRawAbbreviatedPackument({
|
278 | name,
|
279 | registry,
|
280 | mirrors,
|
281 | cached
|
282 | }) {
|
283 | assertValidPackageName({
|
284 | name
|
285 | });
|
286 | const endpoint = `/${name}`;
|
287 | const headers = {
|
288 | Accept: 'application/vnd.npm.install-v1+json'
|
289 | };
|
290 | return fetchFromRegistry({
|
291 | endpoint,
|
292 | headers,
|
293 | registry,
|
294 | mirrors,
|
295 | cached
|
296 | });
|
297 | }
|
298 |
|
299 | /**
|
300 | * `getAbbreviatedPackument` returns the abbreviated packument (package document)
|
301 | * containing only the metadata necessary to install a package present on the registry.
|
302 | *
|
303 | * @remarks
|
304 | * To get all the metadata (full packument) about a package see {@link getPackument}.
|
305 | *
|
306 | * @param name - package name
|
307 | * @param registry - URL of the registry (default: npm registry)
|
308 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
309 | * @param cached - accept cached responses (default: `true`)
|
310 | *
|
311 | * @example
|
312 | * Get the abbreviated packument for package `query-registry` from the npm registry:
|
313 | *
|
314 | * ```typescript
|
315 | * import { getAbbreviatedPackument } from 'query-registry';
|
316 | *
|
317 | * (async () => {
|
318 | * const packument = await getAbbreviatedPackument({ name: 'query-registry' });
|
319 | *
|
320 | * // Output: 'query-registry'
|
321 | * console.log(packument.name);
|
322 | * })();
|
323 | * ```
|
324 | *
|
325 | * @see {@link AbbreviatedPackument}
|
326 | * @see {@link RawAbbreviatedPackument}
|
327 | * @see {@link npmRegistry}
|
328 | * @see {@link npmRegistryMirrors}
|
329 | */
|
330 |
|
331 | async function getAbbreviatedPackument({
|
332 | name,
|
333 | registry,
|
334 | mirrors,
|
335 | cached
|
336 | }) {
|
337 | const rawAbbreviatedPackument = await getRawAbbreviatedPackument({
|
338 | name,
|
339 | registry,
|
340 | mirrors,
|
341 | cached
|
342 | });
|
343 | return normalizeRawAbbreviatedPackument({
|
344 | rawAbbreviatedPackument
|
345 | });
|
346 | }
|
347 |
|
348 | async function fetchDownloadsFromRegistry({
|
349 | endpoint,
|
350 | registryDownloadsAPI = npmRegistryDownloadsAPI,
|
351 | cached
|
352 | }) {
|
353 | return fetchFromRegistry({
|
354 | endpoint,
|
355 | registry: registryDownloadsAPI,
|
356 | mirrors: [],
|
357 | cached
|
358 | });
|
359 | }
|
360 |
|
361 | function normalizeRawDownloadPeriod({
|
362 | rawDownloadPeriod = 'last-week'
|
363 | }) {
|
364 | if (typeof rawDownloadPeriod === 'string') {
|
365 | return rawDownloadPeriod;
|
366 | }
|
367 |
|
368 | if (rawDownloadPeriod instanceof Date) {
|
369 | return getDay(rawDownloadPeriod);
|
370 | }
|
371 |
|
372 | const {
|
373 | start,
|
374 | end
|
375 | } = rawDownloadPeriod;
|
376 | return `${getDay(start)}:${getDay(end)}`;
|
377 | }
|
378 |
|
379 | function getDay(date) {
|
380 | return date.toISOString().split('T')[0];
|
381 | }
|
382 |
|
383 | /**
|
384 | * `getDailyPackageDownloads` returns the number of downloads for a package
|
385 | * for each day in a given time period.
|
386 | *
|
387 | * @param name - package name
|
388 | * @param period - time period in which downloads happened (default: `last-week`)
|
389 | * @param registryDownloadsAPI - URL of the registry's downloads API (default: npm registry)
|
390 | * @param cached - accept cached responses (default: `true`)
|
391 | *
|
392 | * @example
|
393 | * Get the day by day weekly downloads for package `query-registry` from the npm registry:
|
394 | *
|
395 | * ```typescript
|
396 | * import { getDailyPackageDownloads } from 'query-registry';
|
397 | *
|
398 | * (async () => {
|
399 | * const downloads = await getDailyPackageDownloads({ name: 'query-registry' });
|
400 | *
|
401 | * // Output: 'query-registry'
|
402 | * console.log(downloads.package);
|
403 | *
|
404 | * // Output: 'number'
|
405 | * console.log(typeof downloads.downloads[0].downloads);
|
406 | * })();
|
407 | * ```
|
408 | *
|
409 | * @example
|
410 | * Get the day by day monthly downloads for package `query-registry` from the npm registry:
|
411 | *
|
412 | * ```typescript
|
413 | * import { getDailyPackageDownloads } from 'query-registry';
|
414 | *
|
415 | * (async () => {
|
416 | * const downloads = await getDailyPackageDownloads({ name: 'query-registry', period: 'last-month' });
|
417 | *
|
418 | * // Output: 'query-registry'
|
419 | * console.log(downloads.package);
|
420 | *
|
421 | * // Output: 'number'
|
422 | * console.log(typeof downloads.downloads[0].downloads);
|
423 | * })();
|
424 | * ```
|
425 | *
|
426 | * @see {@link DailyPackageDownloads}
|
427 | * @see {@link DownloadPeriod}
|
428 | * @see {@link npmRegistryDownloadsAPI}
|
429 | * @see {@link https://github.com/npm/registry/blob/master/docs/download-counts.md#ranges}
|
430 | */
|
431 |
|
432 | async function getDailyPackageDownloads({
|
433 | name,
|
434 | period: rawDownloadPeriod,
|
435 | registryDownloadsAPI,
|
436 | cached
|
437 | }) {
|
438 | assertValidPackageName({
|
439 | name
|
440 | });
|
441 | const period = normalizeRawDownloadPeriod({
|
442 | rawDownloadPeriod
|
443 | });
|
444 | const endpoint = `/downloads/range/${period}/${name}`;
|
445 | return fetchDownloadsFromRegistry({
|
446 | endpoint,
|
447 | registryDownloadsAPI,
|
448 | cached
|
449 | });
|
450 | }
|
451 |
|
452 | /**
|
453 | * `getDailyRegistryDownloads` returns the number of downloads for all registry packages
|
454 | * for each day in a given time period.
|
455 | *
|
456 | * @param period - time period in which downloads happened (default: `last-week`)
|
457 | * @param registryDownloadsAPI - URL of the registry's downloads API (default: npm registry)
|
458 | * @param cached - accept cached responses (default: `true`)
|
459 | *
|
460 | * @example
|
461 | * Get the day by day weekly downloads for the npm registry:
|
462 | *
|
463 | * ```typescript
|
464 | * import { getDailyRegistryDownloads } from 'query-registry';
|
465 | *
|
466 | * (async () => {
|
467 | * const downloads = await getDailyRegistryDownloads();
|
468 | *
|
469 | * // Output: 'number'
|
470 | * console.log(typeof downloads.downloads[0].downloads);
|
471 | * })();
|
472 | * ```
|
473 | *
|
474 | * @example
|
475 | * Get the day by day monthly downloads for the npm registry:
|
476 | *
|
477 | * ```typescript
|
478 | * import { getDailyRegistryDownloads } from 'query-registry';
|
479 | *
|
480 | * (async () => {
|
481 | * const downloads = await getDailyRegistryDownloads({ period: 'last-month' });
|
482 | *
|
483 | * // Output: 'number'
|
484 | * console.log(typeof downloads.downloads[0].downloads);
|
485 | * })();
|
486 | * ```
|
487 | *
|
488 | * @see {@link DailyRegistryDownloads}
|
489 | * @see {@link DownloadPeriod}
|
490 | * @see {@link npmRegistryDownloadsAPI}
|
491 | * @see {@link https://github.com/npm/registry/blob/master/docs/download-counts.md#ranges}
|
492 | */
|
493 |
|
494 | async function getDailyRegistryDownloads({
|
495 | period: rawDownloadPeriod,
|
496 | registryDownloadsAPI,
|
497 | cached
|
498 | } = {}) {
|
499 | const period = normalizeRawDownloadPeriod({
|
500 | rawDownloadPeriod
|
501 | });
|
502 | const endpoint = `/downloads/range/${period}`;
|
503 | return fetchDownloadsFromRegistry({
|
504 | endpoint,
|
505 | registryDownloadsAPI,
|
506 | cached
|
507 | });
|
508 | }
|
509 |
|
510 | /**
|
511 | * `getPackageDownloads` returns the number of downloads for a package
|
512 | * in a given time period.
|
513 | *
|
514 | * @param name - package name
|
515 | * @param period - time period in which downloads happened (default: `last-week`)
|
516 | * @param registryDownloadsAPI - URL of the registry's downloads API (default: npm registry)
|
517 | * @param cached - accept cached responses (default: `true`)
|
518 | *
|
519 | * @example
|
520 | * Get the weekly downloads for package `query-registry` from the npm registry:
|
521 | *
|
522 | * ```typescript
|
523 | * import { getPackageDownloads } from 'query-registry';
|
524 | *
|
525 | * (async () => {
|
526 | * const downloads = await getPackageDownloads({ name: 'query-registry' });
|
527 | *
|
528 | * // Output: 'query-registry'
|
529 | * console.log(downloads.package);
|
530 | *
|
531 | * // Output: 'number'
|
532 | * console.log(typeof downloads.downloads);
|
533 | * })();
|
534 | * ```
|
535 | *
|
536 | * @example
|
537 | * Get the monthly downloads for package `query-registry` from the npm registry:
|
538 | *
|
539 | * ```typescript
|
540 | * import { getPackageDownloads } from 'query-registry';
|
541 | *
|
542 | * (async () => {
|
543 | * const downloads = await getPackageDownloads({ name: 'query-registry', period: 'last-month' });
|
544 | *
|
545 | * // Output: 'query-registry'
|
546 | * console.log(downloads.package);
|
547 | *
|
548 | * // Output: 'number'
|
549 | * console.log(typeof downloads.downloads);
|
550 | * })();
|
551 | * ```
|
552 | *
|
553 | * @see {@link PackageDownloads}
|
554 | * @see {@link DownloadPeriod}
|
555 | * @see {@link npmRegistryDownloadsAPI}
|
556 | * @see {@link https://github.com/npm/registry/blob/master/docs/download-counts.md#point-values}
|
557 | */
|
558 |
|
559 | async function getPackageDownloads({
|
560 | name,
|
561 | period: rawDownloadPeriod,
|
562 | registryDownloadsAPI,
|
563 | cached
|
564 | }) {
|
565 | assertValidPackageName({
|
566 | name
|
567 | });
|
568 | const period = normalizeRawDownloadPeriod({
|
569 | rawDownloadPeriod
|
570 | });
|
571 | const endpoint = `/downloads/point/${period}/${name}`;
|
572 | return fetchDownloadsFromRegistry({
|
573 | endpoint,
|
574 | registryDownloadsAPI,
|
575 | cached
|
576 | });
|
577 | }
|
578 |
|
579 | function extractRawPackageManifest({
|
580 | rawPackument,
|
581 | version = 'latest'
|
582 | }) {
|
583 | var _distTags$version;
|
584 |
|
585 | const {
|
586 | name,
|
587 | 'dist-tags': distTags,
|
588 | versions
|
589 | } = rawPackument;
|
590 | const versionNumber = (_distTags$version = distTags[version]) != null ? _distTags$version : version;
|
591 | const manifest = versions[versionNumber];
|
592 |
|
593 | if (!manifest) {
|
594 | log('getPackageManifest: invalid package version: %O', {
|
595 | name,
|
596 | version
|
597 | });
|
598 | throw new InvalidPackageVersionError(`invalid package version: '${name}@${version}'`);
|
599 | }
|
600 |
|
601 | return manifest;
|
602 | }
|
603 |
|
604 | /**
|
605 | * `getRawPackument` returns the packument (package document) containing
|
606 | * all the metadata about a package present on the registry.
|
607 | *
|
608 | * Note: the packument is returned as retrieved from the registry.
|
609 | *
|
610 | * @param name - package name
|
611 | * @param registry - URL of the registry (default: npm registry)
|
612 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
613 | * @param cached - accept cached responses (default: `true`)
|
614 | *
|
615 | * @example
|
616 | * Get the packument for package `query-registry` from the npm registry:
|
617 | *
|
618 | * ```typescript
|
619 | * import { getRawPackument } from 'query-registry';
|
620 | *
|
621 | * (async () => {
|
622 | * const packument = await getRawPackument({ name: 'query-registry' });
|
623 | *
|
624 | * // Output: 'query-registry'
|
625 | * console.log(packument.name);
|
626 | * })();
|
627 | * ```
|
628 | *
|
629 | * @see {@link RawPackument}
|
630 | * @see {@link npmRegistry}
|
631 | * @see {@link npmRegistryMirrors}
|
632 | */
|
633 |
|
634 | async function getRawPackument({
|
635 | name,
|
636 | registry,
|
637 | mirrors,
|
638 | cached
|
639 | }) {
|
640 | assertValidPackageName({
|
641 | name
|
642 | });
|
643 | const endpoint = `/${name}`;
|
644 | return fetchFromRegistry({
|
645 | endpoint,
|
646 | registry,
|
647 | mirrors,
|
648 | cached
|
649 | });
|
650 | }
|
651 |
|
652 | /**
|
653 | * `getRawPackageManifest` returns the manifest describing
|
654 | * a specific version of a package.
|
655 | *
|
656 | * Note: the manifest is returned as retrieved from the registry.
|
657 | *
|
658 | * @param name - package name
|
659 | * @param version - package version (default: `latest`)
|
660 | * @param registry - URL of the registry (default: npm registry)
|
661 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
662 | * @param cached - accept cached responses (default: `true`)
|
663 | *
|
664 | * @example
|
665 | * Get the latest manifest for package `query-registry` from the npm registry:
|
666 | *
|
667 | * ```typescript
|
668 | * import { getRawPackageManifest } from 'query-registry';
|
669 | *
|
670 | * (async () => {
|
671 | * const manifest = await getRawPackageManifest({ name: 'query-registry' });
|
672 | *
|
673 | * // Output: 'query-registry'
|
674 | * console.log(manifest.name);
|
675 | * })();
|
676 | * ```
|
677 | *
|
678 | * @example
|
679 | * Get the manifest for package `query-registry@1.0.0` from the npm registry:
|
680 | *
|
681 | * ```typescript
|
682 | * import { getRawPackageManifest } from 'query-registry';
|
683 | *
|
684 | * (async () => {
|
685 | * const manifest = await getRawPackageManifest({ name: 'query-registry', version: '1.0.0' });
|
686 | *
|
687 | * // Output: 'query-registry'
|
688 | * console.log(manifest.name);
|
689 | *
|
690 | * // Output: '1.0.0'
|
691 | * console.log(manifest.version);
|
692 | * })();
|
693 | * ```
|
694 | *
|
695 | * @see {@link RawPackageManifest}
|
696 | * @see {@link npmRegistry}
|
697 | * @see {@link npmRegistryMirrors}
|
698 | */
|
699 |
|
700 | async function getRawPackageManifest({
|
701 | name,
|
702 | version,
|
703 | registry,
|
704 | mirrors,
|
705 | cached
|
706 | }) {
|
707 | const rawPackument = await getRawPackument({
|
708 | name,
|
709 | registry,
|
710 | mirrors,
|
711 | cached
|
712 | });
|
713 | return extractRawPackageManifest({
|
714 | rawPackument,
|
715 | version
|
716 | });
|
717 | }
|
718 |
|
719 | async function getDefinitelyTypedName({
|
720 | rawPackageManifest,
|
721 | registry,
|
722 | mirrors,
|
723 | cached
|
724 | }) {
|
725 | const {
|
726 | name,
|
727 | types,
|
728 | typings
|
729 | } = rawPackageManifest;
|
730 | const definitelyTypedName = toDefinitelyTypedName({
|
731 | name
|
732 | });
|
733 | const alreadyTyped = name === definitelyTypedName || !!types || !!typings;
|
734 |
|
735 | if (alreadyTyped) {
|
736 | return undefined;
|
737 | }
|
738 |
|
739 | let ok = false;
|
740 |
|
741 | try {
|
742 | const {
|
743 | deprecated
|
744 | } = await getRawPackageManifest({
|
745 | name: definitelyTypedName,
|
746 | registry,
|
747 | mirrors,
|
748 | cached
|
749 | });
|
750 | ok = deprecated === undefined;
|
751 | } catch {}
|
752 |
|
753 | return ok ? definitelyTypedName : undefined;
|
754 | }
|
755 | /**
|
756 | * `toDefinitelyTypedName` returns the name of the corresponding
|
757 | * DefinitelyTyped package (for example,
|
758 | * `foo` => `@types/foo`,
|
759 | * `@bar/baz` => `@types/bar__baz`).
|
760 | */
|
761 |
|
762 | function toDefinitelyTypedName({
|
763 | name
|
764 | }) {
|
765 | return name.startsWith('@types/') ? name : `@types/${name.replace('@', '').replace('/', '__')}`;
|
766 | }
|
767 |
|
768 | /**
|
769 | * `getUntypedName` returns the name of the normal package
|
770 | * corresponding to a DefinitelyTyped package.
|
771 | */
|
772 | function getUntypedName({
|
773 | name
|
774 | }) {
|
775 | if (!name.startsWith('@types/')) {
|
776 | return undefined;
|
777 | } // ['foo', undefined] or ['@bar', 'baz']
|
778 |
|
779 |
|
780 | const [scopeOrName, scopedName] = name.replace('@types/', '').split('__');
|
781 | return scopedName ? `@${scopeOrName}/${scopedName}` : scopeOrName;
|
782 | }
|
783 |
|
784 | function normalizeRawLicense({
|
785 | rawLicense
|
786 | }) {
|
787 | if (!rawLicense) {
|
788 | return undefined;
|
789 | }
|
790 |
|
791 | if (typeof rawLicense !== 'string') {
|
792 | return undefined;
|
793 | }
|
794 |
|
795 | return rawLicense;
|
796 | }
|
797 |
|
798 | function normalizeRawRepository({
|
799 | rawRepository
|
800 | }) {
|
801 | if (isRepository(rawRepository)) {
|
802 | return normalizeRepository({
|
803 | rawRepository
|
804 | });
|
805 | }
|
806 |
|
807 | if (typeof rawRepository === 'string') {
|
808 | return normalizeRepository({
|
809 | rawRepository: {
|
810 | url: rawRepository
|
811 | }
|
812 | });
|
813 | }
|
814 |
|
815 | return undefined;
|
816 | }
|
817 |
|
818 | function isRepository(rawRepository) {
|
819 | return rawRepository && typeof rawRepository === 'object' && typeof rawRepository['url'] === 'string' && ['string', 'undefined'].includes(typeof rawRepository['type']) && ['string', 'undefined'].includes(typeof rawRepository['directory']);
|
820 | }
|
821 |
|
822 | function normalizeRepository({
|
823 | rawRepository
|
824 | }) {
|
825 | const {
|
826 | url,
|
827 | directory: repositoryDir
|
828 | } = rawRepository;
|
829 | const info = parseGitURL({
|
830 | url
|
831 | });
|
832 |
|
833 | if (!info) {
|
834 | return undefined;
|
835 | }
|
836 |
|
837 | const {
|
838 | source,
|
839 | full_name: repositoryID,
|
840 | filepath
|
841 | } = info; // Add domain to sources derived from npm-style shortcuts
|
842 |
|
843 | const host = source.replace(/^$/, 'github.com').replace(/^github$/, 'github.com').replace(/^gitlab$/, 'gitlab.com').replace(/^bitbucket$/, 'bitbucket.org');
|
844 | const parsedDir = filepath !== '' ? filepath : undefined;
|
845 | return {
|
846 | type: 'git',
|
847 | url: `https://${host}/${repositoryID}`,
|
848 | directory: repositoryDir != null ? repositoryDir : parsedDir
|
849 | };
|
850 | }
|
851 |
|
852 | function parseGitURL({
|
853 | url
|
854 | }) {
|
855 | let info;
|
856 |
|
857 | try {
|
858 | info = gitUrlParse__default["default"](url);
|
859 | } catch {}
|
860 |
|
861 | return info;
|
862 | }
|
863 |
|
864 | async function normalizeRawPackageManifest({
|
865 | rawPackageManifest,
|
866 | rawPackument,
|
867 | registry,
|
868 | mirrors,
|
869 | cached
|
870 | }) {
|
871 | const {
|
872 | _id: id,
|
873 | name,
|
874 | version,
|
875 | license: rawLicense,
|
876 | repository: rawRepository,
|
877 | _npmUser: publisher
|
878 | } = rawPackageManifest;
|
879 | const createdAt = rawPackument.time[version];
|
880 | const license = normalizeRawLicense({
|
881 | rawLicense
|
882 | });
|
883 | const gitRepository = normalizeRawRepository({
|
884 | rawRepository
|
885 | });
|
886 | const definitelyTypedName = await getDefinitelyTypedName({
|
887 | rawPackageManifest,
|
888 | registry,
|
889 | mirrors,
|
890 | cached
|
891 | });
|
892 | const untypedName = getUntypedName({
|
893 | name
|
894 | });
|
895 | return { ...rawPackageManifest,
|
896 | id,
|
897 | createdAt,
|
898 | publisher,
|
899 | license,
|
900 | gitRepository,
|
901 | definitelyTypedName,
|
902 | untypedName
|
903 | };
|
904 | }
|
905 |
|
906 | /**
|
907 | * `getPackageManifest` returns the manifest describing
|
908 | * a specific version of a package.
|
909 | *
|
910 | * @param name - package name
|
911 | * @param version - package version (default: `latest`)
|
912 | * @param registry - URL of the registry (default: npm registry)
|
913 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
914 | * @param cached - accept cached responses (default: `true`)
|
915 | *
|
916 | * @example
|
917 | * Get the latest manifest for package `query-registry` from the npm registry:
|
918 | *
|
919 | * ```typescript
|
920 | * import { getPackageManifest } from 'query-registry';
|
921 | *
|
922 | * (async () => {
|
923 | * const manifest = await getPackageManifest({ name: 'query-registry' });
|
924 | *
|
925 | * // Output: 'query-registry'
|
926 | * console.log(manifest.name);
|
927 | * })();
|
928 | * ```
|
929 | *
|
930 | * @example
|
931 | * Get the manifest for package `query-registry@1.0.0` from the npm registry:
|
932 | *
|
933 | * ```typescript
|
934 | * import { getPackageManifest } from 'query-registry';
|
935 | *
|
936 | * (async () => {
|
937 | * const manifest = await getPackageManifest({ name: 'query-registry', version: '1.0.0' });
|
938 | *
|
939 | * // Output: 'query-registry'
|
940 | * console.log(manifest.name);
|
941 | *
|
942 | * // Output: '1.0.0'
|
943 | * console.log(manifest.version);
|
944 | * })();
|
945 | * ```
|
946 | *
|
947 | * @see {@link PackageManifest}
|
948 | * @see {@link RawPackageManifest}
|
949 | * @see {@link npmRegistry}
|
950 | * @see {@link npmRegistryMirrors}
|
951 | */
|
952 |
|
953 | async function getPackageManifest({
|
954 | name,
|
955 | version,
|
956 | registry,
|
957 | mirrors,
|
958 | cached
|
959 | }) {
|
960 | const rawPackument = await getRawPackument({
|
961 | name,
|
962 | registry,
|
963 | mirrors,
|
964 | cached
|
965 | });
|
966 | const rawPackageManifest = extractRawPackageManifest({
|
967 | rawPackument,
|
968 | version
|
969 | });
|
970 | const packageManifest = await normalizeRawPackageManifest({
|
971 | rawPackageManifest,
|
972 | rawPackument,
|
973 | registry,
|
974 | mirrors,
|
975 | cached
|
976 | });
|
977 | return packageManifest;
|
978 | }
|
979 |
|
980 | function normalizeRawPackument({
|
981 | rawPackument
|
982 | }) {
|
983 | const {
|
984 | _id: id,
|
985 | 'dist-tags': distTags,
|
986 | time,
|
987 | license: rawLicense,
|
988 | repository: rawRepository
|
989 | } = rawPackument;
|
990 | const license = normalizeRawLicense({
|
991 | rawLicense
|
992 | });
|
993 | const gitRepository = normalizeRawRepository({
|
994 | rawRepository
|
995 | });
|
996 | const versionsToTimestamps = Object.fromEntries(Object.entries(time).filter(([key]) => {
|
997 | return !['created', 'modified'].includes(key);
|
998 | }));
|
999 | return { ...rawPackument,
|
1000 | id,
|
1001 | distTags,
|
1002 | versionsToTimestamps,
|
1003 | license,
|
1004 | gitRepository
|
1005 | };
|
1006 | }
|
1007 |
|
1008 | /**
|
1009 | * `getPackument` returns the packument (package document) containing
|
1010 | * all the metadata about a package present on the registry.
|
1011 | *
|
1012 | * @param name - package name
|
1013 | * @param registry - URL of the registry (default: npm registry)
|
1014 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
1015 | * @param cached - accept cached responses (default: `true`)
|
1016 | *
|
1017 | * @example
|
1018 | * Get the packument for package `query-registry` from the npm registry:
|
1019 | *
|
1020 | * ```typescript
|
1021 | * import { getPackument } from 'query-registry';
|
1022 | *
|
1023 | * (async () => {
|
1024 | * const packument = await getPackument({ name: 'query-registry' });
|
1025 | *
|
1026 | * // Output: 'query-registry'
|
1027 | * console.log(packument.name);
|
1028 | * })();
|
1029 | * ```
|
1030 | *
|
1031 | * @see {@link Packument}
|
1032 | * @see {@link RawPackument}
|
1033 | * @see {@link npmRegistry}
|
1034 | * @see {@link npmRegistryMirrors}
|
1035 | */
|
1036 |
|
1037 | async function getPackument({
|
1038 | name,
|
1039 | registry,
|
1040 | mirrors,
|
1041 | cached
|
1042 | }) {
|
1043 | const rawPackument = await getRawPackument({
|
1044 | name,
|
1045 | registry,
|
1046 | mirrors,
|
1047 | cached
|
1048 | });
|
1049 | return normalizeRawPackument({
|
1050 | rawPackument
|
1051 | });
|
1052 | }
|
1053 |
|
1054 | /**
|
1055 | * `getRegistryDownloads` returns the number of downloads for all registry packages
|
1056 | * in a given time period.
|
1057 | *
|
1058 | * @param period - time period in which downloads happened (default: `last-week`)
|
1059 | * @param registryDownloadsAPI - URL of the registry's downloads API (default: npm registry)
|
1060 | * @param cached - accept cached responses (default: `true`)
|
1061 | *
|
1062 | * @example
|
1063 | * Get the weekly downloads for the npm registry:
|
1064 | *
|
1065 | * ```typescript
|
1066 | * import { getRegistryDownloads } from 'query-registry';
|
1067 | *
|
1068 | * (async () => {
|
1069 | * const downloads = await getRegistryDownloads();
|
1070 | *
|
1071 | * // Output: 'number'
|
1072 | * console.log(typeof downloads.downloads);
|
1073 | * })();
|
1074 | * ```
|
1075 | *
|
1076 | * @example
|
1077 | * Get the monthly downloads for the npm registry:
|
1078 | *
|
1079 | * ```typescript
|
1080 | * import { getRegistryDownloads } from 'query-registry';
|
1081 | *
|
1082 | * (async () => {
|
1083 | * const downloads = await getRegistryDownloads({ period: 'last-month' });
|
1084 | *
|
1085 | * // Output: 'number'
|
1086 | * console.log(typeof downloads.downloads);
|
1087 | * })();
|
1088 | * ```
|
1089 | *
|
1090 | * @see {@link RegistryDownloads}
|
1091 | * @see {@link DownloadPeriod}
|
1092 | * @see {@link npmRegistryDownloadsAPI}
|
1093 | * @see {@link https://github.com/npm/registry/blob/master/docs/download-counts.md#point-values}
|
1094 | */
|
1095 |
|
1096 | async function getRegistryDownloads({
|
1097 | period: rawDownloadPeriod,
|
1098 | registryDownloadsAPI,
|
1099 | cached
|
1100 | } = {}) {
|
1101 | const period = normalizeRawDownloadPeriod({
|
1102 | rawDownloadPeriod
|
1103 | });
|
1104 | const endpoint = `/downloads/point/${period}`;
|
1105 | return fetchDownloadsFromRegistry({
|
1106 | endpoint,
|
1107 | registryDownloadsAPI,
|
1108 | cached
|
1109 | });
|
1110 | }
|
1111 |
|
1112 | /**
|
1113 | * `getRegistryMetadata` returns the metadata describing the registry itself.
|
1114 | *
|
1115 | * @param registry - URL of the registry (default: npm registry)
|
1116 | * @param cached - accept cached responses (default: `true`)
|
1117 | *
|
1118 | * @example
|
1119 | * Get the metadata for the npm registry:
|
1120 | *
|
1121 | * ```typescript
|
1122 | * import { getRegistryMetadata } from 'query-registry';
|
1123 | *
|
1124 | * (async () => {
|
1125 | * const metadata = await getRegistryMetadata();
|
1126 | *
|
1127 | * // Output: 'registry'
|
1128 | * console.log(metadata.db_name);
|
1129 | * })();
|
1130 | * ```
|
1131 | *
|
1132 | * @example
|
1133 | * Get the metadata for a custom registry:
|
1134 | *
|
1135 | * ```typescript
|
1136 | * import { getRegistryMetadata } from 'query-registry';
|
1137 | *
|
1138 | * (async () => {
|
1139 | * const metadata = await getRegistryMetadata({ registry: 'https://example.com' });
|
1140 | * })();
|
1141 | * ```
|
1142 | *
|
1143 | * @see {@link RegistryMetadata}
|
1144 | * @see {@link npmRegistry}
|
1145 | */
|
1146 |
|
1147 | async function getRegistryMetadata({
|
1148 | registry,
|
1149 | cached
|
1150 | } = {}) {
|
1151 | const endpoint = '/';
|
1152 | return fetchFromRegistry({
|
1153 | registry,
|
1154 | mirrors: [],
|
1155 | endpoint,
|
1156 | cached
|
1157 | });
|
1158 | }
|
1159 |
|
1160 | function normalizeRawSearchCriteria({
|
1161 | rawSearchCriteria
|
1162 | }) {
|
1163 | // Convert SearchCriteria to a URL query string
|
1164 | return Object.entries(rawSearchCriteria).filter(([, value]) => ['string', 'number'].includes(typeof value)).map(([key, value]) => `${key}=${value}`).join('&');
|
1165 | }
|
1166 |
|
1167 | /**
|
1168 | * `searchPackages` returns the packages corresponding to a given query.
|
1169 | *
|
1170 | * @param query - one or more search criteria
|
1171 | * @param registry - URL of the registry (default: npm registry)
|
1172 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
1173 | * @param cached - accept cached responses (default: `true`)
|
1174 | *
|
1175 | * @example
|
1176 | * Get the search results for text query `query-registry` from the npm registry:
|
1177 | *
|
1178 | * ```typescript
|
1179 | * import { searchPackages } from 'query-registry';
|
1180 | *
|
1181 | * (async () => {
|
1182 | * const results = await searchPackages({ query: { text: 'query-registry' } });
|
1183 | *
|
1184 | * // Output: 'query-registry'
|
1185 | * console.log(results.objects[0].package.name);
|
1186 | * })();
|
1187 | * ```
|
1188 | *
|
1189 | * @see {@link SearchResults}
|
1190 | * @see {@link SearchCriteria}
|
1191 | * @see {@link npmRegistry}
|
1192 | * @see {@link npmRegistryMirrors}
|
1193 | */
|
1194 |
|
1195 | async function searchPackages({
|
1196 | query: rawSearchCriteria,
|
1197 | registry,
|
1198 | mirrors,
|
1199 | cached
|
1200 | }) {
|
1201 | const endpoint = '/-/v1/search';
|
1202 | const query = normalizeRawSearchCriteria({
|
1203 | rawSearchCriteria
|
1204 | });
|
1205 | return fetchFromRegistry({
|
1206 | endpoint,
|
1207 | query,
|
1208 | registry,
|
1209 | mirrors,
|
1210 | cached
|
1211 | });
|
1212 | }
|
1213 |
|
1214 | exports.FetchError = FetchError;
|
1215 | exports.InvalidPackageNameError = InvalidPackageNameError;
|
1216 | exports.InvalidPackageVersionError = InvalidPackageVersionError;
|
1217 | exports.cloudflareRegistry = cloudflareRegistry;
|
1218 | exports.getAbbreviatedPackument = getAbbreviatedPackument;
|
1219 | exports.getDailyPackageDownloads = getDailyPackageDownloads;
|
1220 | exports.getDailyRegistryDownloads = getDailyRegistryDownloads;
|
1221 | exports.getPackageDownloads = getPackageDownloads;
|
1222 | exports.getPackageManifest = getPackageManifest;
|
1223 | exports.getPackument = getPackument;
|
1224 | exports.getRawAbbreviatedPackument = getRawAbbreviatedPackument;
|
1225 | exports.getRawPackageManifest = getRawPackageManifest;
|
1226 | exports.getRawPackument = getRawPackument;
|
1227 | exports.getRegistryDownloads = getRegistryDownloads;
|
1228 | exports.getRegistryMetadata = getRegistryMetadata;
|
1229 | exports.npmRegistry = npmRegistry;
|
1230 | exports.npmRegistryDownloadsAPI = npmRegistryDownloadsAPI;
|
1231 | exports.npmRegistryMirrors = npmRegistryMirrors;
|
1232 | exports.searchPackages = searchPackages;
|
1233 | exports.yarnRegistry = yarnRegistry;
|
1234 | //# sourceMappingURL=query-registry.cjs.development.js.map
|