@syncropel/error-reporter
v0.2.1
Published
Browser error capture for Syncropel apps. Pluggable transports (Mock / Console / HTTP), privacy-aware sanitization, per-session rate limiting, fingerprint deduplication.
Maintainers
Readme
@syncropel/error-reporter
Browser error capture for Syncropel apps. Reports errors to a pluggable transport (Mock for tests, Console for dev, HTTP for production) with privacy-aware sanitization, per-session rate limiting, and fingerprint deduplication built in.
Why
Most error trackers add a third-party SaaS dependency, ship your data to vendor servers, and force you to learn one more dashboard. This reporter ships errors as structured records to whatever endpoint you choose — your own backend, a debug log, or an in-memory buffer for tests. Same query surface as the rest of your app data; no separate vendor.
Install
npm install @syncropel/error-reporter
# Peer dependencies:
npm install react @syncropel/config| Peer | Version | Why |
|---|---|---|
| react | >=18 | Used internally for the optional React error boundary helpers |
| @syncropel/config | ^0.10.0 | Provides the canonical schemas the report bodies pair with |
Quick start
import { Reporter, ConsoleTransport } from '@syncropel/error-reporter';
const reporter = new Reporter({
transport: new ConsoleTransport(),
release: process.env.NEXT_PUBLIC_RELEASE_VERSION ?? 'dev',
enabled: () => process.env.NODE_ENV !== 'test',
maxSessionEmits: 50, // cap per session (default 50)
fingerprintCooldownMs: 60_000, // dedupe window (default 60s)
});
// Wire to window error events — these methods normalize the browser event,
// derive the route, and are fire-and-forget (safe as listeners directly).
window.addEventListener('error', (event) => {
reporter.captureWindowError(event);
});
window.addEventListener('unhandledrejection', (event) => {
reporter.captureUnhandledRejection(event);
});
// Or call directly from your error boundary / route error / fetch-failure path
await reporter.reportError(error, {
source: 'react-boundary',
route: '/dashboard',
component: 'WorkspaceRenderer',
});Transports
Three reference implementations ship in-tree. All implement the same Transport interface:
interface Transport {
send(body: ErrorBody): Promise<void>;
}MockTransport — in-memory buffer. Use in tests. Records are inspectable via transport.records.
ConsoleTransport — console.error() with formatted output. Use in dev.
HttpTransport — wraps each report in a canonical Syncropel record (F13: 8 fields) and POSTs it to <url>/v1/records. Use in production. Constructor accepts { url, namespace?, headers? }.
import { HttpTransport } from '@syncropel/error-reporter';
const reporter = new Reporter({
transport: new HttpTransport({
url: 'https://telemetry.syncropel.com',
namespace: 'web_errors', // optional — injected as body.namespace
headers: { Authorization: `Bearer ${token}` }, // optional — if the endpoint requires auth
}),
release: '...',
});url is the base URL of a Syncropel kernel; the transport appends /v1/records.
namespace, when set, is injected into the record body as body.namespace
— the kernel derives a record's namespace from that field, so anonymous error
writes land in a dedicated namespace (e.g. web_errors) rather than default.
The fetch uses keepalive: true, so an in-flight report survives a
page-unload navigation without a separate Beacon path.
API surface
import {
// Main orchestrator
Reporter,
// Transports
Transport, // interface
MockTransport,
ConsoleTransport,
HttpTransport,
// Pure helpers
fingerprint, // sync hash from name+message+top-frame
fingerprintAsync, // SHA-256 fingerprint (preferred)
normalizeMessage,
sanitizeMessage,
sanitizeStack,
sanitizeFunctionName,
sanitizeFilePath,
collectRuntime,
parseUserAgent,
getSessionId,
isOptedOut,
OPT_OUT_KEY,
SESSION_KEY,
// Types
ErrorBody, // the report body shape
ResolvedBody, // the deploy-time resolution body shape
ReporterConfig,
ReportContext,
Severity,
CaptureSource,
StackFrame,
Runtime,
} from '@syncropel/error-reporter';Sub-path imports
import { ConsoleTransport } from '@syncropel/error-reporter/transports/console';
import { HttpTransport } from '@syncropel/error-reporter/transports/http';
import { MockTransport } from '@syncropel/error-reporter/transports/mock';Configuration
interface ReporterConfig {
transport: Transport; // required
release: string; // required; e.g. "1.2.3" or "git-sha"
enabled?: () => boolean; // default: enabled unless opted out
maxSessionEmits?: number; // default: 50
fingerprintCooldownMs?: number; // default: 60_000 (1 minute)
}enabled— re-evaluated on everyreportError()call. Defaults to "enabled unless the user opted out" (see Privacy below). Override to additionally gate by environment, feature flag, etc.maxSessionEmits— caps emissions per session. Once exceeded, emits stop. Prevents one user with a broken page from flooding your transport.fingerprintCooldownMs— same fingerprint within window is suppressed. Each emit carriessuppressed_in_windowcount of how many were dropped during dedupe.
Privacy + sanitization
- Stack frames are sanitized: function names + file paths only; no inline closures, no source maps applied client-side
sanitizeMessagestrips email-like patterns and anybearer ...-style tokensruntime.viewport.{w,h}is rounded to nearest 50px to avoid fingerprinting precision- Users can opt out via
localStorage.setItem('syncropel-telemetry-disabled', '1')— theReporterchecksisOptedOut()on every emission and short-circuits. The key is exported asOPT_OUT_KEY.
Report body shape
interface ErrorBody {
kind: 'syncropel.error.v1';
fingerprint: string;
severity: 'error' | 'warning' | 'info';
name: string;
message: string;
stack: StackFrame[];
source: CaptureSource;
component?: string;
route: string;
runtime: Runtime;
release: string;
session_id: string;
suppressed_in_window?: number;
}A companion ResolvedBody shape closes an error thread when a deploy ships the fix:
interface ResolvedBody {
kind: 'syncropel.error.resolved.v1';
fingerprint: string;
fixed_in_release: string;
note?: string;
resolver: string; // DID of whoever resolved it
}License
Apache-2.0. See LICENSE.
Contributing
Source lives at syncropic/syncropel-core/sdks/typescript-error-reporter. Issues + PRs welcome.
