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

matrix-workouts-core

v0.8.0

Published

Parser, API client and export format for Matrix / Johnson Fitness workout records

Readme

matrix-workouts-core

npm CI license

Read what a Matrix / Johnson Fitness exercise bike actually recorded.

The dashboard at matrixworkouts.jfit.co shows six tiles per ride: distance, average incline, average heart rate, calories, duration, average speed. The record behind that page also carries a sample every 10 seconds with power (watts), console resistance level and cadence (rpm) — none of which appears anywhere in the site's UI. This package parses those records, talks to the API that holds the full history, and writes a lossless export document. Those three fields are why it exists.

It has no runtime dependencies and touches no platform: no DOM, no chrome.*, no node:, no fetch. A test asserts that on every run. Node-only helpers live behind a separate entry point, so a browser bundle cannot pull them in by accident.

Install

npm install matrix-workouts-core

Node 20 or newer. ESM only.

Quickstart

import { toWorkout, heartRateStats, fetchWorkoutHistory } from "matrix-workouts-core";

const workout = toWorkout(record);          // one raw record -> normalized model
workout.samples[0]?.powerWatts;             // the thing the site never shows
heartRateStats(workout.samples);            // dropout-filtered, with the count

// The API needs a fetch; it never calls one itself.
const { workouts, truncated, skipped } = await fetchWorkoutHistory(credentials, myFetch);

Under Node, the fetch adapter is already written:

import { httpFetch, downloadHistory } from "matrix-workouts-core/node";
import { loginWithXid } from "matrix-workouts-core";

const credentials = await loginWithXid({ xid, pin }, httpFetch);
const result = await downloadHistory(credentials, { outDir: "history", split: true });

Download a whole history

The site's own list page holds roughly the current week and errors on anything older. The API has everything, so the package ships a CLI that signs in and takes the lot:

cp .env.example .env              # your xid and passcode
npx matrix-workouts-history       # -> history/raw-history-<timestamp>.json
npx matrix-workouts-history --split    # plus one export document per ride
npx matrix-workouts-history --out data # somewhere other than history/

| Variable | Required | Meaning | |---|---|---| | MATRIX_XID | yes | the member number printed on the gym tag | | MATRIX_PIN | yes | the numeric passcode |

Read from .env in the working directory or from the environment; the environment wins. Used for one sign-in request, never written to the output and never printed.

What it exports

| Entry point | Holds | |---|---| | matrix-workouts-core | toWorkout, loadCachedWorkouts, findWorkout, heartRateStats, flagHeartRateDropouts, controlSignature, programMode, targetWattBlocks, sprint8Segments, shareCard, renderShareCardSvg, fetchWorkoutHistory, loginWithXid, readCredentials, workoutExport, workoutTcx, workoutFit, workoutTitle, stravaUpload, bestEffort, bestAmong, beats, shownWatts, powerCurve, personalRecords, rideRecords, and the Workout / Sample types | | matrix-workouts-core/node | httpFetch, downloadHistory, readRawHistoryFile, loadEnvFile — real fetch, real files |

Units are in the names (powerWatts, cadenceRpm, distanceMeters), because the upstream names are ambiguous and one of them — averageDistance, which is cumulative — is actively misleading. Every record keeps its untouched original under raw, so an export is lossless even where this model has no name for a field.

shareCard(workout) decides what a shareable workout image says — one line and one big number per console program — and renderShareCardSvg(card, { format: "story" }) lays it out as an SVG string, 1080 × 1920 or 1080 × 1350 ("feed"). Drawing it is yours: it names Doto and Instrument Sans and does not carry them.

powerCurve(workouts) gives the best mean power across a history for each duration, from 10 s to an hour, and names the ride each one came from. personalRecords(workouts, { recentSince }) gives each duration's all-time best next to its recent best. rideRecords(workout, history) says, for each duration, whether one ride set a first effort, a new record, or neither against the rides before it. Windows are whole 10 s samples and never include the console's partial final sample. Experimental: this API may change before 1.0.

MATRIX_API.md documents the upstream API: endpoints, wire shapes, units, and which parts are verified rather than read out of the site's bundle.

Privacy and scope

Scope: indoor bikes — upright and recumbent. Treadmill and rower records parse, but nothing here is tuned for them.

This library reads one account's own data and sends it nowhere except the API it came from. The CLI handles a passcode for exactly one sign-in request and never stores, logs or prints it; downloads are written 0600 because they are the rider's heart rate. Where you point --out is where that data lives — choose accordingly.

Development

npm test         # vitest
npm run typecheck
npm run build    # -> dist/

All three must pass before committing. See AGENTS.md for the decisions and rules behind the code, and CHANGELOG.md for the release history.

Cutting a release:

npm run release -- patch     # bumps, tags, packs; refuses without a CHANGELOG entry
git push origin main:main --follow-tags
npm publish --access public

Part of matrix-workouts

| | | |---|---| | matrix-workouts-core | this package: parser, API client, export format | | matrix-workouts-chrome | the Chrome extension that puts it on screen | | matrix-workouts-mcp | an MCP server, so an AI agent can ask about your rides |

MIT licensed. Unaffiliated with Matrix Fitness or Johnson Health Tech.