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 urlJoin = require('url-join');
|
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 urlJoin__default = /*#__PURE__*/_interopDefaultLegacy(urlJoin);
|
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(base => urlJoin__default["default"](base, endpoint, query ? `?${query}` : ''));
|
216 | let lastError;
|
217 |
|
218 | for (const url of urls) {
|
219 | try {
|
220 | const json = await fetch({
|
221 | url,
|
222 | headers,
|
223 | cached
|
224 | });
|
225 | return json;
|
226 | } catch (err) {
|
227 | // Keep last fetch error
|
228 | lastError = err;
|
229 | }
|
230 | }
|
231 |
|
232 | log('fetchFromRegistry: cannot retrieve data from registry or mirrors: %O', {
|
233 | endpoint,
|
234 | headers,
|
235 | query,
|
236 | registry,
|
237 | mirrors,
|
238 | lastError
|
239 | });
|
240 | throw lastError;
|
241 | }
|
242 |
|
243 | /**
|
244 | * `getRawAbbreviatedPackument` returns the abbreviated packument (package document)
|
245 | * containing only the metadata necessary to install a package present on the registry.
|
246 | *
|
247 | * Note: the abbreviated packument is returned as retrieved from the registry.
|
248 | *
|
249 | * @param name - package name
|
250 | * @param registry - URL of the registry (default: npm registry)
|
251 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
252 | * @param cached - accept cached responses (default: `true`)
|
253 | *
|
254 | * @example
|
255 | * Get the abbreviated packument for package `query-registry` from the npm registry:
|
256 | *
|
257 | * ```typescript
|
258 | * import { getRawAbbreviatedPackument } from 'query-registry';
|
259 | *
|
260 | * (async () => {
|
261 | * const packument = await getRawAbbreviatedPackument({ name: 'query-registry' });
|
262 | *
|
263 | * // Output: 'query-registry'
|
264 | * console.log(packument.name);
|
265 | * })();
|
266 | * ```
|
267 | *
|
268 | * @see {@link RawAbbreviatedPackument}
|
269 | * @see {@link npmRegistry}
|
270 | * @see {@link npmRegistryMirrors}
|
271 | */
|
272 |
|
273 | async function getRawAbbreviatedPackument({
|
274 | name,
|
275 | registry,
|
276 | mirrors,
|
277 | cached
|
278 | }) {
|
279 | assertValidPackageName({
|
280 | name
|
281 | });
|
282 | const endpoint = `/${name}`;
|
283 | const headers = {
|
284 | Accept: 'application/vnd.npm.install-v1+json'
|
285 | };
|
286 | return fetchFromRegistry({
|
287 | endpoint,
|
288 | headers,
|
289 | registry,
|
290 | mirrors,
|
291 | cached
|
292 | });
|
293 | }
|
294 |
|
295 | /**
|
296 | * `getAbbreviatedPackument` returns the abbreviated packument (package document)
|
297 | * containing only the metadata necessary to install a package present on the registry.
|
298 | *
|
299 | * @remarks
|
300 | * To get all the metadata (full packument) about a package see {@link getPackument}.
|
301 | *
|
302 | * @param name - package name
|
303 | * @param registry - URL of the registry (default: npm registry)
|
304 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
305 | * @param cached - accept cached responses (default: `true`)
|
306 | *
|
307 | * @example
|
308 | * Get the abbreviated packument for package `query-registry` from the npm registry:
|
309 | *
|
310 | * ```typescript
|
311 | * import { getAbbreviatedPackument } from 'query-registry';
|
312 | *
|
313 | * (async () => {
|
314 | * const packument = await getAbbreviatedPackument({ name: 'query-registry' });
|
315 | *
|
316 | * // Output: 'query-registry'
|
317 | * console.log(packument.name);
|
318 | * })();
|
319 | * ```
|
320 | *
|
321 | * @see {@link AbbreviatedPackument}
|
322 | * @see {@link RawAbbreviatedPackument}
|
323 | * @see {@link npmRegistry}
|
324 | * @see {@link npmRegistryMirrors}
|
325 | */
|
326 |
|
327 | async function getAbbreviatedPackument({
|
328 | name,
|
329 | registry,
|
330 | mirrors,
|
331 | cached
|
332 | }) {
|
333 | const rawAbbreviatedPackument = await getRawAbbreviatedPackument({
|
334 | name,
|
335 | registry,
|
336 | mirrors,
|
337 | cached
|
338 | });
|
339 | return normalizeRawAbbreviatedPackument({
|
340 | rawAbbreviatedPackument
|
341 | });
|
342 | }
|
343 |
|
344 | async function fetchDownloadsFromRegistry({
|
345 | endpoint,
|
346 | registryDownloadsAPI = npmRegistryDownloadsAPI,
|
347 | cached
|
348 | }) {
|
349 | return fetchFromRegistry({
|
350 | endpoint,
|
351 | registry: registryDownloadsAPI,
|
352 | mirrors: [],
|
353 | cached
|
354 | });
|
355 | }
|
356 |
|
357 | function normalizeRawDownloadPeriod({
|
358 | rawDownloadPeriod = 'last-week'
|
359 | }) {
|
360 | if (typeof rawDownloadPeriod === 'string') {
|
361 | return rawDownloadPeriod;
|
362 | }
|
363 |
|
364 | if (rawDownloadPeriod instanceof Date) {
|
365 | return getDay(rawDownloadPeriod);
|
366 | }
|
367 |
|
368 | const {
|
369 | start,
|
370 | end
|
371 | } = rawDownloadPeriod;
|
372 | return `${getDay(start)}:${getDay(end)}`;
|
373 | }
|
374 |
|
375 | function getDay(date) {
|
376 | return date.toISOString().split('T')[0];
|
377 | }
|
378 |
|
379 | /**
|
380 | * `getDailyPackageDownloads` returns the number of downloads for a package
|
381 | * for each day in a given time period.
|
382 | *
|
383 | * @param name - package name
|
384 | * @param period - time period in which downloads happened (default: `last-week`)
|
385 | * @param registryDownloadsAPI - URL of the registry's downloads API (default: npm registry)
|
386 | * @param cached - accept cached responses (default: `true`)
|
387 | *
|
388 | * @example
|
389 | * Get the day by day weekly downloads for package `query-registry` from the npm registry:
|
390 | *
|
391 | * ```typescript
|
392 | * import { getDailyPackageDownloads } from 'query-registry';
|
393 | *
|
394 | * (async () => {
|
395 | * const downloads = await getDailyPackageDownloads({ name: 'query-registry' });
|
396 | *
|
397 | * // Output: 'query-registry'
|
398 | * console.log(downloads.package);
|
399 | *
|
400 | * // Output: 'number'
|
401 | * console.log(typeof downloads.downloads[0].downloads);
|
402 | * })();
|
403 | * ```
|
404 | *
|
405 | * @example
|
406 | * Get the day by day monthly downloads for package `query-registry` from the npm registry:
|
407 | *
|
408 | * ```typescript
|
409 | * import { getDailyPackageDownloads } from 'query-registry';
|
410 | *
|
411 | * (async () => {
|
412 | * const downloads = await getDailyPackageDownloads({ name: 'query-registry', period: 'last-month' });
|
413 | *
|
414 | * // Output: 'query-registry'
|
415 | * console.log(downloads.package);
|
416 | *
|
417 | * // Output: 'number'
|
418 | * console.log(typeof downloads.downloads[0].downloads);
|
419 | * })();
|
420 | * ```
|
421 | *
|
422 | * @see {@link DailyPackageDownloads}
|
423 | * @see {@link DownloadPeriod}
|
424 | * @see {@link npmRegistryDownloadsAPI}
|
425 | * @see {@link https://github.com/npm/registry/blob/master/docs/download-counts.md#ranges}
|
426 | */
|
427 |
|
428 | async function getDailyPackageDownloads({
|
429 | name,
|
430 | period: rawDownloadPeriod,
|
431 | registryDownloadsAPI,
|
432 | cached
|
433 | }) {
|
434 | assertValidPackageName({
|
435 | name
|
436 | });
|
437 | const period = normalizeRawDownloadPeriod({
|
438 | rawDownloadPeriod
|
439 | });
|
440 | const endpoint = `/downloads/range/${period}/${name}`;
|
441 | return fetchDownloadsFromRegistry({
|
442 | endpoint,
|
443 | registryDownloadsAPI,
|
444 | cached
|
445 | });
|
446 | }
|
447 |
|
448 | /**
|
449 | * `getDailyRegistryDownloads` returns the number of downloads for all registry packages
|
450 | * for each day in a given time period.
|
451 | *
|
452 | * @param period - time period in which downloads happened (default: `last-week`)
|
453 | * @param registryDownloadsAPI - URL of the registry's downloads API (default: npm registry)
|
454 | * @param cached - accept cached responses (default: `true`)
|
455 | *
|
456 | * @example
|
457 | * Get the day by day weekly downloads for the npm registry:
|
458 | *
|
459 | * ```typescript
|
460 | * import { getDailyRegistryDownloads } from 'query-registry';
|
461 | *
|
462 | * (async () => {
|
463 | * const downloads = await getDailyRegistryDownloads();
|
464 | *
|
465 | * // Output: 'number'
|
466 | * console.log(typeof downloads.downloads[0].downloads);
|
467 | * })();
|
468 | * ```
|
469 | *
|
470 | * @example
|
471 | * Get the day by day monthly downloads for the npm registry:
|
472 | *
|
473 | * ```typescript
|
474 | * import { getDailyRegistryDownloads } from 'query-registry';
|
475 | *
|
476 | * (async () => {
|
477 | * const downloads = await getDailyRegistryDownloads({ period: 'last-month' });
|
478 | *
|
479 | * // Output: 'number'
|
480 | * console.log(typeof downloads.downloads[0].downloads);
|
481 | * })();
|
482 | * ```
|
483 | *
|
484 | * @see {@link DailyRegistryDownloads}
|
485 | * @see {@link DownloadPeriod}
|
486 | * @see {@link npmRegistryDownloadsAPI}
|
487 | * @see {@link https://github.com/npm/registry/blob/master/docs/download-counts.md#ranges}
|
488 | */
|
489 |
|
490 | async function getDailyRegistryDownloads({
|
491 | period: rawDownloadPeriod,
|
492 | registryDownloadsAPI,
|
493 | cached
|
494 | } = {}) {
|
495 | const period = normalizeRawDownloadPeriod({
|
496 | rawDownloadPeriod
|
497 | });
|
498 | const endpoint = `/downloads/range/${period}`;
|
499 | return fetchDownloadsFromRegistry({
|
500 | endpoint,
|
501 | registryDownloadsAPI,
|
502 | cached
|
503 | });
|
504 | }
|
505 |
|
506 | /**
|
507 | * `getPackageDownloads` returns the number of downloads for a package
|
508 | * in a given time period.
|
509 | *
|
510 | * @param name - package name
|
511 | * @param period - time period in which downloads happened (default: `last-week`)
|
512 | * @param registryDownloadsAPI - URL of the registry's downloads API (default: npm registry)
|
513 | * @param cached - accept cached responses (default: `true`)
|
514 | *
|
515 | * @example
|
516 | * Get the weekly downloads for package `query-registry` from the npm registry:
|
517 | *
|
518 | * ```typescript
|
519 | * import { getPackageDownloads } from 'query-registry';
|
520 | *
|
521 | * (async () => {
|
522 | * const downloads = await getPackageDownloads({ name: 'query-registry' });
|
523 | *
|
524 | * // Output: 'query-registry'
|
525 | * console.log(downloads.package);
|
526 | *
|
527 | * // Output: 'number'
|
528 | * console.log(typeof downloads.downloads);
|
529 | * })();
|
530 | * ```
|
531 | *
|
532 | * @example
|
533 | * Get the monthly downloads for package `query-registry` from the npm registry:
|
534 | *
|
535 | * ```typescript
|
536 | * import { getPackageDownloads } from 'query-registry';
|
537 | *
|
538 | * (async () => {
|
539 | * const downloads = await getPackageDownloads({ name: 'query-registry', period: 'last-month' });
|
540 | *
|
541 | * // Output: 'query-registry'
|
542 | * console.log(downloads.package);
|
543 | *
|
544 | * // Output: 'number'
|
545 | * console.log(typeof downloads.downloads);
|
546 | * })();
|
547 | * ```
|
548 | *
|
549 | * @see {@link PackageDownloads}
|
550 | * @see {@link DownloadPeriod}
|
551 | * @see {@link npmRegistryDownloadsAPI}
|
552 | * @see {@link https://github.com/npm/registry/blob/master/docs/download-counts.md#point-values}
|
553 | */
|
554 |
|
555 | async function getPackageDownloads({
|
556 | name,
|
557 | period: rawDownloadPeriod,
|
558 | registryDownloadsAPI,
|
559 | cached
|
560 | }) {
|
561 | assertValidPackageName({
|
562 | name
|
563 | });
|
564 | const period = normalizeRawDownloadPeriod({
|
565 | rawDownloadPeriod
|
566 | });
|
567 | const endpoint = `/downloads/point/${period}/${name}`;
|
568 | return fetchDownloadsFromRegistry({
|
569 | endpoint,
|
570 | registryDownloadsAPI,
|
571 | cached
|
572 | });
|
573 | }
|
574 |
|
575 | function extractRawPackageManifest({
|
576 | rawPackument,
|
577 | version = 'latest'
|
578 | }) {
|
579 | var _distTags$version;
|
580 |
|
581 | const {
|
582 | name,
|
583 | 'dist-tags': distTags,
|
584 | versions
|
585 | } = rawPackument;
|
586 | const versionNumber = (_distTags$version = distTags[version]) != null ? _distTags$version : version;
|
587 | const manifest = versions[versionNumber];
|
588 |
|
589 | if (!manifest) {
|
590 | log('getPackageManifest: invalid package version: %O', {
|
591 | name,
|
592 | version
|
593 | });
|
594 | throw new InvalidPackageVersionError(`invalid package version: '${name}@${version}'`);
|
595 | }
|
596 |
|
597 | return manifest;
|
598 | }
|
599 |
|
600 | /**
|
601 | * `getRawPackument` returns the packument (package document) containing
|
602 | * all the metadata about a package present on the registry.
|
603 | *
|
604 | * Note: the packument is returned as retrieved from the registry.
|
605 | *
|
606 | * @param name - package name
|
607 | * @param registry - URL of the registry (default: npm registry)
|
608 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
609 | * @param cached - accept cached responses (default: `true`)
|
610 | *
|
611 | * @example
|
612 | * Get the packument for package `query-registry` from the npm registry:
|
613 | *
|
614 | * ```typescript
|
615 | * import { getRawPackument } from 'query-registry';
|
616 | *
|
617 | * (async () => {
|
618 | * const packument = await getRawPackument({ name: 'query-registry' });
|
619 | *
|
620 | * // Output: 'query-registry'
|
621 | * console.log(packument.name);
|
622 | * })();
|
623 | * ```
|
624 | *
|
625 | * @see {@link RawPackument}
|
626 | * @see {@link npmRegistry}
|
627 | * @see {@link npmRegistryMirrors}
|
628 | */
|
629 |
|
630 | async function getRawPackument({
|
631 | name,
|
632 | registry,
|
633 | mirrors,
|
634 | cached
|
635 | }) {
|
636 | assertValidPackageName({
|
637 | name
|
638 | });
|
639 | const endpoint = `/${name}`;
|
640 | return fetchFromRegistry({
|
641 | endpoint,
|
642 | registry,
|
643 | mirrors,
|
644 | cached
|
645 | });
|
646 | }
|
647 |
|
648 | /**
|
649 | * `getRawPackageManifest` returns the manifest describing
|
650 | * a specific version of a package.
|
651 | *
|
652 | * Note: the manifest is returned as retrieved from the registry.
|
653 | *
|
654 | * @param name - package name
|
655 | * @param version - package version (default: `latest`)
|
656 | * @param registry - URL of the registry (default: npm registry)
|
657 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
658 | * @param cached - accept cached responses (default: `true`)
|
659 | *
|
660 | * @example
|
661 | * Get the latest manifest for package `query-registry` from the npm registry:
|
662 | *
|
663 | * ```typescript
|
664 | * import { getRawPackageManifest } from 'query-registry';
|
665 | *
|
666 | * (async () => {
|
667 | * const manifest = await getRawPackageManifest({ name: 'query-registry' });
|
668 | *
|
669 | * // Output: 'query-registry'
|
670 | * console.log(manifest.name);
|
671 | * })();
|
672 | * ```
|
673 | *
|
674 | * @example
|
675 | * Get the manifest for package `query-registry@1.0.0` from the npm registry:
|
676 | *
|
677 | * ```typescript
|
678 | * import { getRawPackageManifest } from 'query-registry';
|
679 | *
|
680 | * (async () => {
|
681 | * const manifest = await getRawPackageManifest({ name: 'query-registry', version: '1.0.0' });
|
682 | *
|
683 | * // Output: 'query-registry'
|
684 | * console.log(manifest.name);
|
685 | *
|
686 | * // Output: '1.0.0'
|
687 | * console.log(manifest.version);
|
688 | * })();
|
689 | * ```
|
690 | *
|
691 | * @see {@link RawPackageManifest}
|
692 | * @see {@link npmRegistry}
|
693 | * @see {@link npmRegistryMirrors}
|
694 | */
|
695 |
|
696 | async function getRawPackageManifest({
|
697 | name,
|
698 | version,
|
699 | registry,
|
700 | mirrors,
|
701 | cached
|
702 | }) {
|
703 | const rawPackument = await getRawPackument({
|
704 | name,
|
705 | registry,
|
706 | mirrors,
|
707 | cached
|
708 | });
|
709 | return extractRawPackageManifest({
|
710 | rawPackument,
|
711 | version
|
712 | });
|
713 | }
|
714 |
|
715 | async function getDefinitelyTypedName({
|
716 | rawPackageManifest,
|
717 | registry,
|
718 | mirrors,
|
719 | cached
|
720 | }) {
|
721 | const {
|
722 | name,
|
723 | types,
|
724 | typings
|
725 | } = rawPackageManifest;
|
726 | const definitelyTypedName = toDefinitelyTypedName({
|
727 | name
|
728 | });
|
729 | const alreadyTyped = name === definitelyTypedName || !!types || !!typings;
|
730 |
|
731 | if (alreadyTyped) {
|
732 | return undefined;
|
733 | }
|
734 |
|
735 | let ok = false;
|
736 |
|
737 | try {
|
738 | const {
|
739 | deprecated
|
740 | } = await getRawPackageManifest({
|
741 | name: definitelyTypedName,
|
742 | registry,
|
743 | mirrors,
|
744 | cached
|
745 | });
|
746 | ok = deprecated === undefined;
|
747 | } catch {}
|
748 |
|
749 | return ok ? definitelyTypedName : undefined;
|
750 | }
|
751 | /**
|
752 | * `toDefinitelyTypedName` returns the name of the corresponding
|
753 | * DefinitelyTyped package (for example,
|
754 | * `foo` => `@types/foo`,
|
755 | * `@bar/baz` => `@types/bar__baz`).
|
756 | */
|
757 |
|
758 | function toDefinitelyTypedName({
|
759 | name
|
760 | }) {
|
761 | return name.startsWith('@types/') ? name : `@types/${name.replace('@', '').replace('/', '__')}`;
|
762 | }
|
763 |
|
764 | /**
|
765 | * `getUntypedName` returns the name of the normal package
|
766 | * corresponding to a DefinitelyTyped package.
|
767 | */
|
768 | function getUntypedName({
|
769 | name
|
770 | }) {
|
771 | if (!name.startsWith('@types/')) {
|
772 | return undefined;
|
773 | } // ['foo', undefined] or ['@bar', 'baz']
|
774 |
|
775 |
|
776 | const [scopeOrName, scopedName] = name.replace('@types/', '').split('__');
|
777 | return scopedName ? `@${scopeOrName}/${scopedName}` : scopeOrName;
|
778 | }
|
779 |
|
780 | function normalizeRawLicense({
|
781 | rawLicense
|
782 | }) {
|
783 | if (!rawLicense) {
|
784 | return undefined;
|
785 | }
|
786 |
|
787 | if (typeof rawLicense !== 'string') {
|
788 | return undefined;
|
789 | }
|
790 |
|
791 | return rawLicense;
|
792 | }
|
793 |
|
794 | function normalizeRawRepository({
|
795 | rawRepository
|
796 | }) {
|
797 | if (isRepository(rawRepository)) {
|
798 | return normalizeRepository({
|
799 | rawRepository
|
800 | });
|
801 | }
|
802 |
|
803 | if (typeof rawRepository === 'string') {
|
804 | return normalizeRepository({
|
805 | rawRepository: {
|
806 | url: rawRepository
|
807 | }
|
808 | });
|
809 | }
|
810 |
|
811 | return undefined;
|
812 | }
|
813 |
|
814 | function isRepository(rawRepository) {
|
815 | return rawRepository && typeof rawRepository === 'object' && typeof rawRepository['url'] === 'string' && ['string', 'undefined'].includes(typeof rawRepository['type']) && ['string', 'undefined'].includes(typeof rawRepository['directory']);
|
816 | }
|
817 |
|
818 | function normalizeRepository({
|
819 | rawRepository
|
820 | }) {
|
821 | const {
|
822 | url,
|
823 | directory
|
824 | } = rawRepository;
|
825 | const parsedUrl = parseGitURL({
|
826 | url
|
827 | });
|
828 |
|
829 | if (!parsedUrl) {
|
830 | return undefined;
|
831 | }
|
832 |
|
833 | return {
|
834 | type: 'git',
|
835 | url: parsedUrl,
|
836 | directory
|
837 | };
|
838 | }
|
839 |
|
840 | function parseGitURL({
|
841 | url
|
842 | }) {
|
843 | const urlWithProtocol = url.includes(':') ? // A normal URL or a shortcut like `github:user/repository`
|
844 | url : // The short form github shortcut `user/repository`
|
845 | url.includes('/') ? `github:${url}` : // Not a URL
|
846 | '';
|
847 |
|
848 | try {
|
849 | const {
|
850 | protocol,
|
851 | hostname,
|
852 | pathname
|
853 | } = new URL(urlWithProtocol);
|
854 | const cleanPathname = pathname.replace(/\.git$/, '');
|
855 |
|
856 | if (protocol === 'github:' || hostname === 'github.com') {
|
857 | return urlJoin__default["default"]('https://github.com', cleanPathname);
|
858 | }
|
859 |
|
860 | if (protocol === 'gist:' || hostname === 'gist.github.com') {
|
861 | return urlJoin__default["default"]('https://gist.github.com', cleanPathname);
|
862 | }
|
863 |
|
864 | if (protocol === 'bitbucket:' || hostname === 'bitbucket.org') {
|
865 | return urlJoin__default["default"]('https://bitbucket.org', cleanPathname);
|
866 | }
|
867 |
|
868 | if (protocol === 'gitlab:' || hostname === 'gitlab.com') {
|
869 | return urlJoin__default["default"]('https://gitlab.com', cleanPathname);
|
870 | }
|
871 |
|
872 | return urlWithProtocol;
|
873 | } catch {
|
874 | return undefined;
|
875 | }
|
876 | }
|
877 |
|
878 | async function normalizeRawPackageManifest({
|
879 | rawPackageManifest,
|
880 | rawPackument,
|
881 | registry,
|
882 | mirrors,
|
883 | cached
|
884 | }) {
|
885 | const {
|
886 | _id: id,
|
887 | name,
|
888 | version,
|
889 | license: rawLicense,
|
890 | repository: rawRepository,
|
891 | _npmUser: publisher
|
892 | } = rawPackageManifest;
|
893 | const createdAt = rawPackument.time[version];
|
894 | const license = normalizeRawLicense({
|
895 | rawLicense
|
896 | });
|
897 | const gitRepository = normalizeRawRepository({
|
898 | rawRepository
|
899 | });
|
900 | const definitelyTypedName = await getDefinitelyTypedName({
|
901 | rawPackageManifest,
|
902 | registry,
|
903 | mirrors,
|
904 | cached
|
905 | });
|
906 | const untypedName = getUntypedName({
|
907 | name
|
908 | });
|
909 | return { ...rawPackageManifest,
|
910 | id,
|
911 | createdAt,
|
912 | publisher,
|
913 | license,
|
914 | gitRepository,
|
915 | definitelyTypedName,
|
916 | untypedName
|
917 | };
|
918 | }
|
919 |
|
920 | /**
|
921 | * `getPackageManifest` returns the manifest describing
|
922 | * a specific version of a package.
|
923 | *
|
924 | * @param name - package name
|
925 | * @param version - package version (default: `latest`)
|
926 | * @param registry - URL of the registry (default: npm registry)
|
927 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
928 | * @param cached - accept cached responses (default: `true`)
|
929 | *
|
930 | * @example
|
931 | * Get the latest manifest for package `query-registry` from the npm registry:
|
932 | *
|
933 | * ```typescript
|
934 | * import { getPackageManifest } from 'query-registry';
|
935 | *
|
936 | * (async () => {
|
937 | * const manifest = await getPackageManifest({ name: 'query-registry' });
|
938 | *
|
939 | * // Output: 'query-registry'
|
940 | * console.log(manifest.name);
|
941 | * })();
|
942 | * ```
|
943 | *
|
944 | * @example
|
945 | * Get the manifest for package `query-registry@1.0.0` from the npm registry:
|
946 | *
|
947 | * ```typescript
|
948 | * import { getPackageManifest } from 'query-registry';
|
949 | *
|
950 | * (async () => {
|
951 | * const manifest = await getPackageManifest({ name: 'query-registry', version: '1.0.0' });
|
952 | *
|
953 | * // Output: 'query-registry'
|
954 | * console.log(manifest.name);
|
955 | *
|
956 | * // Output: '1.0.0'
|
957 | * console.log(manifest.version);
|
958 | * })();
|
959 | * ```
|
960 | *
|
961 | * @see {@link PackageManifest}
|
962 | * @see {@link RawPackageManifest}
|
963 | * @see {@link npmRegistry}
|
964 | * @see {@link npmRegistryMirrors}
|
965 | */
|
966 |
|
967 | async function getPackageManifest({
|
968 | name,
|
969 | version,
|
970 | registry,
|
971 | mirrors,
|
972 | cached
|
973 | }) {
|
974 | const rawPackument = await getRawPackument({
|
975 | name,
|
976 | registry,
|
977 | mirrors,
|
978 | cached
|
979 | });
|
980 | const rawPackageManifest = extractRawPackageManifest({
|
981 | rawPackument,
|
982 | version
|
983 | });
|
984 | const packageManifest = await normalizeRawPackageManifest({
|
985 | rawPackageManifest,
|
986 | rawPackument,
|
987 | registry,
|
988 | mirrors,
|
989 | cached
|
990 | });
|
991 | return packageManifest;
|
992 | }
|
993 |
|
994 | function normalizeRawPackument({
|
995 | rawPackument
|
996 | }) {
|
997 | const {
|
998 | _id: id,
|
999 | 'dist-tags': distTags,
|
1000 | time,
|
1001 | license: rawLicense,
|
1002 | repository: rawRepository
|
1003 | } = rawPackument;
|
1004 | const license = normalizeRawLicense({
|
1005 | rawLicense
|
1006 | });
|
1007 | const gitRepository = normalizeRawRepository({
|
1008 | rawRepository
|
1009 | });
|
1010 | const versionsToTimestamps = Object.fromEntries(Object.entries(time).filter(([key]) => {
|
1011 | return !['created', 'modified'].includes(key);
|
1012 | }));
|
1013 | return { ...rawPackument,
|
1014 | id,
|
1015 | distTags,
|
1016 | versionsToTimestamps,
|
1017 | license,
|
1018 | gitRepository
|
1019 | };
|
1020 | }
|
1021 |
|
1022 | /**
|
1023 | * `getPackument` returns the packument (package document) containing
|
1024 | * all the metadata about a package present on the registry.
|
1025 | *
|
1026 | * @param name - package name
|
1027 | * @param registry - URL of the registry (default: npm registry)
|
1028 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
1029 | * @param cached - accept cached responses (default: `true`)
|
1030 | *
|
1031 | * @example
|
1032 | * Get the packument for package `query-registry` from the npm registry:
|
1033 | *
|
1034 | * ```typescript
|
1035 | * import { getPackument } from 'query-registry';
|
1036 | *
|
1037 | * (async () => {
|
1038 | * const packument = await getPackument({ name: 'query-registry' });
|
1039 | *
|
1040 | * // Output: 'query-registry'
|
1041 | * console.log(packument.name);
|
1042 | * })();
|
1043 | * ```
|
1044 | *
|
1045 | * @see {@link Packument}
|
1046 | * @see {@link RawPackument}
|
1047 | * @see {@link npmRegistry}
|
1048 | * @see {@link npmRegistryMirrors}
|
1049 | */
|
1050 |
|
1051 | async function getPackument({
|
1052 | name,
|
1053 | registry,
|
1054 | mirrors,
|
1055 | cached
|
1056 | }) {
|
1057 | const rawPackument = await getRawPackument({
|
1058 | name,
|
1059 | registry,
|
1060 | mirrors,
|
1061 | cached
|
1062 | });
|
1063 | return normalizeRawPackument({
|
1064 | rawPackument
|
1065 | });
|
1066 | }
|
1067 |
|
1068 | /**
|
1069 | * `getRegistryDownloads` returns the number of downloads for all registry packages
|
1070 | * in a given time period.
|
1071 | *
|
1072 | * @param period - time period in which downloads happened (default: `last-week`)
|
1073 | * @param registryDownloadsAPI - URL of the registry's downloads API (default: npm registry)
|
1074 | * @param cached - accept cached responses (default: `true`)
|
1075 | *
|
1076 | * @example
|
1077 | * Get the weekly downloads for the npm registry:
|
1078 | *
|
1079 | * ```typescript
|
1080 | * import { getRegistryDownloads } from 'query-registry';
|
1081 | *
|
1082 | * (async () => {
|
1083 | * const downloads = await getRegistryDownloads();
|
1084 | *
|
1085 | * // Output: 'number'
|
1086 | * console.log(typeof downloads.downloads);
|
1087 | * })();
|
1088 | * ```
|
1089 | *
|
1090 | * @example
|
1091 | * Get the monthly downloads for the npm registry:
|
1092 | *
|
1093 | * ```typescript
|
1094 | * import { getRegistryDownloads } from 'query-registry';
|
1095 | *
|
1096 | * (async () => {
|
1097 | * const downloads = await getRegistryDownloads({ period: 'last-month' });
|
1098 | *
|
1099 | * // Output: 'number'
|
1100 | * console.log(typeof downloads.downloads);
|
1101 | * })();
|
1102 | * ```
|
1103 | *
|
1104 | * @see {@link RegistryDownloads}
|
1105 | * @see {@link DownloadPeriod}
|
1106 | * @see {@link npmRegistryDownloadsAPI}
|
1107 | * @see {@link https://github.com/npm/registry/blob/master/docs/download-counts.md#point-values}
|
1108 | */
|
1109 |
|
1110 | async function getRegistryDownloads({
|
1111 | period: rawDownloadPeriod,
|
1112 | registryDownloadsAPI,
|
1113 | cached
|
1114 | } = {}) {
|
1115 | const period = normalizeRawDownloadPeriod({
|
1116 | rawDownloadPeriod
|
1117 | });
|
1118 | const endpoint = `/downloads/point/${period}`;
|
1119 | return fetchDownloadsFromRegistry({
|
1120 | endpoint,
|
1121 | registryDownloadsAPI,
|
1122 | cached
|
1123 | });
|
1124 | }
|
1125 |
|
1126 | /**
|
1127 | * `getRegistryMetadata` returns the metadata describing the registry itself.
|
1128 | *
|
1129 | * @param registry - URL of the registry (default: npm registry)
|
1130 | * @param cached - accept cached responses (default: `true`)
|
1131 | *
|
1132 | * @example
|
1133 | * Get the metadata for the npm registry:
|
1134 | *
|
1135 | * ```typescript
|
1136 | * import { getRegistryMetadata } from 'query-registry';
|
1137 | *
|
1138 | * (async () => {
|
1139 | * const metadata = await getRegistryMetadata();
|
1140 | *
|
1141 | * // Output: 'registry'
|
1142 | * console.log(metadata.db_name);
|
1143 | * })();
|
1144 | * ```
|
1145 | *
|
1146 | * @example
|
1147 | * Get the metadata for a custom registry:
|
1148 | *
|
1149 | * ```typescript
|
1150 | * import { getRegistryMetadata } from 'query-registry';
|
1151 | *
|
1152 | * (async () => {
|
1153 | * const metadata = await getRegistryMetadata({ registry: 'https://example.com' });
|
1154 | * })();
|
1155 | * ```
|
1156 | *
|
1157 | * @see {@link RegistryMetadata}
|
1158 | * @see {@link npmRegistry}
|
1159 | */
|
1160 |
|
1161 | async function getRegistryMetadata({
|
1162 | registry,
|
1163 | cached
|
1164 | } = {}) {
|
1165 | const endpoint = '/';
|
1166 | return fetchFromRegistry({
|
1167 | registry,
|
1168 | mirrors: [],
|
1169 | endpoint,
|
1170 | cached
|
1171 | });
|
1172 | }
|
1173 |
|
1174 | function normalizeRawSearchCriteria({
|
1175 | rawSearchCriteria
|
1176 | }) {
|
1177 | // Convert SearchCriteria to a URL query string
|
1178 | return Object.entries(rawSearchCriteria).filter(([, value]) => ['string', 'number'].includes(typeof value)).map(([key, value]) => `${key}=${value}`).join('&');
|
1179 | }
|
1180 |
|
1181 | /**
|
1182 | * `searchPackages` returns the packages corresponding to a given query.
|
1183 | *
|
1184 | * @param query - one or more search criteria
|
1185 | * @param registry - URL of the registry (default: npm registry)
|
1186 | * @param mirrors - URLs of the registry mirrors (default: npm registry mirrors)
|
1187 | * @param cached - accept cached responses (default: `true`)
|
1188 | *
|
1189 | * @example
|
1190 | * Get the search results for text query `query-registry` from the npm registry:
|
1191 | *
|
1192 | * ```typescript
|
1193 | * import { searchPackages } from 'query-registry';
|
1194 | *
|
1195 | * (async () => {
|
1196 | * const results = await searchPackages({ query: { text: 'query-registry' } });
|
1197 | *
|
1198 | * // Output: 'query-registry'
|
1199 | * console.log(results.objects[0].package.name);
|
1200 | * })();
|
1201 | * ```
|
1202 | *
|
1203 | * @see {@link SearchResults}
|
1204 | * @see {@link SearchCriteria}
|
1205 | * @see {@link npmRegistry}
|
1206 | * @see {@link npmRegistryMirrors}
|
1207 | */
|
1208 |
|
1209 | async function searchPackages({
|
1210 | query: rawSearchCriteria,
|
1211 | registry,
|
1212 | mirrors,
|
1213 | cached
|
1214 | }) {
|
1215 | const endpoint = '/-/v1/search';
|
1216 | const query = normalizeRawSearchCriteria({
|
1217 | rawSearchCriteria
|
1218 | });
|
1219 | return fetchFromRegistry({
|
1220 | endpoint,
|
1221 | query,
|
1222 | registry,
|
1223 | mirrors,
|
1224 | cached
|
1225 | });
|
1226 | }
|
1227 |
|
1228 | exports.FetchError = FetchError;
|
1229 | exports.InvalidPackageNameError = InvalidPackageNameError;
|
1230 | exports.InvalidPackageVersionError = InvalidPackageVersionError;
|
1231 | exports.cloudflareRegistry = cloudflareRegistry;
|
1232 | exports.getAbbreviatedPackument = getAbbreviatedPackument;
|
1233 | exports.getDailyPackageDownloads = getDailyPackageDownloads;
|
1234 | exports.getDailyRegistryDownloads = getDailyRegistryDownloads;
|
1235 | exports.getPackageDownloads = getPackageDownloads;
|
1236 | exports.getPackageManifest = getPackageManifest;
|
1237 | exports.getPackument = getPackument;
|
1238 | exports.getRawAbbreviatedPackument = getRawAbbreviatedPackument;
|
1239 | exports.getRawPackageManifest = getRawPackageManifest;
|
1240 | exports.getRawPackument = getRawPackument;
|
1241 | exports.getRegistryDownloads = getRegistryDownloads;
|
1242 | exports.getRegistryMetadata = getRegistryMetadata;
|
1243 | exports.npmRegistry = npmRegistry;
|
1244 | exports.npmRegistryDownloadsAPI = npmRegistryDownloadsAPI;
|
1245 | exports.npmRegistryMirrors = npmRegistryMirrors;
|
1246 | exports.searchPackages = searchPackages;
|
1247 | exports.yarnRegistry = yarnRegistry;
|
1248 | //# sourceMappingURL=query-registry.cjs.development.js.map
|