how-npm-am-i.js/index.js
2019-06-25 23:20:45 -06:00

116 lines
3.1 KiB
JavaScript

'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;
});
}