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

@triagly/sdk

v1.3.0

Published

JavaScript SDK for Triagly - Turn user feedback into GitHub issues instantly

Downloads

754

Readme

@triagly/sdk

JavaScript SDK for Triagly - Turn user feedback into GitHub issues instantly.

Lightweight (<10KB gzipped) browser widget that captures user feedback and automatically creates issues in your tracker.

Features

  • 🎨 Beautiful UI Widget - Clean, customizable feedback form
  • 🐛 Console Log Capture - Automatically captures browser console errors and warnings for debugging
  • 🤖 Bot Protection - Cloudflare Turnstile integration to prevent spam
  • 🔒 Secure Authentication - Uses publishable keys with optional hardened mode
  • 📊 Metadata Collection - Browser, URL, viewport info automatically captured
  • 🔐 Privacy & Security - Auto-sanitizes sensitive data (tokens, passwords, emails)
  • Lightweight - Minimal bundle size, no heavy dependencies
  • 🎯 TypeScript - Full type definitions included

Installation

NPM

npm install @triagly/sdk
# or
pnpm add @triagly/sdk
# or
yarn add @triagly/sdk

CDN

<!-- Include Cloudflare Turnstile for bot protection -->
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

<!-- Include Triagly SDK -->
<script src="https://unpkg.com/@triagly/sdk/dist/index.min.js"></script>

Quick Start

Method 1: Simple Mode (Recommended for most users)

import Triagly from '@triagly/sdk';

const triagly = new Triagly({
  publishableKey: 'pub_live_abc123',
  turnstileSiteKey: 'your-turnstile-site-key',
});

Method 2: Hardened Mode (For sensitive applications)

import Triagly from '@triagly/sdk';

const triagly = new Triagly({
  publishableKey: 'pub_live_abc123',
  turnstileSiteKey: 'your-turnstile-site-key',
  getToken: async () => {
    // Call your backend to get a hardened token
    const res = await fetch('/api/triagly-token', {
      method: 'POST',
      headers: { Authorization: `Bearer ${userSessionToken}` }
    });
    const { token } = await res.json();
    return token;
  }
});

Method 3: Auto-initialization (CDN)

<script>
  window.TriaglyConfig = {
    publishableKey: 'pub_live_abc123',
    turnstileSiteKey: 'your-turnstile-site-key',
  };
</script>
<script src="https://unpkg.com/@triagly/sdk/dist/index.min.js"></script>

Method 4: React Example

import { useEffect, useRef } from 'react';
import Triagly from '@triagly/sdk';

function App() {
  const triaglyRef = useRef<Triagly | null>(null);

  useEffect(() => {
    triaglyRef.current = new Triagly({
      publishableKey: 'pub_live_abc123',
      turnstileSiteKey: 'your-turnstile-site-key',
      onSuccess: (feedbackId) => {
        console.log('Feedback submitted:', feedbackId);
      },
      onError: (error) => {
        console.error('Feedback error:', error);
      },
      onOpen: () => {
        console.log('Widget opened');
      },
      onClose: () => {
        console.log('Widget closed');
      },
    });

    return () => {
      triaglyRef.current?.destroy();
    };
  }, []);

  return <div>Your App</div>;
}

Configuration

interface TriaglyConfig {
  // Required
  publishableKey: string;         // Your Triagly publishable key

  // Optional - Authentication
  getToken?: () => Promise<string>; // Callback for hardened mode
  turnstileSiteKey?: string;      // Cloudflare Turnstile site key (required for bot protection)

  // Optional - API Configuration
  apiUrl?: string;                // Custom API URL (defaults to production)

  // Optional - UI Customization
  theme?: 'light' | 'dark' | 'auto';
  position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
  buttonShape?: 'rounded' | 'circular' | 'square' | 'pill'; // Default: "rounded"
  buttonText?: string;            // Default: "🐛 Feedback"
  placeholderText?: string;       // Default: "Describe what happened..."
  successMessage?: string;
  errorMessage?: string;

  // Optional - Callbacks
  onSuccess?: (feedbackId: string) => void;  // Called after successful submission
  onError?: (error: Error) => void;          // Called on submission error
  onOpen?: () => void;                       // Called when widget opens
  onClose?: () => void;                      // Called when widget closes

  // Optional - Console Log Capture
  captureConsole?: boolean;                  // Enable console log capture (default: true)
  consoleLogLimit?: number;                  // Max logs to keep (default: 50)
  consoleLogLevels?: ('error' | 'warn' | 'log')[]; // Which levels to capture (default: ['error', 'warn'])

  // Optional - Custom Metadata
  metadata?: Record<string, any>; // Additional metadata to include
}

API

Constructor

const triagly = new Triagly(config: TriaglyConfig);

Methods

open()

Programmatically open the feedback widget.

triagly.open();

close()

Programmatically close the feedback widget.

triagly.close();

submit(data)

Submit feedback programmatically without UI.

await triagly.submit({
  title: 'Bug: Login button not working',
  description: 'When I click the login button, nothing happens.',
  reporterEmail: '[email protected]',
  tags: ['bug', 'login'],
});

destroy()

Remove the widget and clean up event listeners.

triagly.destroy();

submitNPS(submission)

Submit an NPS (Net Promoter Score) metric. Score must be between 0-10.

const response = await triagly.submitNPS({
  score: 9,
  question: 'How likely are you to recommend us?',
  userIdentifier: '[email protected]',
  metadata: { source: 'email-campaign' }
});

submitCSAT(submission)

Submit a CSAT (Customer Satisfaction) metric. Score must be between 1-5.

const response = await triagly.submitCSAT({
  score: 4,
  question: 'How satisfied are you with our service?',
  userIdentifier: '[email protected]'
});

submitCES(submission)

Submit a CES (Customer Effort Score) metric. Score must be between 1-7.

const response = await triagly.submitCES({
  score: 2,
  question: 'How easy was it to resolve your issue?',
  metadata: { support_ticket_id: 'TKT-123' }
});

getMetricStats(params)

Retrieve statistics for a specific metric type with optional date range.

const stats = await triagly.getMetricStats({
  metricType: 'NPS',
  startDate: '2025-01-01',
  endDate: '2025-01-31'
});

Metrics Support

The SDK now supports submitting standard metrics like NPS (Net Promoter Score), CSAT (Customer Satisfaction), and CES (Customer Effort Score).

Submit NPS (Net Promoter Score)

import Triagly from '@triagly/sdk';

const triagly = new Triagly({
  publishableKey: 'pub_live_abc123'
});

// Submit NPS (score: 0-10)
const npsResponse = await triagly.submitNPS({
  score: 9,
  question: 'How likely are you to recommend us?',
  userIdentifier: '[email protected]',
  metadata: { source: 'email-campaign' }
});

console.log('NPS submitted:', npsResponse.id);

Submit CSAT (Customer Satisfaction)

// Submit CSAT (score: 1-5)
const csatResponse = await triagly.submitCSAT({
  score: 4,
  question: 'How satisfied are you with our service?',
  userIdentifier: '[email protected]'
});

console.log('CSAT submitted:', csatResponse.id);

Submit CES (Customer Effort Score)

// Submit CES (score: 1-7, lower is better)
const cesResponse = await triagly.submitCES({
  score: 2,
  question: 'How easy was it to resolve your issue?',
  userIdentifier: '[email protected]',
  metadata: { support_ticket_id: 'TKT-123' }
});

console.log('CES submitted:', cesResponse.id);

Get Metric Statistics

// Get NPS statistics for a date range
const stats = await triagly.getMetricStats({
  metricType: 'NPS',
  startDate: '2025-01-01',
  endDate: '2025-01-31'
});

// Type-safe access based on metric type
if (stats.metric_type === 'NPS') {
  console.log(`NPS Score: ${stats.score}`);
  console.log(`Promoters: ${stats.promoters_percent}%`);
  console.log(`Passives: ${stats.passives_percent}%`);
  console.log(`Detractors: ${stats.detractors_percent}%`);
} else if (stats.metric_type === 'CSAT') {
  console.log(`Average Score: ${stats.average_score}`);
  console.log(`Satisfied: ${stats.percent_satisfied}%`);
} else if (stats.metric_type === 'CES') {
  console.log(`Average Effort Score: ${stats.average_effort_score}`);
}
console.log(`Total Responses: ${stats.total_responses}`);

Metric Score Ranges

  • NPS: 0-10 (0 = Not at all likely, 10 = Extremely likely)
  • CSAT: 1-5 (1 = Very dissatisfied, 5 = Very satisfied)
  • CES: 1-7 (1 = Very easy, 7 = Very difficult)

The SDK automatically validates score ranges and throws descriptive errors for invalid inputs.

Examples

Custom Button Trigger

<button id="custom-feedback-btn">Report Issue</button>

<script>
  const triagly = new Triagly({
    publishableKey: 'pub_live_abc123',
    turnstileSiteKey: 'your-turnstile-site-key',
  });

  document.getElementById('custom-feedback-btn').onclick = () => {
    triagly.open();
  };
</script>

With Custom Metadata

const triagly = new Triagly({
  publishableKey: 'pub_live_abc123',
  turnstileSiteKey: 'your-turnstile-site-key',
  metadata: {
    appVersion: '1.2.3',
    environment: 'production',
    userId: 'user-123',
  },
});

Programmatic Submission

const triagly = new Triagly({
  publishableKey: 'pub_live_abc123',
  turnstileSiteKey: 'your-turnstile-site-key',
});

// Submit without opening widget
try {
  await triagly.submit({
    title: 'Feature Request',
    description: 'It would be great to have dark mode.',
    reporterEmail: '[email protected]',
    tags: ['feature-request'],
  });
  console.log('Feedback sent!');
} catch (error) {
  console.error('Failed:', error);
}

Using Pre-built Themes

<!-- Dark theme -->
<link rel="stylesheet" href="https://unpkg.com/@triagly/sdk/themes/dark.css">

<!-- Minimal theme -->
<link rel="stylesheet" href="https://unpkg.com/@triagly/sdk/themes/minimal.css">

<!-- Gradient theme -->
<link rel="stylesheet" href="https://unpkg.com/@triagly/sdk/themes/gradient.css">

<!-- Ocean theme -->
<link rel="stylesheet" href="https://unpkg.com/@triagly/sdk/themes/ocean.css">

Custom Styling with CSS Variables

:root {
  /* Customize colors easily */
  --triagly-button-bg: #ff0066;
  --triagly-button-bg-hover: #cc0052;
  --triagly-modal-bg: #ffffff;
  --triagly-modal-radius: 20px;
  --triagly-btn-primary-bg: #ff0066;
}

See Styling Guide for all available variables and more examples.

Console Log Capture

The SDK automatically captures browser console messages (errors and warnings by default) to help developers debug issues.

How It Works

  • Intercepts console.error, console.warn, and optionally console.log
  • Stores the last 50 messages in a circular buffer
  • Includes stack traces for errors
  • Automatically sanitizes sensitive data (tokens, passwords, emails)
  • Sends logs with feedback submissions

Example Captured Logs

When a user reports "Login doesn't work", the SDK automatically includes:

[14:30:15] ❌ ERROR: TypeError: Cannot read property 'submit' of undefined
    at LoginForm.handleSubmit (login.js:42:15)

[14:30:10] ⚠️ WARN: Deprecated API used: oldAuthMethod()

[14:30:08] ❌ ERROR: Network request failed: POST /api/auth/login

Privacy & Sanitization

The SDK automatically sanitizes:

  • API keys and tokens (e.g., token=***)
  • Passwords (e.g., password=***)
  • Email addresses (e.g., ***@***.com)
  • JWT tokens (e.g., jwt.***)
  • Credit card numbers
  • GitHub tokens

Disable Console Capture

const triagly = new Triagly({
  projectId: 'your-project-id',
  apiUrl: 'https://your-api.com',
  captureConsole: false,  // Disable console capture
});

Customize Console Capture

const triagly = new Triagly({
  projectId: 'your-project-id',
  apiUrl: 'https://your-api.com',
  captureConsole: true,
  consoleLogLimit: 100,              // Keep last 100 logs
  consoleLogLevels: ['error'],       // Only capture errors
});

Rate Limiting

The SDK automatically rate-limits submissions to 3 per 5 minutes per user (stored in localStorage).

Migration Guide

Upgrading from v0.0.x to v0.1.0

This release introduces breaking changes for improved security and bot protection.

What Changed

  1. projectIdpublishableKey

    • Old: projectId: 'uuid-here'
    • New: publishableKey: 'pub_live_abc123'
  2. apiUrl is now optional

    • The SDK now defaults to the production API
    • Only set this if using a custom deployment
  3. Turnstile bot protection

    • Add turnstileSiteKey to your config
    • Include the Turnstile script in your HTML

Migration Steps

Before:

const triagly = new Triagly({
  projectId: 'your-project-id',
  apiUrl: 'https://your-project.supabase.co/functions/v1',
});

After:

const triagly = new Triagly({
  publishableKey: 'pub_live_abc123', // Get from dashboard
  turnstileSiteKey: 'your-turnstile-site-key', // Get from Cloudflare
});

Don't forget to add Turnstile:

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

Backward Compatibility

The SDK maintains backward compatibility with projectId for now, but will show a deprecation warning. Update to publishableKey as soon as possible.

Browser Support

  • Chrome/Edge (latest 2 versions)
  • Firefox (latest 2 versions)
  • Safari (latest 2 versions)

Development

# Install dependencies
pnpm install

# Build
pnpm build

# Watch mode
pnpm dev

Documentation

License

MIT