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

@bearwatch/sdk

v0.1.3

Published

BearWatch SDK for Node.js - Job monitoring and alerting

Readme

@bearwatch/sdk

Official BearWatch SDK for Node.js - Job monitoring and alerting for indie developers.

Installation

npm install @bearwatch/sdk

Requirements

  • Node.js 18.0.0 or higher (uses native fetch)
  • ESM only - CommonJS is not supported

Quick Start

1. Get API Key

Go to BearWatch Dashboard → Project Settings → Create API Key (e.g., bw_kI6t8QA21on0DKeRDlen8r2hzucVNL3WdAfaZgQdetY).

2. Create a Job

Create a job in the dashboard. You'll get a job ID (24-character hex string, e.g., 507f1f77bcf86cd799439011).

3. Install and Use

Let's assume you have a daily backup job that runs at 2:00 AM:

// cron-jobs.ts
import cron from 'node-cron';
import { BearWatch } from '@bearwatch/sdk';

const bw = new BearWatch({ apiKey: 'your-api-key' });

cron.schedule('0 0 2 * * *', async () => {
  await bw.wrap('507f1f77bcf86cd799439011', async () => {
    await backup();
  });
});

Usage

ping - Manual Status Reporting

Use ping when you need fine-grained control over status reporting:

cron.schedule('0 0 2 * * *', async () => {
  try {
    await backup();
    await bw.ping('507f1f77bcf86cd799439011', { status: 'SUCCESS' });
  } catch (error) {
    await bw.ping('507f1f77bcf86cd799439011', {
      status: 'FAILED',
      error: error instanceof Error ? error.message : String(error),
    });
  }
});

Include output and metadata:

cron.schedule('0 0 0 * * *', async () => {
  const bytes = await backup();
  await bw.ping('507f1f77bcf86cd799439011', {
    status: 'SUCCESS',
    output: `Backup completed: ${bytes} bytes`,
    metadata: { bytes },
  });
});

PingOptions

| Option | Type | Default | Description | | ------------- | ------------------------- | -------------- | ---------------------------------------- | | status | RequestStatus | 'SUCCESS' | 'RUNNING', 'SUCCESS', or 'FAILED' | | output | string | - | Output message (max 10KB) | | error | string | - | Error message for FAILED status (max 10KB) | | startedAt | Date \| string | completedAt | Job start time | | completedAt | Date \| string | current time | Job completion time | | metadata | Record<string, unknown> | - | Additional key-value pairs (max 10KB) | | retry | boolean | true | Enable/disable retry |

Note: TIMEOUT and MISSED are server-detected states and cannot be set in requests.

wrap - Automatic Status Reporting

Wraps a function and automatically:

  • Measures startedAt and completedAt
  • Reports SUCCESS or FAILED based on whether the function completes or throws
cron.schedule('0 0 2 * * *', async () => {
  await bw.wrap('507f1f77bcf86cd799439011', async () => {
    await backup();
  });
});

Include metadata:

cron.schedule('0 0 2 * * *', async () => {
  await bw.wrap('507f1f77bcf86cd799439011', async () => {
    await backup();
  }, {
    metadata: {
      server: 'backup-01',
      region: 'ap-northeast-2',
      version: '1.2.0',
    },
  });
});

WrapOptions

| Option | Type | Default | Description | | ---------- | ------------------------- | ------- | ------------------------------------- | | metadata | Record<string, unknown> | - | Additional key-value pairs (max 10KB) | | retry | boolean | true | Enable/disable retry |

Error handling behavior:

  • On success: reports SUCCESS with execution duration
  • On error: reports FAILED with error message, then re-throws the original error
cron.schedule('0 0 2 * * *', async () => {
  try {
    await bw.wrap('507f1f77bcf86cd799439011', async () => {
      await backup();
    });
  } catch (error) {
    // BearWatch already reported FAILED status
    // You can add additional error handling here
    console.error(error);
  }
});

Tip: Use wrap for most cases. Use ping when you need more control (e.g., reporting RUNNING status for long jobs).

Configuration

const bw = new BearWatch({
  apiKey: 'your-api-key',

  // Optional (defaults shown)
  timeout: 30000, // 30 seconds
  maxRetries: 3,
  retryDelay: 500, // 500ms base delay
});

| Option | Type | Required | Default | Description | | ------------ | -------- | -------- | -------- | ------------------------- | | apiKey | string | Yes | - | API key for authentication | | timeout | number | No | 30000 | Request timeout (ms) | | maxRetries | number | No | 3 | Max retry attempts | | retryDelay | number | No | 500 | Initial retry delay (ms) |

Retry Policy

| Method | Default Retry | Reason | | -------- | ------------- | ------------------------ | | ping() | Enabled | Idempotent operation | | wrap() | Enabled | Uses ping() internally |

Retry Behavior

  • Exponential backoff: 500ms → 1000ms → 2000ms
  • 429 Rate Limit: Respects Retry-After header (rate limit: 100 requests/minute per API key)
  • 5xx Server Errors: Retries with backoff
  • 401/404: No retry (client errors)

Disable Retry

// Disable retry for a specific call
await bw.ping('507f1f77bcf86cd799439011', { retry: false });

Error Handling

When the SDK fails to communicate with BearWatch (network failure, server down, invalid API key, etc.), it throws a BearWatchError:

import { BearWatch, BearWatchError } from '@bearwatch/sdk';

try {
  await bw.ping('507f1f77bcf86cd799439011');
} catch (error) {
  if (error instanceof BearWatchError) {
    // SDK failed to report to BearWatch
    console.error(`Code: ${error.code}`);
    console.error(`Status: ${error.statusCode}`);
  }
}

Error Codes

| Code | Description | Retry | | ----------------- | ------------------------ | ------- | | INVALID_API_KEY | 401 - Invalid API key | No | | JOB_NOT_FOUND | 404 - Job not found | No | | RATE_LIMITED | 429 - Rate limit reached | Yes | | SERVER_ERROR | 5xx - Server error | Yes | | NETWORK_ERROR | Network failure | Yes | | TIMEOUT | Request timed out | Yes |

TypeScript

The SDK is written in TypeScript and includes full type definitions:

import {
  BearWatch,
  BearWatchConfig,
  BearWatchError,
  ErrorCode,
  ErrorContext,
  HeartbeatResponse,
  PingOptions,
  WrapOptions,
  RequestStatus,   // For requests: 'RUNNING' | 'SUCCESS' | 'FAILED'
  ResponseStatus,  // For responses: includes 'TIMEOUT' | 'MISSED'
  Status,          // Alias for ResponseStatus (deprecated)
} from '@bearwatch/sdk';

Method Signatures

class BearWatch {
  constructor(config: BearWatchConfig);
  ping(jobId: string, options?: PingOptions): Promise<HeartbeatResponse>;
  wrap<T>(jobId: string, fn: () => Promise<T>, options?: WrapOptions): Promise<T>;
}

HeartbeatResponse

interface HeartbeatResponse {
  jobId: string;      // Job ID
  runId: string;      // Unique run ID for this execution
  status: ResponseStatus;
  receivedAt: string; // ISO 8601 timestamp
}

Common Patterns

node-cron

import cron from 'node-cron';
import { BearWatch } from '@bearwatch/sdk';

const bw = new BearWatch({ apiKey: 'your-api-key' });

// Every day at 3:00 AM
cron.schedule('0 0 3 * * *', async () => {
  await bw.wrap('6848c9e5f8a2b3d4e5f60001', async () => {
    await backup();
  });
});

AWS Lambda (EventBridge Scheduler)

import { BearWatch } from '@bearwatch/sdk';

const bw = new BearWatch({ apiKey: process.env.BEARWATCH_API_KEY });

export const handler = async () => {
  await bw.wrap('6848c9e5f8a2b3d4e5f60002', async () => {
    await backup();
  });
};

Long-Running Jobs

async function runBackup() {
  const jobId = '6848c9e5f8a2b3d4e5f60003';
  const startedAt = new Date();

  await bw.ping(jobId, { status: 'RUNNING' });

  try {
    await backup();
    await bw.ping(jobId, {
      status: 'SUCCESS',
      startedAt,
      completedAt: new Date(),
    });
  } catch (error) {
    await bw.ping(jobId, {
      status: 'FAILED',
      startedAt,
      completedAt: new Date(),
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
}

FAQ

Q: Do I need to create jobs in the dashboard first? A: Yes, create a job in the BearWatch Dashboard first to get a job ID.

Q: What's the difference between wrap and ping? A: wrap automatically measures execution time and reports SUCCESS/FAILED based on whether the function completes or throws. ping gives you manual control over when and what to report.

Q: What happens if the SDK fails to report (network error)? A: By default, the SDK retries 3 times with exponential backoff. If all retries fail, ping throws a BearWatchError. For wrap, the original function's error takes priority and is always re-thrown.

Troubleshooting

"Cannot use import statement outside a module" This SDK is ESM only. Add "type": "module" to your package.json.

"fetch is not defined" Requires Node.js 18.0.0 or higher which includes native fetch.

"JOB_NOT_FOUND" error Create the job in the BearWatch Dashboard first. The job ID must exist before sending pings.

CommonJS Support

This package is ESM only and does not support CommonJS (require()).

If you need CommonJS support, please open an issue. CommonJS support may be added in a future version.

License

MIT