'use strict'; // Inspired in part by // * https://www.npmjs.com/package/npmjs-api // * https://github.com/kevva/npm-user-packages/blob/master/index.js // Official docs at // * https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#meta-endpoints // * https://github.com/npm/registry/blob/master/docs/download-counts.md var batchAsync = require('batchasync').batchAsync; var request = require('@root/request'); request = require('util').promisify(request); var hnai = exports; hnai.packages = function(username) { // I didn't bother to implement paging because... I didn't return request({ url: 'https://api.npms.io/v2/search?q=author:' + username + '&size=250&from=0', json: true }).then(function(resp) { // { total: 214, results: [ { package: { name: 'foobar' } } ] } if (resp.total > 250) { console.warn(); console.warn("You have more than 250 published packages, but I haven't implemented paging."); console.warn("I'd say please open an issue, but you're ovbiously skilled, so please PR."); console.warn(); } return resp.body.results.map(function(result) { return result.package.name; }); }); }; hnai.downloads = function(pkgnames) { // https://github.com/npm/registry/blob/master/docs/download-counts.md#bulk-queries // FYI: Earliest date is January 10, 2015 // * One-off: https://api.npmjs.org/downloads/range/{period}[/{package}] // * Bulk: https://api.npmjs.org/downloads/point/last-day/npm,express // All the batching logic var batches = []; var batch = []; pkgnames.forEach(function(pkgname) { if ('@' === pkgname[0]) { batches.push([pkgname]); return; } if (batch.length >= 128) { batches.push(batch); batch = []; } batch.push(pkgname); }); if (batch.length) { batches.push(batch); } // do the batching return batchAsync(4, batches, function(batch) { if (1 === batch.length) { return one(batch[0]); } else { return many(batch); } }).then(function(results) { // transform the results var all = []; results.forEach(function(map) { //#console.debug('batch size:', Object.keys(map).length); Object.keys(map).forEach(function(key) { if (!map[key]) { // can be 'null' for just-published packages map[key] = { downloads: 0 }; } all.push({ package: key, downloads: map[key].downloads }); }); }); // get back one big happy list return all; }); }; // these need to be simplified and transformed function one(pkgname) { return request({ url: 'https://api.npmjs.org/downloads/range/last-month/' + pkgname, json: true }).then(function(resp) { // { package: 'foobar', downloads: [{ downloads: 1, day: '2019-06-16' }] } var result = {}; result[resp.body.package] = { package: resp.body.package, downloads: resp.body.downloads.reduce(function(sum, el) { return el.downloads; }, 0) }; return result; }); } // these are exactly what we want, pretty much function many(pkgnames) { return request({ url: 'https://api.npmjs.org/downloads/point/last-month/' + pkgnames.join(','), json: true }).then(function(resp) { // { foobar: { downloads: 12 } } return resp.body; }); }