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

@ceon-oy/monitor-sdk

v1.0.15

Published

Client SDK for Ceon Monitor - Error tracking, health monitoring, security events, and vulnerability scanning

Readme

Ceon Monitor SDK

Lightweight client SDK for integrating with the Ceon Monitor service. Provides error reporting, technology tracking, vulnerability auditing, and security event monitoring.

Table of Contents

Installation

# From npm (recommended)
npm install @ceon-oy/monitor-sdk

# From GitHub
npm install github:ceon-oy/ceon-monitor-sdk

Or add to your package.json:

{
  "dependencies": {
    "@ceon-oy/monitor-sdk": "^1.0.0"
  }
}

Related

Quick Start

import { MonitorClient } from '@ceon-oy/monitor-sdk';

// Initialize the client
const monitor = new MonitorClient({
  apiKey: process.env.CEON_MONITOR_API_KEY!,
  endpoint: 'https://monitor.example.com',
  environment: process.env.NODE_ENV,
  trackDependencies: true,  // Auto-sync package.json
});

// Capture an error
try {
  await riskyOperation();
} catch (error) {
  await monitor.captureError(error as Error, {
    route: '/api/users',
    method: 'POST',
    statusCode: 500,
  });
}

// Capture a message
await monitor.captureMessage('User signed up', 'INFO', {
  metadata: { userId: '123', plan: 'premium' },
});

// Run vulnerability audit
await monitor.auditDependencies();

// Flush and close before shutdown
await monitor.close();

Configuration

interface MonitorClientConfig {
  apiKey: string;                    // Your project API key (required)
  endpoint: string;                  // Monitor service URL (required)
  environment?: string;              // Environment name (default: 'production')
  batchSize?: number;                // Errors to batch before sending (default: 10)
  flushIntervalMs?: number;          // Auto-flush interval in ms (default: 5000)
  trackDependencies?: boolean;       // Auto-sync package.json (default: false)
  dependencySources?: {              // Multiple package.json sources
    path: string;
    environment: string;
  }[];
  excludePatterns?: string[];        // Glob patterns to exclude (e.g., '@types/*')
  autoAudit?: boolean;               // Enable automatic vulnerability scanning (default: false)
  auditPaths?: {                     // Multiple directories for vulnerability scanning
    path: string;                    // Directory path (e.g., '.', '../client')
    environment: string;             // Environment label (e.g., 'server', 'client')
  }[];
}

Basic Configuration

const monitor = new MonitorClient({
  apiKey: process.env.CEON_MONITOR_API_KEY!,
  endpoint: 'https://monitor.example.com',
  environment: 'production',
});

Multi-Environment Dependency Tracking

For projects with separate server and client folders:

const monitor = new MonitorClient({
  apiKey: process.env.CEON_MONITOR_API_KEY!,
  endpoint: 'https://monitor.example.com',
  environment: 'server',
  trackDependencies: true,
  dependencySources: [
    { path: './package.json', environment: 'server' },
    { path: '../client/package.json', environment: 'client' },
  ],
  excludePatterns: ['@types/*'],  // Filter out TypeScript type definitions
});

Initialization Patterns by Runtime

The SDK initialization pattern depends on your application's runtime environment:

Long-Running Servers (Express, Fastify, plain Node.js)

Traditional Node.js servers run as persistent processes. Use explicit initialization and graceful shutdown:

// lib/monitor.ts
import { MonitorClient } from '@ceon-oy/monitor-sdk';

let monitor: MonitorClient | null = null;

export function initializeMonitor() {
  if (!monitor) {
    monitor = new MonitorClient({
      apiKey: process.env.CEON_MONITOR_API_KEY!,
      endpoint: process.env.CEON_MONITOR_ENDPOINT!,
      environment: process.env.NODE_ENV,
      trackDependencies: true,
      autoAudit: true,
    });
  }
  return monitor;
}

export function getMonitor() {
  if (!monitor) {
    throw new Error('Monitor not initialized. Call initializeMonitor() first.');
  }
  return monitor;
}

// index.ts
import { initializeMonitor, getMonitor } from './lib/monitor';

const monitor = initializeMonitor();

// Graceful shutdown - ensures queued errors are sent before exit
process.on('SIGTERM', async () => {
  console.log('Shutting down gracefully...');
  await getMonitor().close();
  process.exit(0);
});

process.on('SIGINT', async () => {
  console.log('Shutting down gracefully...');
  await getMonitor().close();
  process.exit(0);
});

Why? Long-running servers need:

  • Single instance to avoid memory leaks
  • Graceful shutdown to flush queued errors before exit
  • Process signal handlers for clean termination

Next.js / Serverless / Edge Functions

Serverless environments handle lifecycle automatically. Create the client directly:

// lib/monitor.ts
import { MonitorClient } from '@ceon-oy/monitor-sdk';

export const monitor = new MonitorClient({
  apiKey: process.env.CEON_MONITOR_API_KEY!,
  endpoint: process.env.CEON_MONITOR_ENDPOINT!,
  environment: process.env.NODE_ENV,
  batchSize: 1,  // Send immediately (no batching for short-lived functions)
});

Why no explicit shutdown?

  • Each request is isolated and short-lived
  • The platform handles process lifecycle
  • No persistent process to clean up
  • Set batchSize: 1 to send errors immediately (don't wait for batch)

For API routes that need guaranteed delivery:

// app/api/something/route.ts
import { monitor } from '@/lib/monitor';

export async function POST(request: Request) {
  try {
    // ... your logic
  } catch (error) {
    monitor.captureError(error as Error);
    await monitor.flush();  // Ensure error is sent before response
  }
}

Summary Table

| Environment | Initialize Pattern | Graceful Shutdown | batchSize | |-------------|-------------------|-------------------|-----------| | Express/Fastify/Node.js | initializeMonitor() + singleton | Yes (SIGTERM/SIGINT handlers) | Default (10) | | Next.js (App Router) | Direct instantiation | No | 1 (recommended) | | Next.js (API Routes) | Direct instantiation | No, but call flush() | 1 (recommended) | | Vercel/AWS Lambda | Direct instantiation | No, but call flush() | 1 (required) | | Docker/Kubernetes | initializeMonitor() + singleton | Yes (for graceful pod termination) | Default (10) |

Features

Error Capture

Capture an Error

try {
  // ... your code
} catch (error) {
  await monitor.captureError(error as Error, {
    severity: 'ERROR',        // DEBUG, INFO, WARNING, ERROR, CRITICAL
    route: '/api/users',      // API route or page path
    method: 'POST',           // HTTP method
    statusCode: 500,          // HTTP status code
    userAgent: req.headers['user-agent'],
    ip: req.ip,
    requestId: req.id,
    metadata: {               // Any additional data
      userId: '123',
      action: 'create_user',
    },
  });
}

Capture a Message

await monitor.captureMessage('Payment processed', 'INFO', {
  route: '/api/payments',
  metadata: { orderId: '456', amount: 99.99 },
});

Technology Tracking

Automatic Tracking

Enable trackDependencies: true to automatically sync your package.json dependencies:

const monitor = new MonitorClient({
  apiKey: process.env.CEON_MONITOR_API_KEY!,
  endpoint: 'https://monitor.example.com',
  trackDependencies: true,
});

Manual Reporting

await monitor.reportTechnologies([
  { name: 'node', version: '20.10.0', type: 'runtime' },
  { name: 'express', version: '4.18.2', type: 'framework' },
  { name: 'prisma', version: '5.0.0', type: 'database' },
]);

Vulnerability Auditing

Scan your dependencies for security vulnerabilities and report them to Ceon Monitor.

// Basic audit
const result = await monitor.auditDependencies();

if (result) {
  console.log(`Scan complete: ${result.processed} vulnerabilities found`);
  console.log(`Critical: ${result.summary.critical}, High: ${result.summary.high}`);
}

// Audit a specific project directory
await monitor.auditDependencies({
  projectPath: '/path/to/project',
  environment: 'production',
});

Supported Package Managers

The SDK automatically detects your package manager based on lock files:

| Package Manager | Detection | Audit Command | |-----------------|-----------|---------------| | npm | package-lock.json (default) | npm audit --json | | yarn | yarn.lock | yarn audit --json | | pnpm | pnpm-lock.yaml | npm audit --json (compatibility) |

No configuration needed - the SDK auto-detects and uses the appropriate audit command.

Automatic Auditing (Recommended)

Enable autoAudit: true to let the SDK automatically scan for vulnerabilities based on the interval configured in the Ceon Monitor dashboard:

const monitor = new MonitorClient({
  apiKey: process.env.CEON_MONITOR_API_KEY!,
  endpoint: 'https://monitor.example.com',
  autoAudit: true,  // Enable automatic vulnerability scanning
});

With autoAudit enabled:

  • SDK fetches the scan interval from the server on startup
  • Runs an initial vulnerability scan
  • Schedules recurring scans based on server configuration (e.g., every 24 hours)
  • Responds to on-demand "Run Scan" requests from the dashboard (polled every 5 minutes)

Configure the scan interval in the Ceon Monitor dashboard under the project's Vulnerabilities page.

Multi-Directory Vulnerability Auditing

For projects with separate server and client directories (or monorepos), use auditPaths to scan multiple directories:

const monitor = new MonitorClient({
  apiKey: process.env.CEON_MONITOR_API_KEY!,
  endpoint: 'https://monitor.example.com',
  autoAudit: true,
  auditPaths: [
    { path: '.', environment: 'server' },
    { path: '../client', environment: 'client' },
  ],
  // Also track dependencies from both
  trackDependencies: true,
  dependencySources: [
    { path: './package.json', environment: 'server' },
    { path: '../client/package.json', environment: 'client' },
  ],
});

With auditPaths configured:

  • Each directory is scanned independently using npm audit
  • Results are tagged with the environment label (e.g., 'server', 'client')
  • All vulnerabilities appear in a single project dashboard
  • Use the environment filter in the dashboard to view specific environments

You can also manually trigger a multi-directory audit:

const result = await monitor.auditMultiplePaths();
if (result) {
  console.log('Audit results by environment:');
  for (const env of result.results) {
    console.log(`  ${env.environment}: ${env.processed} vulnerabilities`);
  }
  console.log('Total:', result.totalSummary);
}

Manual Scheduled Auditing

If you prefer manual control over scheduling:

// Run on app startup
monitor.auditDependencies();

// Run daily via cron
import cron from 'node-cron';

cron.schedule('0 3 * * *', async () => {
  await monitor.auditDependencies();
});

// Or using setInterval
setInterval(async () => {
  await monitor.auditDependencies();
}, 24 * 60 * 60 * 1000); // Daily

Security Events

Report Security Event

await monitor.reportSecurityEvent({
  eventType: 'FAILED_LOGIN',
  category: 'AUTHENTICATION',
  severity: 'MEDIUM',
  ip: '192.168.1.1',
  identifier: '[email protected]',
  metadata: { attempts: 3 },
});

Check for Brute Force

const result = await monitor.checkBruteForce({
  ip: '192.168.1.1',
  identifier: '[email protected]',
  threshold: 5,
  windowMinutes: 15,
});

if (result.blocked) {
  // Block the request
  throw new Error('Too many attempts. Please try again later.');
}

Framework Examples

Express.js

import express from 'express';
import { MonitorClient } from '@ceon-oy/monitor-sdk';

const app = express();
const monitor = new MonitorClient({
  apiKey: process.env.CEON_MONITOR_API_KEY!,
  endpoint: process.env.CEON_MONITOR_ENDPOINT!,
  environment: process.env.NODE_ENV,
  trackDependencies: true,
});

// Your routes
app.get('/api/health', (req, res) => {
  res.json({ status: 'ok' });
});

// Error handling middleware
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
  monitor.captureError(err, {
    route: req.path,
    method: req.method,
    statusCode: 500,
    userAgent: req.get('user-agent'),
    ip: req.ip,
  });
  res.status(500).json({ error: 'Internal server error' });
});

// Graceful shutdown
process.on('SIGTERM', async () => {
  await monitor.close();
  process.exit(0);
});

app.listen(3001, () => console.log('Server running on port 3001'));

Next.js

1. Create a monitor utility (lib/monitor.ts):

import { MonitorClient } from '@ceon-oy/monitor-sdk';

let monitor: MonitorClient | null = null;

export function getMonitor(): MonitorClient {
  if (!monitor) {
    monitor = new MonitorClient({
      apiKey: process.env.CEON_MONITOR_API_KEY!,
      endpoint: process.env.CEON_MONITOR_ENDPOINT || 'https://your-monitor-server.com',
      environment: process.env.NODE_ENV || 'development',
      trackDependencies: true,
    });
  }
  return monitor;
}

2. Use in API routes (app/api/example/route.ts):

import { NextRequest, NextResponse } from 'next/server';
import { getMonitor } from '@/lib/monitor';

export async function POST(request: NextRequest) {
  const monitor = getMonitor();

  try {
    const body = await request.json();
    // Your logic here
    return NextResponse.json({ success: true });
  } catch (error) {
    await monitor.captureError(error as Error, {
      route: '/api/example',
      method: 'POST',
      statusCode: 500,
    });
    return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
  }
}

3. Global error handling (app/error.tsx):

'use client';

import { useEffect } from 'react';

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    fetch('/api/log-error', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        message: error.message,
        stack: error.stack,
        digest: error.digest,
      }),
    });
  }, [error]);

  return (
    <div>
      <h2>Something went wrong!</h2>
      <button onClick={() => reset()}>Try again</button>
    </div>
  );
}

4. Create error logging API route (app/api/log-error/route.ts):

import { NextRequest, NextResponse } from 'next/server';
import { getMonitor } from '@/lib/monitor';

export async function POST(request: NextRequest) {
  const monitor = getMonitor();
  const { message, stack, digest } = await request.json();

  await monitor.captureMessage(message, 'ERROR', {
    metadata: { stack, digest, source: 'client' },
  });

  return NextResponse.json({ logged: true });
}

React (Monolithic)

For a React project with Express backend in the same folder:

my-app/
├── package.json
├── src/
│   ├── client/          # React frontend
│   └── server/          # Express backend

Server (src/server/index.ts):

import express from 'express';
import path from 'path';
import { MonitorClient } from '@ceon-oy/monitor-sdk';

const app = express();
app.use(express.json());

const monitor = new MonitorClient({
  apiKey: process.env.CEON_MONITOR_API_KEY!,
  endpoint: process.env.CEON_MONITOR_ENDPOINT!,
  environment: process.env.NODE_ENV || 'development',
  trackDependencies: true,
});

// Client error logging endpoint
app.post('/api/log-client-error', async (req, res) => {
  const { message, stack, componentStack } = req.body;
  await monitor.captureMessage(message, 'ERROR', {
    metadata: { stack, componentStack, source: 'client' },
  });
  res.json({ logged: true });
});

// Global error handler
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
  monitor.captureError(err, {
    route: req.path,
    method: req.method,
    statusCode: 500,
  });
  res.status(500).json({ error: 'Internal server error' });
});

// Graceful shutdown
process.on('SIGTERM', async () => {
  await monitor.close();
  process.exit(0);
});

app.listen(3001, () => console.log('Server running on port 3001'));

React Error Boundary (src/client/ErrorBoundary.tsx):

import React, { Component, ErrorInfo, ReactNode } from 'react';

interface Props {
  children: ReactNode;
}

interface State {
  hasError: boolean;
}

class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(): State {
    return { hasError: true };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    fetch('/api/log-client-error', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        message: error.message,
        stack: error.stack,
        componentStack: errorInfo.componentStack,
      }),
    });
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

export default ErrorBoundary;

React (Separate Server/Client)

For projects with separate server and client folders:

my-project/
├── server/
│   ├── package.json
│   └── src/index.ts
└── client/
    ├── package.json
    └── src/

Server (server/src/index.ts):

import express from 'express';
import cors from 'cors';
import { MonitorClient } from '@ceon-oy/monitor-sdk';

const app = express();
app.use(cors());
app.use(express.json());

// Multi-environment dependency tracking
const monitor = new MonitorClient({
  apiKey: process.env.CEON_MONITOR_API_KEY!,
  endpoint: process.env.CEON_MONITOR_ENDPOINT!,
  environment: process.env.NODE_ENV || 'development',
  trackDependencies: true,
  dependencySources: [
    { path: './package.json', environment: 'server' },
    { path: '../client/package.json', environment: 'client' },
  ],
  excludePatterns: ['@types/*'],
});

// Endpoint for client-side error logging
app.post('/api/log-error', async (req, res) => {
  const { message, stack, componentStack, url, userAgent } = req.body;

  await monitor.captureMessage(message, 'ERROR', {
    route: url,
    metadata: { stack, componentStack, userAgent, source: 'client' },
  });

  res.json({ logged: true });
});

// Global error handler
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
  monitor.captureError(err, {
    route: req.path,
    method: req.method,
    statusCode: 500,
    userAgent: req.get('user-agent'),
    ip: req.ip,
  });
  res.status(500).json({ error: 'Internal server error' });
});

// Graceful shutdown
process.on('SIGTERM', async () => {
  await monitor.close();
  process.exit(0);
});

app.listen(3001, () => console.log('Server running on port 3001'));

React Error Boundary (client/src/components/ErrorBoundary.tsx):

import React, { Component, ErrorInfo, ReactNode } from 'react';

interface Props {
  children: ReactNode;
}

interface State {
  hasError: boolean;
  error: Error | null;
}

class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error: Error): State {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    fetch(`${process.env.REACT_APP_API_URL}/api/log-error`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        message: error.message,
        stack: error.stack,
        componentStack: errorInfo.componentStack,
        url: window.location.href,
        userAgent: navigator.userAgent,
      }),
    }).catch(console.error);
  }

  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h1>Something went wrong</h1>
          <button onClick={() => window.location.reload()}>Reload Page</button>
        </div>
      );
    }
    return this.props.children;
  }
}

export default ErrorBoundary;

Global error handlers (client/src/index.tsx):

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import ErrorBoundary from './components/ErrorBoundary';

// Global error handlers
window.onerror = (message, source, lineno, colno, error) => {
  fetch(`${process.env.REACT_APP_API_URL}/api/log-error`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      message: String(message),
      stack: error?.stack,
      url: window.location.href,
      userAgent: navigator.userAgent,
    }),
  }).catch(console.error);
};

window.onunhandledrejection = (event) => {
  fetch(`${process.env.REACT_APP_API_URL}/api/log-error`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      message: event.reason?.message || 'Unhandled Promise Rejection',
      stack: event.reason?.stack,
      url: window.location.href,
      userAgent: navigator.userAgent,
    }),
  }).catch(console.error);
};

const root = ReactDOM.createRoot(document.getElementById('root')!);
root.render(
  <React.StrictMode>
    <ErrorBoundary>
      <App />
    </ErrorBoundary>
  </React.StrictMode>
);

Real-World Example: Full-Stack Monitoring

This example shows how the Ceon Hours project monitors both server and client code from a single SDK installation on the server.

Project Structure:

ceon-projects/
├── ceon-hours-api/          # Express server (SDK installed here)
│   ├── package.json
│   └── lib/config/monitor-setup.js
└── ceon-hours-web-app/      # React client (no SDK needed)
    └── package.json

1. Monitor Setup (ceon-hours-api/lib/config/monitor-setup.js):

const { MonitorClient } = require("@ceon-oy/monitor-sdk");

let monitor = null;

function initializeMonitor() {
  if (process.env.CEON_MONITOR_API_KEY) {
    monitor = new MonitorClient({
      apiKey: process.env.CEON_MONITOR_API_KEY,
      endpoint: process.env.CEON_MONITOR_ENDPOINT || "https://ceonmonitor.com",
      environment: "server",
      trackDependencies: true,
      autoAudit: true,
      // Track dependencies from both server AND client
      dependencySources: [
        { path: "./package.json", environment: "server" },
        { path: "../ceon-hours-web-app/package.json", environment: "client" },
      ],
      // Audit vulnerabilities in both
      auditPaths: [
        { path: ".", environment: "server" },
        { path: "../ceon-hours-web-app", environment: "client" },
      ],
    });
    console.log("[CeonMonitor] SDK initialized");
  }
  return monitor;
}

function getMonitor() {
  return monitor;
}

module.exports = { initializeMonitor, getMonitor };

2. Initialize in Server Entry (ceon-hours-api/index.js):

const { initializeMonitor, getMonitor } = require("./lib/config/monitor-setup");

// Initialize at startup
const monitor = initializeMonitor();

// Export for middleware
module.exports.getMonitor = getMonitor;

// Graceful shutdown
process.on("SIGTERM", async () => {
  console.log("[CeonMonitor] Flushing pending errors...");
  if (monitor) await monitor.flush();
  process.exit(0);
});

3. Error Handler Middleware (ceon-hours-api/lib/middleware/errorHandler.js):

const { getMonitor } = require("../config/monitor-setup");

const errorHandler = (error, req, res, next) => {
  const monitor = getMonitor();
  if (monitor && error.status >= 400) {
    monitor.captureError(error, {
      route: req.path,
      method: req.method,
      statusCode: error.status || 500,
      userAgent: req.get("user-agent"),
      ip: req.ip,
      severity: error.status >= 500 ? "ERROR" : "WARNING",
    }).catch(console.error);
  }

  res.status(error.status || 500).json({ error: error.message });
};

module.exports = errorHandler;

4. Environment Variables (.env):

# Get API key from https://ceonmonitor.com dashboard
CEON_MONITOR_API_KEY=your-project-api-key

# Endpoint options:
# - Production: https://ceonmonitor.com
# - Local dev: http://localhost:4040
# - Docker dev: http://host.docker.internal:4040
CEON_MONITOR_ENDPOINT=https://ceonmonitor.com

Important: Relative Path Requirements

The dependencySources and auditPaths use relative paths from the server's working directory. For multi-project monitoring to work:

  1. Directory structure must match - The client project must be at the expected relative path (e.g., ../ceon-hours-web-app)
  2. Both projects must be present - If the client folder doesn't exist, only server dependencies will be tracked
  3. npm must be available - Vulnerability auditing requires npm to be installed

Troubleshooting: If client dependencies aren't being tracked on other machines:

  • Verify the relative path is correct for that machine's directory structure
  • Check that the client's package.json exists at the expected path
  • The SDK will log warnings if paths are invalid

API Reference

MonitorClient

constructor(config: MonitorClientConfig)

Creates a new monitor client instance.

captureError(error: Error, context?: ErrorContext): Promise<void>

Captures an error and queues it for sending.

captureMessage(message: string, severity?: Severity, context?: ErrorContext): Promise<void>

Captures a log message with optional severity level.

reportTechnologies(technologies: Technology[]): Promise<void>

Reports technology stack information.

reportSecurityEvent(event: SecurityEvent): Promise<void>

Reports a security event.

checkBruteForce(params: BruteForceParams): Promise<BruteForceResult>

Checks for brute force attacks.

auditDependencies(options?: AuditOptions): Promise<AuditResult | null>

Runs npm audit and sends results to the server.

auditMultiplePaths(): Promise<MultiAuditSummary | null>

Runs npm audit on all directories configured in auditPaths and returns a combined summary.

flush(): Promise<void>

Immediately sends all queued errors.

close(): Promise<void>

Flushes remaining errors and stops the client.

Batching Behavior

The SDK batches errors to reduce network overhead:

  1. Errors are queued locally
  2. Queue is flushed when:
    • batchSize errors are queued (default: 10)
    • flushIntervalMs milliseconds pass (default: 5000ms)
    • flush() is called manually
    • close() is called

For serverless/edge environments where the process may terminate quickly:

  • Set batchSize: 1 to send immediately
  • Call await monitor.flush() at the end of each request

Building

npm install
npm run build    # Build for production
npm run dev      # Watch mode for development

Types

type Severity = 'DEBUG' | 'INFO' | 'WARNING' | 'ERROR' | 'CRITICAL';

interface ErrorContext {
  severity?: Severity;
  route?: string;
  method?: string;
  statusCode?: number;
  userAgent?: string;
  ip?: string;
  requestId?: string;
  metadata?: Record<string, unknown>;
}

interface Technology {
  name: string;
  version: string;
  type: 'runtime' | 'framework' | 'library' | 'database' | 'tool' | 'other';
  environment?: string;
}

interface SecurityEvent {
  eventType: string;
  category: 'AUTHENTICATION' | 'AUTHORIZATION' | 'RATE_LIMIT' | 'SUSPICIOUS_ACTIVITY' | 'DATA_ACCESS';
  severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
  ip?: string;
  identifier?: string;
  metadata?: Record<string, unknown>;
}

interface AuditPath {
  path: string;        // Directory path (e.g., '.', '../client')
  environment: string; // Environment label (e.g., 'server', 'client')
}

interface MultiAuditSummary {
  results: Array<{
    environment: string;
    scanId: string;
    processed: number;
    resolved: number;
    summary: { critical: number; high: number; moderate: number; low: number; info: number };
  }>;
  totalSummary: { critical: number; high: number; moderate: number; low: number; info: number };
}

License

MIT