Files
usage-statistics/dist/collectors/npm.js

113 lines
4.3 KiB
JavaScript

/**
* NPM package statistics collector with enhanced metrics
*/
const PlatformSettings = {
name: 'NPM',
};
const BASE_URL = 'https://api.npmjs.org/downloads/range';
const CHUNK_DAYS = 540; // 18 months max per request
const START_DATE = new Date('2015-01-10'); // Earliest NPM data
function formatDate(date) {
return date.toISOString().split('T')[0];
}
function addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
async function fetchChunk(start, end, packageName) {
const url = `${BASE_URL}/${formatDate(start)}:${formatDate(end)}/${packageName}`;
const res = await fetch(url);
if (!res.ok) {
throw new Error(`Failed to fetch data: ${res.status} ${res.statusText}`);
}
const json = await res.json();
return json.downloads;
}
async function getFullDownloadHistory(packageName, startDate) {
const today = new Date();
let currentStart = new Date(startDate);
let allDownloads = [];
while (currentStart < today) {
const currentEnd = addDays(currentStart, CHUNK_DAYS - 1);
const end = currentEnd > today ? today : currentEnd;
console.log(`Fetching ${formatDate(currentStart)} to ${formatDate(end)}...`);
const chunk = await fetchChunk(currentStart, end, packageName);
allDownloads = allDownloads.concat(chunk);
currentStart = addDays(end, 1); // move to next chunk
}
return Array.from(new Set(allDownloads));
}
export async function collectNpm(packageName) {
try {
// Get package info from npm registry
const packageUrl = `https://registry.npmjs.org/${packageName}`;
const packageResponse = await fetch(packageUrl);
const packageData = await packageResponse.json();
// Get download statistics
let downloadsMonthly;
let downloadsWeekly;
let downloadsDaily;
try {
// Monthly downloads
const monthlyUrl = `https://api.npmjs.org/downloads/point/last-month/${packageName}`;
const monthlyResponse = await fetch(monthlyUrl);
const monthlyData = await monthlyResponse.json();
downloadsMonthly = monthlyData.downloads || null;
}
catch (error) {
console.warn(`Could not fetch NPM monthly downloads for ${packageName}:`, error);
}
try {
// Weekly downloads
const weeklyUrl = `https://api.npmjs.org/downloads/point/last-week/${packageName}`;
const weeklyResponse = await fetch(weeklyUrl);
const weeklyData = await weeklyResponse.json();
downloadsWeekly = weeklyData.downloads || null;
}
catch (error) {
console.warn(`Could not fetch NPM weekly downloads for ${packageName}:`, error);
}
try {
// Daily downloads
const dailyUrl = `https://api.npmjs.org/downloads/point/last-day/${packageName}`;
const dailyResponse = await fetch(dailyUrl);
const dailyData = await dailyResponse.json();
downloadsDaily = dailyData.downloads || null;
}
catch (error) {
console.warn(`Could not fetch NPM daily downloads for ${packageName}:`, error);
}
const downloadsRange = await getFullDownloadHistory(packageName, new Date(packageData.time?.created || START_DATE));
const downloadsTotal = downloadsRange.reduce((acc, curr) => acc + curr.downloads, 0);
return {
platform: PlatformSettings.name,
name: packageName,
timestamp: new Date().toISOString(),
metrics: {
downloadsTotal,
downloadsMonthly,
downloadsWeekly,
downloadsDaily,
downloadsRange,
}
};
}
catch (error) {
return {
platform: PlatformSettings.name,
name: packageName,
timestamp: new Date().toISOString(),
metrics: {},
error: error instanceof Error ? error.message : String(error)
};
}
}
export async function collectNpmBatch(packageNames) {
const resultPromises = [];
for (const packageName of packageNames) {
resultPromises.push(collectNpm(packageName));
}
return Promise.all(resultPromises);
}