sitepong
v0.1.9
Published
Official SitePong SDK for error tracking and monitoring
Downloads
1,304
Maintainers
Readme
Installation
npm install sitepongQuick Start
import sitepong from 'sitepong';
sitepong.init({
apiKey: 'your-api-key',
environment: 'production',
release: '1.0.0',
});
// Errors are automatically captured!
// Or capture manually:
sitepong.captureError(new Error('Something went wrong'));Features
Error Tracking
Automatic error capture for uncaught exceptions and unhandled promise rejections. Works in both browser and Node.js environments.
// Automatic capture - just initialize!
sitepong.init({ apiKey: 'your-api-key' });
// Manual capture with context
try {
riskyOperation();
} catch (error) {
sitepong.captureError(error, {
tags: { component: 'checkout' },
extra: { orderId: '12345' },
});
}
// Capture messages
sitepong.captureMessage('User signed up', 'info', {
user: { id: 'user-123' },
});Session Replay
Record and replay user sessions to see exactly what happened before an error. Privacy-focused with automatic input masking.
sitepong.init({
apiKey: 'your-api-key',
replay: {
enabled: true,
maskInputs: true, // Mask sensitive inputs
blockSelector: '.private', // Block specific elements
sampleRate: 0.5, // Record 50% of sessions
recordNetwork: true, // Capture XHR/fetch requests
recordConsole: true, // Capture console logs
},
});
// Manual control
sitepong.startReplay();
sitepong.stopReplay();
const sessionId = sitepong.getReplaySessionId();Feature Flags
Ship features with confidence using real-time feature flags and A/B testing.
sitepong.init({
apiKey: 'your-api-key',
enableFlags: true,
});
// Wait for flags to load
await sitepong.waitForFlags();
// Boolean flags
if (sitepong.getFlag('new-checkout', false)) {
showNewCheckout();
}
// Multivariate flags / A/B tests
const variant = sitepong.getVariant('button-color', 'blue');
const payload = sitepong.getVariantPayload('pricing-test');
// Get all flags
const allFlags = sitepong.getAllFlags();Product Analytics
Understand user behavior with event tracking and autocapture.
sitepong.init({
apiKey: 'your-api-key',
analytics: {
enabled: true,
autocapturePageviews: true,
autocaptureClicks: true,
autocaptureForms: true,
},
});
// Track custom events
sitepong.track('Purchase Completed', {
product: 'Pro Plan',
revenue: 99,
});
// Identify users
sitepong.identify('user-123', {
name: 'John Doe',
email: '[email protected]',
plan: 'pro',
});
// Group users
sitepong.group('company-456', {
name: 'Acme Inc',
industry: 'Technology',
});
// Manual page views
sitepong.trackPageView('/checkout');Device Intelligence & Fraud Detection
Identify devices and detect fraudulent activity with advanced fingerprinting.
sitepong.init({
apiKey: 'your-api-key',
fingerprint: {
enabled: true,
extendedSignals: true,
},
});
// Get stable visitor ID
const { visitorId, confidence } = await sitepong.getVisitorId();
// Get device signals
const signals = await sitepong.getDeviceSignals();
// Fraud detection
const fraud = await sitepong.getFraudCheck();
if (fraud.riskScore > 0.8) {
blockTransaction();
}Performance Monitoring
Track performance with Web Vitals, custom transactions, and distributed tracing.
sitepong.init({
apiKey: 'your-api-key',
performance: {
enabled: true,
webVitals: true,
navigationTiming: true,
resourceTiming: true,
sampleRate: 1.0,
},
});
// Get Web Vitals
const vitals = sitepong.getWebVitals();
// { LCP: 1200, FID: 50, CLS: 0.1, FCP: 800, TTFB: 200 }
// Custom transactions
const txId = sitepong.startTransaction('checkout-flow');
const spanId = sitepong.startSpan(txId, 'validate-cart');
// ... do work
sitepong.endSpan(txId, spanId);
sitepong.endTransaction(txId);
// Distributed tracing
import { createTraceContext, propagateTrace } from 'sitepong';
const trace = createTraceContext();
fetch('/api/order', {
headers: propagateTrace(trace),
});Cron Monitoring
Monitor scheduled jobs and get alerted when they fail or miss a schedule.
sitepong.init({
apiKey: 'your-api-key',
crons: { enabled: true },
});
// Simple check-in
await sitepong.cronCheckin('daily-backup');
// Start/end pattern
const cron = sitepong.cronStart('nightly-sync');
try {
await performSync();
await cron.ok();
} catch (error) {
await cron.error();
}
// Wrap async functions
await sitepong.cronWrap('hourly-cleanup', async () => {
await cleanupOldRecords();
});Custom Metrics
Track business and application metrics with counters, gauges, and histograms.
sitepong.init({
apiKey: 'your-api-key',
metrics: { enabled: true },
});
// Counters
sitepong.metricIncrement('api.requests', 1, { tags: { endpoint: '/users' } });
// Gauges
sitepong.metricGauge('queue.size', 42);
// Histograms
sitepong.metricHistogram('response.size', 1024);
// Distributions
sitepong.metricDistribution('payment.amount', 99.99);
// Timing
await sitepong.metricTime('db.query', async () => {
return await db.query('SELECT * FROM users');
});
// Manual timer
const timer = sitepong.metricStartTimer('operation.duration');
// ... do work
timer.stop();Database Query Tracking
Monitor database performance and detect N+1 query patterns.
sitepong.init({
apiKey: 'your-api-key',
database: {
enabled: true,
slowQueryThreshold: 100, // Log queries over 100ms
redactParams: true, // Redact sensitive parameters
},
});
// Track queries
const users = await sitepong.dbTrack(
'SELECT * FROM users WHERE id = ?',
() => db.query('SELECT * FROM users WHERE id = ?', [userId])
);
// Detect N+1 patterns
const patterns = sitepong.getDbNPlusOnePatterns();
// [{ query: 'SELECT * FROM orders WHERE user_id = ?', count: 50 }]Production Profiling
Profile your production code to identify performance bottlenecks.
sitepong.init({
apiKey: 'your-api-key',
profiling: {
enabled: true,
sampleRate: 0.1, // Profile 10% of operations
maxDuration: 30000, // Max 30s profiles
},
});
// Profile async operations
const result = await sitepong.profile('processOrder', async () => {
return await processOrder(orderId);
});
// Manual spans
const endSpan = sitepong.startProfileSpan('validate');
// ... validation logic
endSpan();React Integration
First-class React support with components and hooks.
import { ... } from 'sitepong/react';Provider & Error Boundary
import { SitePongProvider, SitePongErrorBoundary } from 'sitepong/react';
function App() {
return (
<SitePongProvider
apiKey="your-api-key"
config={{
environment: 'production',
analytics: { enabled: true, autocapturePageviews: true },
replay: { enabled: true },
}}
>
<SitePongErrorBoundary fallback={<ErrorPage />}>
<YourApp />
</SitePongErrorBoundary>
</SitePongProvider>
);
}Hooks
import {
// Core
useSitePong,
useCaptureException,
useSetUser,
// Feature Flags
useFeatureFlag,
useExperiment,
// Analytics
useTrack,
useIdentify,
// Fingerprinting
useVisitorId,
useFraudCheck,
// Performance
useWebVitals,
usePerformanceTransaction,
// Replay
useReplay,
} from 'sitepong/react';
function CheckoutButton() {
const track = useTrack();
const showNewDesign = useFeatureFlag('new-checkout-design', false);
const { variant } = useExperiment('button-color');
const handleClick = () => {
track('Checkout Started', { items: cart.length });
// ...
};
return (
<button
onClick={handleClick}
style={{ background: variant === 'red' ? 'red' : 'blue' }}
>
{showNewDesign ? 'Complete Purchase' : 'Checkout'}
</button>
);
}React Native / Expo
First-class React Native support with automatic error capture, navigation tracking, and performance monitoring.
# Core
npm install sitepong @react-native-async-storage/async-storage
# Optional: persistent device ID (survives reinstalls)
npm install @sitepong/device-id
# Optional: screen recording
npm install @sitepong/screen-recorderAdd plugins to your app.json (only for native modules you installed):
{
"plugins": [
"@sitepong/device-id",
"@sitepong/screen-recorder"
]
}Then run npx expo prebuild to regenerate native projects.
import { SitePongRNProvider } from 'sitepong/react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
export default function App() {
const navigationRef = useNavigationContainerRef();
return (
<SitePongRNProvider
apiKey="your-api-key"
asyncStorage={AsyncStorage}
navigationRef={navigationRef}
>
<NavigationContainer ref={navigationRef}>
{/* Your screens */}
</NavigationContainer>
</SitePongRNProvider>
);
}React Native Hooks
import {
useScreenTrack,
useAppState,
useRemoteConfig,
useRNPerformance,
} from 'sitepong/react-native';
function MyScreen() {
useScreenTrack('MyScreen');
const appState = useAppState();
const config = useRemoteConfig();
const perf = useRNPerformance('MyScreen');
// perf.markRenderComplete() when screen is ready
}Screen Recording
Captures H.264 video in a rolling buffer. Requires the @sitepong/screen-recorder native module.
npm install @sitepong/screen-recorderTwo modes:
On Error (via Provider)
Recording starts automatically and keeps the last 10 seconds. When an error is captured the buffer is automatically flushed and attached to the error. No video is sent during normal usage.
<SitePongRNProvider
apiKey="your-api-key"
asyncStorage={AsyncStorage}
screenRecording={{
enabled: true,
fps: 1,
quality: 'standard',
// bufferDuration defaults to 10_000 (10s) in on-error mode
}}
>
{/* Your app */}
</SitePongRNProvider>When captureError() is called (or the global error handler fires), the SDK automatically flushes the last 10 seconds of video and attaches it to the error.
Manual
Start and stop recording yourself. Keeps the last 60 seconds by default. Call flushScreenRecording() to upload when you decide.
import {
startScreenRecording,
stopScreenRecording,
flushScreenRecording,
} from 'sitepong/react-native';
// Start — keeps a 60s rolling buffer
startScreenRecording('your-api-key', 'https://ingest.sitepong.com', sessionId, {
fps: 1,
quality: 'standard',
// bufferDuration defaults to 60_000 (60s) in manual mode
});
// Upload the buffer whenever you want
await flushScreenRecording();
// Stop and discard the buffer
stopScreenRecording();Masking sensitive content:
import { SensitiveView } from '@sitepong/screen-recorder';
<SensitiveView>
<Text>SSN: 123-45-6789</Text>
<Text>Card: 4111-1111-1111-1111</Text>
</SensitiveView>Content inside <SensitiveView> is replaced with a black rectangle in recorded video.
Configuration:
| Option | Default (on-error) | Default (manual) | Description |
|---|---|---|---|
| enabled | false | n/a | Enable via provider |
| fps | 1 | 1 | Frames per second (1-10) |
| quality | 'standard' | 'standard' | 'low' / 'standard' / 'high' |
| sampleRate | 1.0 | 1.0 | Fraction of sessions to record (0-1) |
| bufferDuration | 10000 (10s) | 60000 (60s) | Rolling buffer size in ms |
| maxDuration | 3600000 | 3600000 | Max recording duration in ms |
Device Intelligence (React Native)
Persistent device identification that survives app reinstalls. Requires the @sitepong/device-id native module.
npm install @sitepong/device-idAdd the config plugin to your app.json / app.config.js:
{
"plugins": ["@sitepong/device-id"]
}Once installed, the SDK automatically uses the native device ID:
- iOS: UUID stored in Keychain (persists across uninstall/reinstall, cleared on factory reset)
- Android: SHA-256 of
ANDROID_ID+ SharedPreferences UUID (stable per signing key since Android 8)
import { fetchPersistentDeviceId, fetchNativeDeviceSignals } from 'sitepong/react-native';
// Get persistent device ID
const deviceId = await fetchPersistentDeviceId();
// Get full native signals
const signals = await fetchNativeDeviceSignals();
// { deviceId, platform, osVersion, model, manufacturer, isEmulator, screenScale,
// identifierForVendor (iOS), androidId (Android) }Or use the native module directly:
import { getDeviceId, getDeviceSignals } from '@sitepong/device-id';
const deviceId = await getDeviceId();
const signals = await getDeviceSignals();Identity hierarchy (all platforms):
| Level | Scope | Persistence |
|-------|-------|-------------|
| deviceId | Hardware-level | Survives reinstalls (RN only) |
| anonymousId | App-level UUID | Persists in storage |
| sessionId | Session-scoped | 30-min timeout |
| userId | Developer-set | Via identify() |
SQLite Query Tracking
Monitor expo-sqlite query performance with automatic timing and slow query detection.
import { createTrackedDatabase } from 'sitepong/react-native';
import * as SQLite from 'expo-sqlite';
const db = SQLite.openDatabaseSync('myapp.db');
const trackedDb = createTrackedDatabase(db, {
slowQueryThreshold: 100, // ms — log queries slower than this
trackAll: false, // if true, track every query
});
// Use trackedDb exactly like db — same API
const users = trackedDb.getAllSync('SELECT * FROM users WHERE active = 1');Tracked queries emit $sqlite_query events with { sql, durationMs, rowCount } and add breadcrumbs for debugging.
What's Included
- Error Capture: Automatic
ErrorUtils.setGlobalHandler()+ promise rejection tracking - Screen Recording: H.264 rolling buffer — on-error (10s, auto-attached) or manual (60s)
- Device Intelligence: Persistent device ID via Keychain (iOS) / ANDROID_ID (Android)
- SQLite Tracking: Query timing and slow query detection for expo-sqlite
- Navigation Tracking: React Navigation integration, tracks
$screen_viewevents + breadcrumbs - Network Interception: XHR monkey-patch for request tracking + breadcrumbs
- Performance: Cold start timing, screen render tracking
- App State: Automatic foreground/background/inactive tracking
- AsyncStorage: Persistent storage for session data and remote config cache
Remote Config
The SDK can fetch behavior configuration from SitePong servers on init, allowing you to change sampling rates, feature toggles, and transport settings without publishing a new SDK version.
sitepong.init({
apiKey: 'your-api-key',
remoteConfig: { enabled: true },
});
// Check if a remote config feature is enabled
const enabled = sitepong.isRemoteConfigFeatureEnabled('session_replay');
// Get full remote config
const config = sitepong.getRemoteConfig();
// Listen for config changes
sitepong.onRemoteConfigChange((newConfig) => {
console.log('Config updated:', newConfig);
});Remote config is managed from the SDK Config page in the SitePong dashboard, where you can adjust:
- Sampling rates for errors, analytics, replay, and performance
- Feature toggles for any SDK feature
- Autocapture settings (pageviews, clicks, forms, network requests)
- Transport settings (flush interval, batch size, retry attempts)
The SDK caches config locally (with configurable TTL) and refreshes in the background, so your app always starts instantly with the last-known config.
Framework Integration
Next.js
// app/providers.tsx
'use client';
import { SitePongProvider } from 'sitepong/react';
export function Providers({ children }) {
return (
<SitePongProvider
apiKey={process.env.NEXT_PUBLIC_SITEPONG_KEY}
config={{
environment: process.env.NODE_ENV,
replay: { enabled: true },
}}
>
{children}
</SitePongProvider>
);
}Express
import express from 'express';
import sitepong from 'sitepong';
const app = express();
sitepong.init({
apiKey: process.env.SITEPONG_KEY,
environment: 'production',
});
// Error handling middleware
app.use((err, req, res, next) => {
sitepong.captureError(err, {
extra: {
url: req.url,
method: req.method,
userId: req.user?.id,
},
});
next(err);
});Node.js
import sitepong from 'sitepong';
sitepong.init({
apiKey: process.env.SITEPONG_KEY,
environment: process.env.NODE_ENV,
});
// Uncaught exceptions and unhandled rejections
// are automatically capturedConfiguration Reference
sitepong.init({
// Required
apiKey: 'your-api-key',
// Core
endpoint: 'https://ingest.sitepong.com',
environment: 'production',
release: '1.0.0',
autoCapture: true,
maxBatchSize: 10,
flushInterval: 5000,
debug: false,
// Feature Flags
enableFlags: false,
flagsEndpoint: undefined,
// Analytics
analytics: {
enabled: false,
autocapturePageviews: false,
autocaptureClicks: false,
autocaptureForms: false,
},
// Session Replay
replay: {
enabled: false,
maskInputs: true,
blockSelector: undefined,
maskSelector: undefined,
sampleRate: 1.0,
maxSessionDuration: 3600000,
recordNetwork: false,
recordConsole: false,
},
// Performance
performance: {
enabled: false,
webVitals: true,
navigationTiming: true,
resourceTiming: false,
sampleRate: 1.0,
},
// Fingerprinting
fingerprint: {
enabled: false,
extendedSignals: false,
},
// Cron Monitoring
crons: {
enabled: false,
},
// Custom Metrics
metrics: {
enabled: false,
flushInterval: 10000,
maxBatchSize: 100,
},
// Database Tracking
database: {
enabled: false,
slowQueryThreshold: 100,
redactParams: true,
},
// Profiling
profiling: {
enabled: false,
sampleRate: 0.1,
maxDuration: 30000,
},
});API Reference
Core
| Method | Description |
|--------|-------------|
| init(config) | Initialize the SDK |
| captureError(error, context?) | Capture an error |
| captureMessage(message, level?, context?) | Capture a message |
| setUser(user) | Set user context |
| setTags(tags) | Set tags |
| setContext(context) | Set additional context |
| flush() | Flush the error queue |
Feature Flags
| Method | Description |
|--------|-------------|
| getFlag(key, defaultValue?) | Get boolean flag |
| getVariant(key, defaultValue?) | Get variant key |
| getVariantPayload(key, defaultValue?) | Get variant payload |
| getAllFlags() | Get all flags |
| waitForFlags() | Wait for flags to load |
| areFlagsReady() | Check if flags are loaded |
| refreshFlags() | Refresh flags from server |
Analytics
| Method | Description |
|--------|-------------|
| track(event, properties?) | Track custom event |
| trackPageView(url?, properties?) | Track page view |
| identify(userId, traits?) | Identify user |
| group(groupId, traits?) | Group users |
| resetAnalytics() | Reset analytics state |
Session Replay
| Method | Description |
|--------|-------------|
| startReplay() | Start recording |
| stopReplay() | Stop recording |
| isReplayRecording() | Check if recording |
| getReplaySessionId() | Get session ID |
Performance
| Method | Description |
|--------|-------------|
| startTransaction(name, data?) | Start transaction |
| endTransaction(id, status?) | End transaction |
| startSpan(txId, name, data?) | Start span |
| endSpan(txId, spanId, status?) | End span |
| getWebVitals() | Get Web Vitals |
Fingerprinting
| Method | Description |
|--------|-------------|
| getVisitorId() | Get visitor ID |
| getDeviceSignals() | Get device signals |
| getFraudCheck() | Run fraud check |
Metrics
| Method | Description |
|--------|-------------|
| metricIncrement(name, value?, options?) | Increment counter |
| metricGauge(name, value, options?) | Set gauge |
| metricHistogram(name, value, options?) | Record histogram |
| metricDistribution(name, value, options?) | Record distribution |
| metricTime(name, fn, options?) | Time async function |
| metricStartTimer(name, options?) | Start manual timer |
Cron Monitoring
| Method | Description |
|--------|-------------|
| cronCheckin(slug, options?) | Simple check-in |
| cronStart(slug, environment?) | Start cron job |
| cronWrap(slug, fn, environment?) | Wrap async function |
Database
| Method | Description |
|--------|-------------|
| dbTrack(query, fn, source?) | Track async query |
| dbTrackSync(query, fn, source?) | Track sync query |
| getDbQueryCount() | Get query count |
| getDbNPlusOnePatterns() | Get N+1 patterns |
Remote Config
| Method | Description |
|--------|-------------|
| getRemoteConfig() | Get full remote config |
| isRemoteConfigFeatureEnabled(feature) | Check if remote feature is enabled |
| onRemoteConfigChange(callback) | Listen for config changes (returns unsubscribe fn) |
Profiling
| Method | Description |
|--------|-------------|
| profile(name, fn, metadata?) | Profile async function |
| startProfileSpan(name, metadata?) | Start profile span |
| getProfiles() | Get all profiles |
| getLatestProfile() | Get latest profile |
| flushProfiles() | Flush profiles |
Documentation
For full documentation, visit sitepong.com/docs.
License
MIT
