Grid jsDelivr stats

author(s): Torstein Hønsi

by stitot

HTML

<script src="https://code.highcharts.com/highcharts.js"></script>

<div id="container" class="container"></div>
<div id="container-npm"class="container"></div>

CSS

body {
  background: #fff;
}

.container {
  height: 400px;
  margin: 20px;
}

JavaScript

(async () => {
  const JSDELIVR_API_BASE = "https://data.jsdelivr.com/v1/stats/packages/npm";
  const NPM_API_BASE = "https://api.npmjs.org/downloads/range";
  const NPM_REGISTRY_BASE = "https://registry.npmjs.org";
  const PKG = "@highcharts/grid-lite";
  const FILE = "/grid-lite.js";
  const PERIOD = "quarter";
  const ENCODED_PKG = encodeURIComponent(PKG);

  const compareVersions = (a, b) => {
    const pa = a.split(".").map(Number),
      pb = b.split(".").map(Number);
    return pa[0] - pb[0] || pa[1] - pb[1] || pa[2] - pb[2];
  };

  const fetchJSON = async (url, label) => {
    const res = await fetch(url);
    if (!res.ok) throw new Error(`Failed to fetch ${label}: ${res.status}`);
    return res.json();
  };

  const getNpmPublishDates = async () => {
    const json = await fetchJSON(`${NPM_REGISTRY_BASE}/${ENCODED_PKG}`, "NPM metadata");
    return json.time || {};
  };

  const getVersions = async () => {
    const json = await fetchJSON(`${JSDELIVR_API_BASE}/${ENCODED_PKG}/versions?period=${PERIOD}`, "jsDelivr versions");
    return json.map((v) => v.version).sort(compareVersions);
  };

  const getFileStats = async (version) => {
    const res = await fetch(`${JSDELIVR_API_BASE}/${PKG}@${version}/files?period=${PERIOD}`);
    if (!res.ok) return null;

    const files = await res.json();
    const entry = files.find((f) => f.name === FILE);
    if (!entry) return null;

    return {
      version,
      dates: Object.fromEntries(Object.entries(entry.hits.dates).map(([d, v]) => [d, v === 0 ? null : v])),
    };
  };

  const computeMovingAverage = (data, windowSize = 7) => {
    const result = [];
    for (let i = 0; i < data.length; i++) {
      const window = data.slice(Math.max(0, i - windowSize + 1), i + 1);
      const avg = window.reduce((sum, [, val]) => sum + val, 0) / window.length;
      result.push([data[i][0], Math.round(avg)]);
    }
    return result;
  };

  const renderChart =...