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

oura-ts

v0.1.0

Published

TypeScript library and CLI for the Oura Ring API v2

Readme

oura-ts

CI npm

TypeScript library and CLI for the Oura Ring API v2.

Installation

As a library

npm install oura-ts

As a global CLI

npm install -g oura-ts

# Then use directly
oura --help

Library Usage

Basic Usage

import { OuraClient } from 'oura-ts';

const client = new OuraClient({
  accessToken: 'your-access-token',
});

// Get personal info
const info = await client.personalInfo.get();

// Get a single document by ID
const activity = await client.dailyActivity.get('document-id');

// List with date range (async iterator)
for await (const sleep of client.dailySleep.list({
  startDate: '2025-01-01',
  endDate: '2025-01-31',
})) {
  console.log(sleep.score);
}

// Collect all results at once
const allSleep = await client.dailySleep.list({ startDate: '2025-01-01' }).all();

Available Resources

| Resource | Methods | Parameters | |----------|---------|------------| | personalInfo | get() | - | | dailyActivity | get(id), list(options) | date range | | dailyCardiovascularAge | get(id), list(options) | date range | | dailyReadiness | get(id), list(options) | date range | | dailyResilience | get(id), list(options) | date range | | dailySleep | get(id), list(options) | date range | | dailySpO2 | get(id), list(options) | date range | | dailyStress | get(id), list(options) | date range | | enhancedTag | get(id), list(options) | date range | | heartrate | list(options) | datetime range | | restModePeriod | get(id), list(options) | date range | | ringConfiguration | get(id), list(options) | date range | | session | get(id), list(options) | date range | | sleep | get(id), list(options) | date range | | sleepTime | get(id), list(options) | date range | | tag | get(id), list(options) | date range | | vo2Max | get(id), list(options) | date range | | workout | get(id), list(options) | date range |

OAuth Flow

import { OuraOAuth } from 'oura-ts';

const oauth = new OuraOAuth({
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  redirectUri: 'http://localhost:8080/callback',
  scopes: ['daily', 'heartrate', 'personal'],
});

// Generate authorization URL
const { url, state } = oauth.getAuthorizationUrl();

// Exchange authorization code for tokens
const tokens = await oauth.exchangeCode(code);

// Refresh tokens
const newTokens = await oauth.refreshAccessToken(tokens.refreshToken);

// Revoke token
await oauth.revokeAccessToken(tokens.accessToken);

Decoders

The API returns some fields as encoded strings. Use the decoders to convert them:

import { decodeSleepPhases, decodeMovement, decodeActivityClass } from 'oura-ts';

// sleep_phase_5_min: "44442332..." -> array of phases
const phases = decodeSleepPhases(sleep.sleep_phase_5_min);
// [{ minute: 0, phase: 'awake' }, { minute: 5, phase: 'awake' }, ...]

// movement_30_sec: "1143222..." -> array of movement levels
const movement = decodeMovement(sleep.movement_30_sec);
// [{ second: 0, level: 'none' }, { second: 30, level: 'none' }, ...]

// class_5_min: "012345..." -> array of activity classes
const activity = decodeActivityClass(dailyActivity.class_5_min);
// [{ minute: 0, activity: 'non_wear' }, { minute: 5, activity: 'rest' }, ...]

Error Handling

import { OuraError, AuthenticationError, RateLimitError } from 'oura-ts';

try {
  await client.personalInfo.get();
} catch (error) {
  if (error instanceof AuthenticationError) {
    // 401 - Invalid or expired token
  } else if (error instanceof RateLimitError) {
    // 429 - Rate limit exceeded
    console.log(`Retry after ${error.retryAfter} seconds`);
  } else if (error instanceof OuraError) {
    // Other API errors
    console.log(error.statusCode, error.body);
  }
}

CLI Usage

Authentication

# Interactive OAuth login (opens browser)
oura auth login --client-id YOUR_ID --client-secret YOUR_SECRET

# Check token status
oura auth status

# Refresh token
oura auth refresh

# Logout (remove stored token)
oura auth logout

Fetching Data

# Get personal info
oura get personal-info

# Get data with date range
oura get daily-sleep --start 2025-01-01 --end 2025-01-31

# Get single document by ID
oura get daily-activity abc123

# Get heartrate (uses datetime)
oura get heartrate --start 2025-01-01T00:00:00 --end 2025-01-02T00:00:00

# Decode encoded fields
oura get daily-activity --start 2025-01-01 --decode

Webhooks

# List webhooks
oura webhooks list --client-id ID --client-secret SECRET

# Create webhook
oura webhooks create --client-id ID --client-secret SECRET \
  --url https://example.com/hook \
  --data-type daily_sleep \
  --event create \
  --verification-token abc123

# Get webhook details
oura webhooks get WEBHOOK_ID --client-id ID --client-secret SECRET

# Renew webhook
oura webhooks renew WEBHOOK_ID --client-id ID --client-secret SECRET

# Delete webhook
oura webhooks delete WEBHOOK_ID --client-id ID --client-secret SECRET

Configuration

Token Storage

Tokens are stored at ~/.oura_token by default. Override with:

# CLI argument
oura get personal-info --token-file /path/to/token

# Environment variable
export OURA_TOKEN_FILE=/path/to/token

Direct Token

Skip file storage entirely:

export OURA_ACCESS_TOKEN=your-token
oura get personal-info

Sandbox Mode

Use sandbox API endpoints:

oura get personal-info --sandbox

Development

# Install dependencies
pnpm install

# Generate types from OpenAPI spec
pnpm generate:types

# Build
pnpm build

# Typecheck
pnpm typecheck

# Lint
pnpm lint

# Format
pnpm format

Git Hooks

Husky is configured with:

  • pre-commit: Runs typecheck, lint, and auto-formats code
  • commit-msg: Validates conventional commit messages

Testing

# Run all tests
pnpm test

# Run tests in watch mode
pnpm test

# Run with coverage
pnpm test run --coverage

Tests are organized in three layers:

  1. Unit tests (src/**/*.test.ts) - Pure function tests (decoders, token storage)
  2. Mocked client tests (tests/client.test.ts) - HTTP layer mocked with MSW
  3. Integration tests (tests/integration/) - Real API calls, skipped if OURA_ACCESS_TOKEN not set

To run integration tests:

OURA_ACCESS_TOKEN=your-token pnpm test run tests/integration/

License

MIT