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

@isava/sentinel-sdk

v2.1.0

Published

Official JavaScript/TypeScript SDK for the Sentinel Watch Security Platform

Readme

sentinel-watch-sdk

Official JavaScript/TypeScript SDK for the Sentinel Watch Security Platform.

Built to Stripe SDK standards — fully typed, tree-shakeable, zero runtime dependencies, works in Node 18+, browsers, and edge runtimes.


Install

npm install sentinel-watch-sdk
# or
yarn add sentinel-watch-sdk
# or
pnpm add sentinel-watch-sdk

Optional: Install axios if you prefer it over the native Fetch API:

npm install axios

Quick Start

import SentinelClient from 'sentinel-watch-sdk';

const sentinel = new SentinelClient({
  baseUrl: 'https://api.your-sentinel.io/api',
  timeout: 15_000,
  maxRetries: 3,
});

// 1. Login — token stored automatically, no manual header management
const { user } = await sentinel.auth.login('[email protected]', 'p@ssw0rd');
console.log(user.role);           // 'security_analyst'
console.log(user.dashboardRoute); // '/analyst'

// 2. Work with typed resources
const { data: alerts, total } = await sentinel.alerts.list({
  severity: 'critical',
  status:   'open',
  limit:    20,
});

// 3. Shorthand actions
await sentinel.alerts.investigate(alerts[0]._id);
await sentinel.alerts.resolve(alerts[0]._id);

// 4. Dashboard + analyst summary in one call
const [stats, summary] = await Promise.all([
  sentinel.dashboard.stats(),
  sentinel.analyst.summary(),
]);
console.log(stats.alerts.open);           // e.g. 14
console.log(summary.vulnerabilities.open); // e.g. 5

Configuration

const sentinel = new SentinelClient({
  // Required in production
  baseUrl: 'https://api.your-sentinel.io/api',

  // Pre-set token (useful in server-side apps with stored sessions)
  token: process.env.SENTINEL_TOKEN,

  // Request timeout in ms (default: 30_000)
  timeout: 15_000,

  // Max retry attempts for 5xx errors (default: 2, exponential backoff)
  maxRetries: 3,

  // 'auto' | 'fetch' | 'axios'  (default: 'auto')
  transport: 'axios',

  // Verbose request/response logging — dev only
  debug: process.env.NODE_ENV === 'development',
});

Error Handling

Every error the SDK throws is an instance of SentinelError or one of its subclasses. This mirrors the Stripe SDK pattern — catch what you need, let everything else bubble.

import {
  SentinelClient,
  SentinelError,
  AuthenticationError,
  PermissionError,
  NotFoundError,
  ValidationError,
  RateLimitError,
  BillingError,
  NetworkError,
  TimeoutError,
  isSentinelError,
} from 'sentinel-watch-sdk';

try {
  const alert = await sentinel.alerts.get('bad-id');
} catch (err) {
  if (err instanceof NotFoundError) {
    console.error('Alert not found');                         // 404
  } else if (err instanceof AuthenticationError) {
    // Token expired — redirect to login
    sentinel.clearToken();
    redirectToLogin();
  } else if (err instanceof ValidationError) {
    // Server-side field validation failed
    err.fieldErrors.forEach(e => console.error(e.field, e.message));
  } else if (err instanceof RateLimitError) {
    // Back off and retry
    await sleep(err.retryAfter ? err.retryAfter * 1000 : 2000);
  } else if (err instanceof BillingError) {
    console.error('Upgrade your plan to access this feature.');
  } else if (err instanceof NetworkError) {
    console.error('No internet connection.');
  } else if (err instanceof TimeoutError) {
    console.error('Request timed out — try again.');
  } else if (isSentinelError(err)) {
    // All other Sentinel errors
    console.error(err.code, err.statusCode, err.message);
  } else {
    throw err; // Unknown — rethrow
  }
}

Error classes

| Class | HTTP status | Code | When | |-----------------------|-------------|-------------------------|----------------------------------------| | AuthenticationError | 401 | authentication_error | Token missing, invalid, or expired | | PermissionError | 403 | permission_denied | Valid token but wrong role | | NotFoundError | 404 | resource_not_found | Resource doesn't exist | | ConflictError | 409 | resource_already_exists| Duplicate email, name, etc. | | ValidationError | 400 | validation_error | Body failed server validation | | RateLimitError | 429 | rate_limit_error | Too many requests | | BillingError | 402 | subscription_inactive | Plan limit, trial expired, etc. | | ApiError | 5xx | api_error | Server threw an unexpected error | | NetworkError | — | network_error | Fetch failed before response received | | TimeoutError | — | timeout_error | Request exceeded timeout ms |


Interceptors

// Add a custom header to every request
sentinel.addRequestInterceptor(req => ({
  ...req,
  headers: { ...req.headers, 'X-Tenant-ID': tenantId },
}));

// Log every response with its request ID
sentinel.addResponseInterceptor(res => {
  console.log(`[${res.statusCode}] ${res.requestId}`);
  return res;
});

// Auto-logout when token expires
sentinel.addErrorInterceptor(err => {
  if (err instanceof AuthenticationError) {
    sentinel.clearToken();
    window.location.href = '/login';
  }
  throw err;
});

All Resources

Core

// ── Auth ──────────────────────────────────────────────────────────────────
await sentinel.auth.login(email, password)
await sentinel.auth.register(name, email, password, orgName?)
await sentinel.auth.me()
await sentinel.auth.forgotPassword(email)
await sentinel.auth.resetPassword(token, newPassword)
await sentinel.auth.changePassword(current, new)
sentinel.auth.logout()                  // clears token locally

// ── Alerts ────────────────────────────────────────────────────────────────
await sentinel.alerts.list({ severity, status, page, limit })
await sentinel.alerts.get(id)
await sentinel.alerts.create({ title, severity, source, ... })
await sentinel.alerts.update(id, { status, ... })
await sentinel.alerts.investigate(id)   // shorthand
await sentinel.alerts.resolve(id)       // shorthand
await sentinel.alerts.stats()           // → { total, critical, high, open, ... }
await sentinel.alerts.delete(id)

// ── Incidents ─────────────────────────────────────────────────────────────
await sentinel.incidents.list({ status, severity })
await sentinel.incidents.get(id)
await sentinel.incidents.create({ title, severity })
await sentinel.incidents.update(id, { status })
await sentinel.incidents.contain(id)    // shorthand → status: 'contained'
await sentinel.incidents.resolve(id)    // shorthand → status: 'resolved'
await sentinel.incidents.close(id)      // shorthand → status: 'closed'
await sentinel.incidents.stats()

// ── Organizations ─────────────────────────────────────────────────────────
await sentinel.organizations.list()
await sentinel.organizations.pending()  // orgs awaiting approval
await sentinel.organizations.get(id)
await sentinel.organizations.approve(id)
await sentinel.organizations.reject(id, reason?)
await sentinel.organizations.suspend(id)
await sentinel.organizations.members(id)

// ── Users ─────────────────────────────────────────────────────────────────
await sentinel.users.list({ role, organizationId })
await sentinel.users.get(id)
await sentinel.users.create({ name, email, password, role })
await sentinel.users.update(id, { name, isActive })
await sentinel.users.assignSystems(id, [systemId1, systemId2])
await sentinel.users.delete(id)

// ── Systems, Threats, Detection Rules ────────────────────────────────────
await sentinel.systems.list()
await sentinel.threats.list({ severity, status })
await sentinel.detectionRules.list({ type, status })

// ── Playbooks ─────────────────────────────────────────────────────────────
await sentinel.playbooks.list()
await sentinel.playbooks.run(id)        // executes + increments executionCount

// ── Dashboard ─────────────────────────────────────────────────────────────
await sentinel.dashboard.stats()        // global counts
await sentinel.dashboard.reportSummary() // 30-day trends

// ── Billing ───────────────────────────────────────────────────────────────
await sentinel.billing.plans()
await sentinel.billing.subscription()
await sentinel.billing.createOrder('pro')   // → { orderId, approvalUrl }
await sentinel.billing.captureOrder(orderId)
await sentinel.billing.cancel()
await sentinel.billing.reactivate()
await sentinel.billing.invoices()

Analyst

// ── Summary ───────────────────────────────────────────────────────────────
await sentinel.analyst.summary()         // all secondary stats in one call

// ── Vulnerabilities ───────────────────────────────────────────────────────
await sentinel.vulnerabilities.listWithStats()  // data + aggregate stats
await sentinel.vulnerabilities.markPatching(id)
await sentinel.vulnerabilities.markPatched(id)

// ── Phishing ──────────────────────────────────────────────────────────────
await sentinel.phishing.list()
await sentinel.phishing.create({ reporter, subject })
await sentinel.phishing.quarantine(id)
await sentinel.phishing.block(id)
await sentinel.phishing.resolve(id)

// ── Malware ───────────────────────────────────────────────────────────────
await sentinel.malware.list()
await sentinel.malware.submit({ hash, fileName, type })
await sentinel.malware.startAnalysis(id)
await sentinel.malware.markComplete(id)

// ── Certificates ──────────────────────────────────────────────────────────
await sentinel.certificates.list()
await sentinel.certificates.create({ name, expiresAt })
await sentinel.certificates.delete(id)

// ── Firewall Changes ──────────────────────────────────────────────────────
await sentinel.firewallChanges.list({ status: 'pending' })
await sentinel.firewallChanges.create({ requester, rule })
await sentinel.firewallChanges.approve(id)
await sentinel.firewallChanges.reject(id)

// ── Threat Hunts ──────────────────────────────────────────────────────────
await sentinel.threatHunts.list()
await sentinel.threatHunts.create({ name, ttps, query })
await sentinel.threatHunts.start(id)
await sentinel.threatHunts.finish(id, findings)

// ── User Activity (UEBA) ──────────────────────────────────────────────────
await sentinel.userActivity.list({ risk: 'high' })
await sentinel.userActivity.review(id)

// ── Compliance ────────────────────────────────────────────────────────────
await sentinel.compliance.stats()  // ISO 27001, NIST, GDPR, SOC2, PCI-DSS

Token Management

// Option A: Login (token stored automatically)
await sentinel.auth.login(email, password);

// Option B: Pre-load from storage
const sentinel = new SentinelClient({ token: localStorage.getItem('token') });

// Option C: Set after construction
sentinel.setToken(storedToken);

// Chaining
const sentinel = new SentinelClient({ baseUrl })
  .setToken(process.env.SENTINEL_TOKEN!);

// Get current token (e.g. to persist after login)
const { token } = await sentinel.auth.login(email, password);
localStorage.setItem('token', token);

// Clear on logout
sentinel.clearToken();

TypeScript

All types are exported from the root package:

import type {
  Alert, AlertStatus, AlertStats,
  Incident, IncidentStatus,
  Organization, User, System,
  Vulnerability, ThreatHunt,
  DashboardStats, AnalystSummary,
  PagedResponse, ApiResponse,
  SentinelConfig,
} from 'sentinel-watch-sdk';

Publishing

npm run build          # compile TypeScript → dist/
npm publish            # publish [email protected] to npmjs.com