@yraylabs/boring-analytics
v1.0.2
Published
Official SDK for Boring Analytics — product analytics and error monitoring
Maintainers
Readme
boring-analytics
Official SDK for Boring Analytics — lightweight product analytics and error monitoring.
- Minimal dependencies — native
fetch/crypto; optionalweb-vitalsfor Core Web Vitals - Works everywhere — browser, Node.js, Edge runtimes
- TypeScript-first — full type definitions included
- Tiny footprint — tree-shakeable ESM + CJS builds
- Auto-batching — queues events and flushes efficiently
- Error monitoring — captures unhandled errors automatically
Installation
npm install boring-analyticsQuick Start
import { BoringAnalytics } from 'boring-analytics';
const analytics = new BoringAnalytics({
apiKey: 'your-api-key',
});
// Track events
analytics.track('button_clicked', {
properties: { buttonId: 'signup', page: '/home' },
});
// Identify users
analytics.identify('user-123', {
traits: { email: '[email protected]', name: 'Jane' },
});
// Track page views
analytics.page('Home');Configuration
const analytics = new BoringAnalytics({
// Required
apiKey: 'your-api-key',
// Optional
endpoint: 'https://api.boringanalytics.io', // API endpoint
flushAt: 20, // Flush after N queued items (default: 20)
flushInterval: 5000, // Flush every N ms (default: 5000)
maxRetries: 3, // Retry failed requests (default: 3)
debug: false, // Log debug info to console
// Auto-capture
autoCapture: {
errors: true, // Capture unhandled errors (default: true)
pageViews: false, // Auto-track SPA navigation (default: false)
webVitals: false, // Send LCP, FID, INP, CLS, TTFB (default: false)
scrollDepth: false, // Send scroll_depth at 25%, 50%, 75%, 100% (default: false)
outboundClicks: false, // Track external link and download clicks (default: false)
},
// Merged into every event
defaultProperties: {
appVersion: '1.2.0',
environment: 'production',
},
// Called on transport errors
onError: (err) => console.error('Analytics error:', err),
});API
track(name, options?)
Track a named event.
analytics.track('purchase_completed', {
properties: {
orderId: 'order-456',
amount: 99.99,
currency: 'USD',
},
});identify(userId, options?)
Identify the current user. Sets the userId for all subsequent calls.
analytics.identify('user-123', {
traits: {
email: '[email protected]',
name: 'Jane Doe',
plan: 'pro',
},
});page(name?, options?)
Track a page view.
analytics.page('Pricing');
analytics.page('Product', {
properties: { productId: 'abc-123' },
});screen(name, options?)
Track a screen view (mobile apps).
analytics.screen('Settings');group(groupId, options?)
Associate the user with a group/organization.
analytics.group('org-456', {
traits: { name: 'Acme Inc', plan: 'enterprise' },
});alias(userId, previousId)
Link two user identities.
analytics.alias('user-123', 'anon-789');trackRevenue(options)
Track revenue (e.g. purchase, subscription). Sends a purchase event with amount and currency.
analytics.trackRevenue({
amount: 29.99,
currency: 'USD',
properties: { orderId: 'ord_123', productId: 'sku_456' },
});trackExposure(options)
Track feature-flag or A–B test exposure.
analytics.trackExposure({
flag: 'pricing_test',
variant: 'treatment',
properties: { page: '/pricing' },
});captureError(error, options?)
Manually capture an error.
try {
await riskyOperation();
} catch (err) {
analytics.captureError(err, {
level: 'ERROR', // DEBUG | INFO | WARNING | ERROR | FATAL
tags: { component: 'checkout' },
metadata: { orderId: '123' },
release: 'v1.2.3',
handled: true,
});
}flush()
Immediately send all queued events.
await analytics.flush();reset()
Clear the current user identity. Call on logout.
analytics.reset();shutdown()
Gracefully shut down — flushes remaining events and removes global error handlers.
await analytics.shutdown();Auto-Capture: Web Vitals, Scroll Depth, Outbound Clicks
Enable optional automatic tracking (browser only):
const analytics = new BoringAnalytics({
apiKey: 'your-api-key',
autoCapture: {
pageViews: true, // SPA route changes (pushState/replaceState/popstate)
webVitals: true, // LCP, FID, INP, CLS, TTFB → event "web_vitals"
scrollDepth: true, // 25%, 50%, 75%, 100% → event "scroll_depth"
outboundClicks: true, // External links & downloads → event "outbound_click"
},
});- webVitals: Sends one event per metric with
metric,value,rating,delta,id. - scrollDepth: Sends one event per threshold (25, 50, 75, 100) with
depth. - outboundClicks: Sends
href,domain, andlink_type(externalordownload).
Auto Page View Tracking
Enable automatic page view tracking for SPAs:
const analytics = new BoringAnalytics({
apiKey: 'your-api-key',
autoCapture: {
pageViews: true, // Tracks pushState, replaceState, and popstate
},
});Error Monitoring
Unhandled errors are captured automatically by default. You can also capture errors manually:
// Automatic (enabled by default)
// window.onerror and unhandledrejection are captured
// Manual capture
analytics.captureError(new Error('Something went wrong'), {
level: 'WARNING',
tags: { feature: 'checkout' },
});Context Enrichment
The SDK automatically captures context in the browser:
- Browser: name, version
- OS: name, version
- Device: type (desktop/mobile/tablet)
- Page: URL, referrer, title, path
- User Agent and Locale
You can override or extend context per-call:
analytics.track('event', {
context: {
location: { country: 'US', city: 'San Francisco' },
},
});Server-Side Usage (Node.js)
import { BoringAnalytics } from 'boring-analytics';
const analytics = new BoringAnalytics({
apiKey: 'your-server-api-key',
autoCapture: { errors: false },
});
// Track server-side events
analytics.identify('user-123');
analytics.track('api_request', {
properties: { endpoint: '/users', method: 'GET', duration: 42 },
});
// Flush before process exits
process.on('SIGTERM', async () => {
await analytics.shutdown();
process.exit(0);
});License
MIT
