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

@the-governor-hq/wearable-sdk

v0.1.1

Published

TypeScript-first OAuth SDK for Garmin, Fitbit, and more. Ship wearable auth in 15 minutes.

Downloads

155

Readme

@the-governor-hq/wearable-sdk

Work in progress. This SDK is under active development and not yet intended for public or production use.

Ship wearable OAuth in 15 minutes.

TypeScript-first SDK for connecting to Garmin, Fitbit, and more. One npm install, three API calls, done.

NEW: 🎮 OAuth Server Kit — Not just an SDK! Includes a runnable playground for instant testing.


👉 GET STARTED NOW | Quick Start | Setup Guide | Playground Docs


⚡ Try It Now

npm run dev

Opens an interactive playground at http://localhost:3001 with:

  • Big OAuth buttons → Connect Garmin / Fitbit instantly
  • Visual flow → See redirect URLs, state, scopes, token results
  • Token management → View stored tokens + refresh button
  • Webhook tester → Paste payloads, verify signatures, see handler logs

With ngrok tunnel:

npm run dev:tunnel  # Exposes local server + prints callback URLs

➡️ Full Playground Documentation

Features

  • Passport.js-like DX — Strategy pattern per provider, one-line Prisma storage
  • Prisma-first storage — Pass your PrismaClient, tokens persist to any database Prisma supports
  • OAuth 2.0 + PKCE — Secure authorization flows out of the box
  • Auto token refresh — Tokens refresh transparently before they expire
  • Normalized data — Activities, sleep, dailies in a consistent schema
  • Raw data preserved — Every response includes the original provider payload
  • 2-month backfill — One call to fetch historical data
  • Zero dependencies (almost) — Only zod in production
  • ESM + CJS — Works everywhere
  • 🎮 Interactive Playground — Test OAuth flows without writing code

Quick Start

npm install @the-governor-hq/wearable-sdk

1. Add the token model to your Prisma schema

Copy the model from prisma/wearable-token.prisma into your schema.prisma, then run:

npx prisma migrate dev --name add-wearable-tokens

2. Initialize the SDK with your PrismaClient

import { PrismaClient } from "@prisma/client";
import { WearableSDK } from "@the-governor-hq/wearable-sdk";

const prisma = new PrismaClient();

const sdk = new WearableSDK({
  prisma,                    // ← that's it, tokens persist to your DB
  providers: {
    garmin: {
      clientId: process.env.GARMIN_CLIENT_ID!,
      clientSecret: process.env.GARMIN_CLIENT_SECRET!,
      redirectUri: "http://localhost:3000/auth/garmin/callback",
    },
    fitbit: {
      clientId: process.env.FITBIT_CLIENT_ID!,
      clientSecret: process.env.FITBIT_CLIENT_SECRET!,
      redirectUri: "http://localhost:3000/auth/fitbit/callback",
    },
  },
});

// 1. Generate auth URL → redirect user to provider consent screen
const { url, state } = sdk.getAuthUrl("garmin", "user-123");

// 2. Handle callback → tokens saved automatically
const result = await sdk.handleCallback("garmin", code, state);

// 3. Fetch data — tokens refresh automatically
const activities = await sdk.getActivities("garmin", {
  userId: "user-123",
  startDate: "2024-01-01",
  endDate: "2024-01-31",
});

Express Example (~50 lines)

import express from "express";
import { PrismaClient } from "@prisma/client";
import { WearableSDK } from "@the-governor-hq/wearable-sdk";

const prisma = new PrismaClient();

const sdk = new WearableSDK({
  prisma,
  debug: true,
  providers: {
    garmin: {
      clientId: process.env.GARMIN_CLIENT_ID!,
      clientSecret: process.env.GARMIN_CLIENT_SECRET!,
      redirectUri: "http://localhost:3000/auth/garmin/callback",
    },
  },
});

const app = express();

// Connect — redirect to Garmin
app.get("/auth/garmin", (req, res) => {
  const { url } = sdk.getAuthUrl("garmin", "demo-user");
  res.redirect(url);
});

// Callback — exchange code for tokens
app.get("/auth/garmin/callback", async (req, res) => {
  const result = await sdk.handleCallback(
    "garmin",
    req.query.code as string,
    req.query.state as string,
  );
  res.json({ connected: result.provider });
});

// Fetch activities
app.get("/activities", async (req, res) => {
  const data = await sdk.getActivities("garmin", {
    userId: "demo-user",
    startDate: "2024-01-01",
  });
  res.json(data);
});

app.listen(3000);

API Reference

WearableSDK

new WearableSDK(config: WearableSDKConfig)

| Option | Type | Description | |--------|------|-------------| | providers | ProvidersConfig | Provider credentials (garmin, fitbit) | | prisma | PrismaClient | Prisma client for token persistence (recommended) | | tokenStore | TokenStore | Custom store (overrides prisma) | | debug | boolean | Enable console logging | | logger | SDKLogger | Custom logger |

OAuth Flow

// Generate authorization URL
sdk.getAuthUrl(provider, userId): { url, state }

// Handle OAuth callback
await sdk.handleCallback(provider, code, state): CallbackResult

// Manually refresh tokens
await sdk.refreshTokens(provider, userId): OAuthTokens

Data Fetching

// Activities (workouts)
await sdk.getActivities(provider, { userId, startDate?, endDate? })

// Sleep sessions
await sdk.getSleep(provider, { userId, startDate?, endDate? })

// Daily summaries (steps, HR, etc.)
await sdk.getDailies(provider, { userId, startDate?, endDate? })

// Backfill 2 months of history
await sdk.backfill(provider, {
  userId,
  daysBack: 60,
  dataTypes: ["activities", "sleep", "dailies"],
})

Connection Management

// Check connection health
await sdk.getConnectionHealth(provider, userId)

// Check all providers
await sdk.getConnectionHealthAll(userId)

// Disconnect
await sdk.disconnect(provider, userId)
await sdk.disconnectAll(userId)

Normalized Data Types

NormalizedActivity

{
  id: string;
  provider: "garmin" | "fitbit";
  type: string;           // "run", "bike", "swim", "walk", ...
  startTime: string;      // ISO 8601
  endTime: string;
  durationSeconds: number;
  calories?: number;
  distanceMeters?: number;
  steps?: number;
  averageHeartRate?: number;
  maxHeartRate?: number;
  source?: string;
  raw: unknown;           // Original provider payload
}

NormalizedSleep

{
  id: string;
  provider: "garmin" | "fitbit";
  date: string;           // YYYY-MM-DD
  startTime: string;
  endTime: string;
  durationSeconds: number;
  deepSleepSeconds?: number;
  lightSleepSeconds?: number;
  remSleepSeconds?: number;
  awakeSeconds?: number;
  sleepScore?: number;
  stages?: SleepStage[];
  raw: unknown;
}

NormalizedDaily

{
  id: string;
  provider: "garmin" | "fitbit";
  date: string;          // YYYY-MM-DD
  steps?: number;
  calories?: number;
  distanceMeters?: number;
  activeMinutes?: number;
  restingHeartRate?: number;
  averageHeartRate?: number;
  maxHeartRate?: number;
  stressLevel?: number;
  floorsClimbed?: number;
  raw: unknown;
}

Token Storage (Prisma)

The SDK uses Prisma as its storage abstraction — one client handles Postgres, MySQL, SQLite, MongoDB, or any datasource Prisma supports.

Setup

  1. Copy the model from prisma/wearable-token.prisma into your schema:
model WearableToken {
  userId       String
  provider     String
  accessToken  String @db.Text
  refreshToken String? @db.Text
  expiresAt    String?
  scopes       String?
  raw          String? @db.Text
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt

  @@id([userId, provider])
  @@map("wearable_tokens")
}
  1. Migrate:
npx prisma migrate dev --name add-wearable-tokens
  1. Pass your PrismaClient:
import { PrismaClient } from "@prisma/client";
import { WearableSDK } from "@the-governor-hq/wearable-sdk";

const prisma = new PrismaClient();
const sdk = new WearableSDK({ prisma, providers: { /* ... */ } });

Advanced: Custom Token Store

If you don't use Prisma, you can implement the TokenStore interface directly:

import type { TokenStore, OAuthTokens, ProviderName } from "@the-governor-hq/wearable-sdk";

class MyTokenStore implements TokenStore {
  async save(userId: string, provider: ProviderName, tokens: OAuthTokens) { /* ... */ }
  async get(userId: string, provider: ProviderName): Promise<OAuthTokens | null> { /* ... */ }
  async delete(userId: string, provider: ProviderName): Promise<void> { /* ... */ }
  async has(userId: string, provider: ProviderName): Promise<boolean> { /* ... */ }
}

const sdk = new WearableSDK({
  tokenStore: new MyTokenStore(), // overrides prisma
  providers: { /* ... */ },
});

Dev / Testing

With no prisma or tokenStore, the SDK falls back to an in-memory store (with a console warning). Fine for quick prototyping — tokens are lost on restart.

Sub-path Imports

// Main SDK
import { WearableSDK } from "@the-governor-hq/wearable-sdk";

// Stores (Prisma, Memory, or bring your own)
import { PrismaTokenStore, MemoryTokenStore } from "@the-governor-hq/wearable-sdk/stores";

// Just Garmin provider
import { GarminProvider } from "@the-governor-hq/wearable-sdk/garmin";

// Just Fitbit provider
import { FitbitProvider } from "@the-governor-hq/wearable-sdk/fitbit";

📚 Documentation

Provider Notes

Garmin

  • Requires a Garmin Connect Developer account
  • Uses OAuth 2.0 + PKCE (code challenge S256)
  • API access requires legal entity / enterprise approval
  • Supports webhooks for real-time push notifications

Fitbit

  • Free public API — register at dev.fitbit.com
  • Uses OAuth 2.0 with Basic Auth + optional PKCE
  • Rate limit: 150 requests/hour (personal apps)
  • Sleep data available via v1.2 API

Architecture

src/
├── index.ts            # Barrel exports
├── sdk.ts              # WearableSDK main class
├── types/              # TypeScript types for everything
├── core/
│   ├── base-provider.ts    # Abstract strategy class
│   ├── errors.ts           # Error hierarchy
│   ├── http-client.ts      # Fetch + retries + rate-limit
│   └── token-store.ts      # TokenStore interface
├── providers/
│   ├── garmin/             # Garmin OAuth + Wellness API
│   └── fitbit/             # Fitbit OAuth + Web API
├── stores/
│   └── memory-store.ts     # In-memory store (dev/test)
└── utils/
    ├── pkce.ts             # PKCE generation
    └── logger.ts           # Logger utilities

Acknowledgements

This SDK was built with the assistance of AI tools, including Claude Opus 4.6, Claude Sonnet 4.5, and Gemini 3.0.

License

MIT — The Governor HQ