npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@osrs-tracker/hiscores

v6.0.0

Published

Helpers for OSRS hiscores and the world list: a hiscore client, a player count from the world list, the mapping from Jagex's JSON and snapshot diffs

Readme

@osrs-tracker/hiscores · NPM package GitHub license

Helpers for Old School RuneScape hiscores in OSRS Tracker: a client for Jagex's JSON hiscores, the mapping from Jagex's JSON to the domain model (HiscoreEntry in @osrs-tracker/models), and comparing two hiscore snapshots, plus a client for Jagex's world list that counts the players online. Import the skill and activity names (SkillEnum, ActivityEnum) and the XP helpers from @osrs-tracker/models.

A HiscoreEntry keys skills and activities by name. A value is { rank, level, xp } / { rank, score } with rank: null when unranked (below Jagex's ranking cut-off) but with xp or a score, null when there's no xp or score, and a missing key when the name wasn't on the hiscores. Overall is never null. Names Jagex adds come through before SkillEnum or ActivityEnum know them: the enums are for looking up known names only.

Install

npm install @osrs-tracker/hiscores @osrs-tracker/models

Usage

import { getOverallXpDiff, hiscoreDiff } from '@osrs-tracker/hiscores';
import { calculateXPForSkillLevel } from '@osrs-tracker/models';

// What changed between two snapshots (a `HiscoreDiff`): keyed over both entries' names, every value a number; `null` or
// missing counts as 0, except a skill's level, which counts as 1
const gains = hiscoreDiff(todayEntry, lastWeekEntry);
const overallXpGained = getOverallXpDiff(todayEntry, lastWeekEntry);

calculateXPForSkillLevel(99); // 13034431

Fetching a hiscore (Node 18+ or a browser; pass fetch to use your own client). getHiscore never throws: every outcome is a result.

import { getHiscore } from '@osrs-tracker/hiscores';

const result = await getHiscore({ baseUrl: 'https://secure.runescape.com', username: 'Lynx Titan' });
if (result.status === 'found') {
  const entry = { date: new Date(), scrapingOffset: 0, ...result.hiscore }; // a HiscoreEntry
  console.log(entry.skills.Overall, result.layout.skills); // layout: the names in Jagex's order
} else if (result.status === 'notFound')
  console.log('Not on the hiscores'); // HTTP 404/400, don't retry
else console.log(`Failed: ${result.reason}`); // worth retrying

Options: table (default 'hiscore_oldschool'; 'hiscore_oldschool_ironman', 'hiscore_oldschool_ultimate' or 'hiscore_oldschool_hardcore_ironman' for the ironman tables), fetch (default the global fetch) and timeoutMs (default 10 seconds, after which the result is failed).

Already have Jagex's index_lite.json (typed JagexHiscoreJson), e.g. from a proxy? Map it the same way:

import { fromJagex, JagexHiscoreJson } from '@osrs-tracker/hiscores';

const { skills, activities, layout } = fromJagex(json as JagexHiscoreJson);

Counting the players online from Jagex's world list (Node 18+; www.runescape.com sends no CORS headers, so not from a browser). fetchWorldList throws on a non-2xx response, a network error or the timeout, and parseWorldList throws when the response doesn't match the expected format. Don't poll it faster than once a minute.

import { fetchWorldList, parseWorldList } from '@osrs-tracker/hiscores';

const counts = parseWorldList(await fetchWorldList({ userAgent: 'github:me/my-app' }));
const playerCount = { date: new Date(), ...counts }; // a PlayerCount: total, members, f2p, worlds, byLocation

Options: fetch (default the global fetch), timeoutMs (default 10 seconds) and userAgent (not sent by default).

Development

OSRS Tracker was originally built entirely without AI assistance. Since October 2026, I've started using Claude, Anthropic's AI coding assistant, to help improve development speed and reliability.