@triagly/sdk
v1.3.0
Published
JavaScript SDK for Triagly - Turn user feedback into GitHub issues instantly
Downloads
754
Maintainers
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/sdkCDN
<!-- 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 optionallyconsole.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/loginPrivacy & 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
projectId→publishableKey- Old:
projectId: 'uuid-here' - New:
publishableKey: 'pub_live_abc123'
- Old:
apiUrlis now optional- The SDK now defaults to the production API
- Only set this if using a custom deployment
Turnstile bot protection
- Add
turnstileSiteKeyto your config - Include the Turnstile script in your HTML
- Add
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 devDocumentation
- Accessibility Guide - WCAG 2.1 AA compliance & keyboard navigation
- Callbacks Guide - Complete callback documentation
- Console Capture Guide - Automatic console log capture
- Styling Guide - Customize button and modal styles
- Local Testing Guide - Test the package locally
- Contributing Guide - Contribution guidelines
- Release Guide - How to create releases
License
MIT
