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

@dottech/ticket-snap

v0.2.3

Published

Capture 500/runtime errors with rich page/request/response snapshots + screenshot; open ticket via public endpoint.

Readme

@dottech/ticket-snap

npm version License: MIT TypeScript

Capture 500/runtime errors with rich page/request/response snapshots + screenshot; open ticket via public endpoint.

ticket-snap is a lightweight, zero-dependency library that automatically captures errors, network failures, and runtime exceptions in your web application. It collects comprehensive context including screenshots, network activity, console logs, and page metadata, then submits detailed bug reports to your endpoint.

Table of Contents

Features

  • 🎯 Automatic Error Capture: Intercepts fetch/XHR requests and global errors
  • 📸 Screenshot Support: Captures page screenshots using html2canvas or SVG fallback
  • 🔒 Security First: Built-in redaction for sensitive data (passwords, tokens, emails)
  • 📦 Offline Queue: Queues reports when offline, retries automatically
  • 🎨 Flexible Triggers: Auto-submit, prompt user, or manual reporting modes
  • 🔌 Framework Agnostic: Works with React, Vue, Angular, or vanilla JS
  • 📊 Rich Context: Captures network activity, console logs, performance timings
  • 🪵 Breadcrumbs & Context: Trail of user/app events and mutable context (userId, route) for "what led to this" debugging
  • 🎛️ Highly Configurable: Extensive options for customization

Installation

npm

npm install @dottech/ticket-snap

CDN (unpkg)

<script src="https://unpkg.com/@dottech/ticket-snap/dist/ticket-snap.umd.js"></script>

CDN (jsDelivr)

<script src="https://cdn.jsdelivr.net/npm/@dottech/ticket-snap/dist/ticket-snap.umd.js"></script>

ES Modules

import TicketSnap from '@dottech/ticket-snap';

CommonJS

const TicketSnap = require('@dottech/ticket-snap');

Quick Start

import TicketSnap from '@dottech/ticket-snap';

TicketSnap.init({
  endpoint: 'https://your-api.com/api/v1/public/bug-reports',
  projectKey: 'MY-PROJECT',
  triggerMode: 'prompt', // Show popup on errors
  includeScreenshot: true
});

That's it! The library will now automatically capture:

  • HTTP 500+ errors from fetch/XHR requests
  • Unhandled JavaScript errors
  • Unhandled promise rejections

Configuration

Required Options

endpoint (string, required)

The API endpoint URL where bug reports will be submitted.

endpoint: 'https://api.example.com/api/v1/public/bug-reports'

Project & Routing

projectKey (string, optional)

Project identifier for routing tickets. Default: 'TICKET-WEB'

projectKey: 'MY-PROJECT-KEY'

labels (string[], optional)

Array of labels to attach to tickets. Default: ['auto-captured', 'source:web']

labels: ['production', 'critical', 'frontend']

Behavior Control

triggerMode ('auto' | 'prompt' | 'manual', optional)

Controls when and how error reports are submitted:

  • 'auto': Automatically submit reports without user interaction
  • 'prompt': Show a popup dialog for user to review and add notes before submitting
  • 'manual': Only submit when reportNow() is called explicitly

Default: 'prompt' (or inferred from autoSubmitOn500 for backward compatibility)

// Auto-submit on errors (no user interaction)
triggerMode: 'auto'

// Show popup for user review
triggerMode: 'prompt'

// Manual control only
triggerMode: 'manual'

promptUser (boolean, optional)

When calling reportNow(), whether to show the popup dialog. Default: true

promptUser: true  // Show popup when reportNow() is called
promptUser: false // Auto-submit without popup

autoSubmitOn500 (boolean, optional)

Legacy option: If triggerMode is not set, this determines behavior:

  • true → equivalent to triggerMode: 'auto'
  • false → equivalent to triggerMode: 'prompt'

This option is kept for backward compatibility but is ignored when triggerMode is explicitly set.

Screenshot Options

includeScreenshot (boolean, optional)

Whether to capture screenshots when errors occur. Default: true

includeScreenshot: true

screenshotStrategy ('auto' | 'html2canvas' | 'svg-foreignObject' | 'none', optional)

Screenshot capture method:

  • 'auto': Try html2canvas if available, fallback to SVG method
  • 'html2canvas': Use html2canvas library (must be loaded separately)
  • 'svg-foreignObject': Use SVG-based capture (built-in, no dependencies)
  • 'none': Disable screenshots

Default: 'auto'

// Use html2canvas if available (requires loading html2canvas separately)
screenshotStrategy: 'html2canvas'

// Use built-in SVG method
screenshotStrategy: 'svg-foreignObject'

// Disable screenshots
screenshotStrategy: 'none'

Note: To use html2canvas, load it before ticket-snap:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/html2canvas.min.js"></script>
<script src="https://unpkg.com/@dottech/ticket-snap/dist/ticket-snap.umd.js"></script>

Authentication

auth (AuthConfig, optional)

Authentication configuration for the API endpoint. Default: { mode: 'none' }

type AuthConfig =
  | { mode: 'basic'; secret: string }    // Basic auth: Authorization: Basic base64(projectKey:secret)
  | { mode: 'ingestKey'; key: string }   // Query param: ?ingestKey=...
  | { mode: 'none' };                    // No authentication

Basic Authentication:

auth: {
  mode: 'basic',
  secret: 'your-secret-key'
}
// Sends: Authorization: Basic base64(projectKey:secret)

Ingest Key (Query Parameter):

auth: {
  mode: 'ingestKey',
  key: 'your-ingest-key'
}
// Appends: ?ingestKey=your-ingest-key

No Authentication:

auth: { mode: 'none' }
// or simply omit the auth option

transport ('formFields' | 'metadataPart', optional)

How to structure the submission payload:

  • 'formFields': All data as individual FormData fields (recommended for most backends)
  • 'metadataPart': Single JSON metadata part + attachments

Default: 'formFields' if auth.mode === 'basic', otherwise 'metadataPart'

transport: 'formFields'  // Recommended for most backends

Data Limits & Redaction

redact (RedactRule[], optional)

Custom redaction rules to sanitize sensitive data. Rules are applied in addition to default redactions.

type RedactRule =
  | { key: RegExp; replaceWith?: string }        // Redact header/field names
  | { pattern: RegExp; replaceWith: string };    // Redact text patterns

Default redactions (always applied):

  • Headers: authorization, cookie
  • Patterns: "password":"...", Bearer <token>, phone numbers, emails

Custom redaction example:

redact: [
  // Redact custom header
  { key: /x-api-key/i, replaceWith: '***' },
  
  // Redact credit card numbers
  { pattern: /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g, replaceWith: '****-****-****-****' },
  
  // Redact SSN
  { pattern: /\b\d{3}-\d{2}-\d{4}\b/g, replaceWith: '***-**-****' }
]

ringSize (number, optional)

Maximum number of recent network requests to keep in memory. Default: 20

ringSize: 50  // Keep last 50 requests

networkCaptureMode ('all' | 'errorsOnly', optional)

  • 'all': Capture every request (default).
  • 'errorsOnly': Only keep 4xx/5xx and network failures. Ideal for integration-error focus; 200s are not stored.
networkCaptureMode: 'errorsOnly'  // Only failed requests in history

networkRecentCount (number, optional)

How many recent network entries to attach to each snapshot and payload. Default: 10

networkRecentCount: 15  // Last 15 requests per report

maxBodyChars (number, optional)

Maximum characters to capture from request/response bodies. Default: 2000

maxBodyChars: 5000  // Capture up to 5000 chars

maxConsole (number, optional)

Maximum number of console log entries to capture. Default: 100

maxConsole: 200

maxPerf (number, optional)

Maximum number of performance entries to capture. Default: 150

maxPerf: 300

Breadcrumbs & context (frontend trail)

maxBreadcrumbs (number, optional)

Maximum breadcrumb entries (user/app event trail) to keep. Default: 30

maxBreadcrumbs: 50

captureNavigationBreadcrumbs (boolean, optional)

Auto-add a breadcrumb on popstate and hashchange. Default: true

captureNavigationBreadcrumbs: true

initialContext (Record<string, unknown>, optional)

Initial key-value context attached to every snapshot (e.g. userId, feature flags).

initialContext: { userId: 'u-123', feature: 'checkout-v2' }

Use TicketSnap.setContext(key, value) at runtime to update context.

SPA Integration

routeResolver (() => RouteInfo, optional)

Function to extract current route information for SPA frameworks. Default: returns current pathname

interface RouteInfo {
  route?: string;                    // Route path (e.g., '/users/:id')
  params?: Record<string, string | number>;  // Route parameters
  method?: string;                   // HTTP method
  urlOverride?: string;              // Override URL in report
}

Vue Router example:

import { useRoute } from 'vue-router';

routeResolver: () => {
  const route = useRoute();
  return {
    route: route.path,
    params: route.params,
    method: route.meta?.method
  };
}

React Router example:

import { useLocation, useParams } from 'react-router-dom';

routeResolver: () => {
  const location = useLocation();
  const params = useParams();
  return {
    route: location.pathname,
    params: params
  };
}

App Metadata

app (AppContext, optional)

Application metadata to include in reports.

interface AppContext {
  appName?: string;
  appVersion?: string;
  environment?: string;      // 'development', 'staging', 'production'
  build?: string;           // Build number or hash
  commit?: string;          // Git commit SHA
  releaseChannel?: string;  // 'stable', 'beta', 'canary'
}

Example:

app: {
  appName: 'MyApp',
  appVersion: '1.2.3',
  environment: 'production',
  build: '20240115.1234',
  commit: 'abc123def',
  releaseChannel: 'stable'
}

Event Callbacks

onSubmitted ((res: unknown) => void, optional)

Callback invoked when a report is successfully submitted.

onSubmitted: (response) => {
  console.log('Ticket created:', response);
  // response contains the API response
}

onError ((err: unknown) => void, optional)

Callback invoked when submission fails (report is queued for retry).

onError: (error) => {
  console.error('Failed to submit report:', error);
  // Report is automatically queued for retry
}

API Reference

init(config: TicketSnapConfig)

Initializes ticket-snap with the provided configuration. Must be called before the library can capture errors.

const api = TicketSnap.init({
  endpoint: 'https://api.example.com/bug-reports',
  projectKey: 'MY-PROJECT'
});

Returns: The API object with methods below.

reportNow(extraNotes?: string, options?: { includeScreenshot?: boolean })

Manually trigger a bug report. Useful for user-initiated reporting or custom triggers.

// Basic usage
TicketSnap.reportNow('User reported an issue');

// Without screenshot
TicketSnap.reportNow('Issue description', { includeScreenshot: false });

// With additional notes
TicketSnap.reportNow('Button click failed');

Parameters:

  • extraNotes (string, optional): Additional notes to include in the report
  • options (object, optional):
    • includeScreenshot (boolean, optional): Whether to include screenshot. Default: true

Behavior:

  • If promptUser: true, shows popup dialog for user to review and add notes
  • If promptUser: false, submits immediately without popup

flushOfflineNow()

Manually retry sending queued offline reports. Returns a Promise.

// Retry queued reports
await TicketSnap.flushOfflineNow();

Use cases:

  • User clicks "Retry" button after network failure
  • Periodic retry in background
  • On app visibility change (handled automatically in non-manual modes)

addBreadcrumb(message: string, category?: string)

Add a breadcrumb (e.g. "Clicked checkout", "Opened modal"). Included in the next snapshot for "what led to this" debugging.

TicketSnap.addBreadcrumb('Clicked checkout button', 'ui');
TicketSnap.addBreadcrumb('Opened payment modal');

setContext(keyOrObj: string | Record<string, unknown>, value?: unknown)

Set context key-value(s) merged into every snapshot (e.g. current route, userId).

TicketSnap.setContext('userId', 'u-456');
TicketSnap.setContext({ route: '/checkout', step: 2 });

getContext(): Record<string, unknown>

Returns a copy of current context (for debugging or custom UI).

getRecentNetwork(): unknown[]

Returns a copy of recent network entries (for debugging or custom UI).

getBreadcrumbs(): BreadcrumbEntry[]

Returns a copy of the breadcrumb trail.

version (string)

Current library version.

console.log(TicketSnap.version); // '0.2.1'

Examples

Basic Setup

import TicketSnap from '@dottech/ticket-snap';

TicketSnap.init({
  endpoint: 'https://api.example.com/api/v1/public/bug-reports',
  projectKey: 'MY-PROJECT',
  triggerMode: 'prompt',
  includeScreenshot: true
});

Auto-Submit on Errors

TicketSnap.init({
  endpoint: 'https://api.example.com/api/v1/public/bug-reports',
  projectKey: 'MY-PROJECT',
  triggerMode: 'auto',  // Auto-submit without user interaction
  includeScreenshot: true,
  onSubmitted: (response) => {
    console.log('Error report submitted:', response);
  }
});

Manual Reporting Only

TicketSnap.init({
  endpoint: 'https://api.example.com/api/v1/public/bug-reports',
  projectKey: 'MY-PROJECT',
  triggerMode: 'manual',  // No automatic reporting
  includeScreenshot: true
});

// Add custom error handler
window.addEventListener('error', (event) => {
  // Your custom logic
  if (shouldReport(event)) {
    TicketSnap.reportNow(`Custom error: ${event.message}`);
  }
});

Vue/Nuxt Integration

// plugins/ticket-snap.client.js (Nuxt 3)
import TicketSnap from '@dottech/ticket-snap';

export default defineNuxtPlugin(() => {
  TicketSnap.init({
    endpoint: 'https://api.example.com/api/v1/public/bug-reports',
    projectKey: 'MY-PROJECT',
    triggerMode: 'prompt',
    routeResolver: () => {
      const route = useRoute();
      return {
        route: route.path,
        params: route.params
      };
    },
    app: {
      appName: 'MyNuxtApp',
      appVersion: '1.0.0',
      environment: process.env.NODE_ENV
    }
  });
});

React Integration

// App.js or index.js
import { useEffect } from 'react';
import TicketSnap from '@dottech/ticket-snap';
import { useLocation, useParams } from 'react-router-dom';

function App() {
  const location = useLocation();
  const params = useParams();

  useEffect(() => {
    TicketSnap.init({
      endpoint: 'https://api.example.com/api/v1/public/bug-reports',
      projectKey: 'MY-PROJECT',
      triggerMode: 'prompt',
      routeResolver: () => ({
        route: location.pathname,
        params: params
      }),
      app: {
        appName: 'MyReactApp',
        appVersion: process.env.REACT_APP_VERSION,
        environment: process.env.NODE_ENV
      }
    });
  }, []);

  return <YourApp />;
}

Custom Redaction

TicketSnap.init({
  endpoint: 'https://api.example.com/api/v1/public/bug-reports',
  projectKey: 'MY-PROJECT',
  redact: [
    // Redact API keys in headers
    { key: /x-api-key/i, replaceWith: '***' },
    
    // Redact credit card numbers in request bodies
    { pattern: /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g, replaceWith: '****-****-****-****' },
    
    // Redact custom tokens
    { pattern: /"token"\s*:\s*"[^"]+"/gi, replaceWith: '"token":"***"' }
  ]
});

Offline Queue Handling

TicketSnap.init({
  endpoint: 'https://api.example.com/api/v1/public/bug-reports',
  projectKey: 'MY-PROJECT',
  triggerMode: 'auto',
  onError: (error) => {
    // Report failed to send, queued automatically
    console.warn('Report queued for retry:', error);
    
    // Show UI indicator
    showOfflineIndicator();
  },
  onSubmitted: () => {
    // Report sent successfully
    hideOfflineIndicator();
  }
});

// Manual retry button
document.getElementById('retry-reports').addEventListener('click', async () => {
  await TicketSnap.flushOfflineNow();
  alert('Queued reports sent');
});

Basic Authentication

TicketSnap.init({
  endpoint: 'https://api.example.com/api/v1/public/bug-reports',
  projectKey: 'MY-PROJECT',
  auth: {
    mode: 'basic',
    secret: 'your-secret-key-here'
  },
  transport: 'formFields'
});

TypeScript Support

Full TypeScript definitions are included. Import types as needed:

import TicketSnap, { 
  TicketSnapConfig, 
  Snapshot, 
  RedactRule, 
  TriggerMode 
} from '@dottech/ticket-snap';

const config: TicketSnapConfig = {
  endpoint: 'https://api.example.com/bug-reports',
  projectKey: 'MY-PROJECT',
  triggerMode: 'prompt' as TriggerMode
};

TicketSnap.init(config);

Browser Compatibility

ticket-snap works in all modern browsers that support:

  • fetch API (or polyfill)
  • XMLHttpRequest
  • Promise
  • localStorage
  • crypto.subtle (for hashing, with fallback)

Tested in:

  • Chrome/Edge 90+
  • Firefox 88+
  • Safari 14+
  • Mobile browsers (iOS Safari, Chrome Mobile)

For older browsers, consider polyfills for fetch and Promise.

Advanced Usage

Custom Error Filtering

// Only report errors matching certain criteria
const originalErrorHandler = window.onerror;
window.onerror = (message, source, lineno, colno, error) => {
  // Your custom filtering logic
  if (shouldReportError(error)) {
    TicketSnap.reportNow(`Filtered error: ${message}`);
  }
  // Call original handler if needed
  if (originalErrorHandler) {
    originalErrorHandler(message, source, lineno, colno, error);
  }
};

Integration with Error Boundaries (React)

class ErrorBoundary extends React.Component {
  componentDidCatch(error, errorInfo) {
    TicketSnap.reportNow(
      `React Error Boundary: ${error.message}`,
      { includeScreenshot: true }
    );
  }

  render() {
    // Your error boundary UI
  }
}

Conditional Reporting

TicketSnap.init({
  endpoint: 'https://api.example.com/api/v1/public/bug-reports',
  projectKey: 'MY-PROJECT',
  triggerMode: 'manual',  // Manual control
  includeScreenshot: true
});

// Only report in production
if (process.env.NODE_ENV === 'production') {
  window.addEventListener('error', (event) => {
    TicketSnap.reportNow(`Production error: ${event.message}`);
  });
}

Troubleshooting

Reports not being sent

Check:

  1. Verify endpoint URL is correct and accessible
  2. Check browser console for errors
  3. Verify authentication credentials if using auth
  4. Check Network tab to see if requests are being made
  5. Ensure triggerMode is set correctly (not 'manual' if expecting auto-reports)

Screenshots not working

Solutions:

  1. If using html2canvas, ensure it's loaded before ticket-snap
  2. Try screenshotStrategy: 'svg-foreignObject' for built-in method
  3. Check browser console for CORS or CSP errors
  4. Some browsers block screenshots in certain contexts (extensions, iframes)

Too much data being captured

Adjust limits:

maxBodyChars: 1000,  // Reduce body size
maxConsole: 50,      // Reduce console logs
ringSize: 10         // Reduce network history

Reports stuck in offline queue

Manual retry:

await TicketSnap.flushOfflineNow();

Check localStorage:

const queue = JSON.parse(localStorage.getItem('tsnap-queue') || '[]');
console.log('Queued reports:', queue.length);

CORS errors

Ensure your API endpoint allows requests from your domain. The library sends POST requests with FormData.

TypeScript errors

Ensure you're using TypeScript 4.5+ and have @types/node installed if needed.

License

MIT © 3bbasDev

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.