Error monitoring SDK for the browser — by Pionne.
Auto-captures uncaught errors and unhandled promise rejections, ships rich client context (UA, OS, viewport, locale, URL), and survives page unloads via navigator.sendBeacon. ~3 KB gzipped, zero dependencies, no source maps required.
Works in any browser app — plain JS, React, Vue, Svelte, Angular, Next.js, Astro, etc.
Pionne is mobile-first: you sign up, create projects, and watch your error feed from the Pionne mobile app, not a web dashboard.
pio_live_…)Pionne.init({ token }) below⚠️ The token is only shown once at project creation — store it in an env var (VITE_PIONNE_TOKEN, NEXT_PUBLIC_PIONNE_TOKEN, etc.) and never commit it.
npm install @pionne/web
// main.tsx (or _app.tsx for Next, root.ts for Remix…)
import { Pionne } from '@pionne/web';
Pionne.init({
token: 'pio_live_xxx',
release: '1.0.0', // optional
// environment auto-detected (localhost → development, else production)
});
That's it. JS errors and unhandled promise rejections are now reported automatically.
try {
doRiskyThing();
} catch (err) {
Pionne.captureException(err, { tags: { feature: 'checkout' } });
}
Pionne.captureMessage('user reached empty state', { level: 'info' });
Pionne.setUser('u_42'); // appears on every event afterward
Pionne.setUser(null); // forget
Pionne.setTags({ tier: 'pro', region: 'eu' });
Pionne.setEnabled(false);
Continuous-ish CPU profiling is shipped on @pionne/[email protected]
(Hermes sampler) and is on the roadmap for @pionne/web next. The browser
implementation will use Performance.profile() (Chrome only, behind a flag
in Firefox/Safari) and fall back to Performance.measure()-based manual
spans where the sampler isn't available.
The API will mirror RN exactly so you can reuse the same wrappers across platforms:
// Coming in @pionne/web ~v0.4.0
await Pionne.profile('CheckoutFlow', async () => {
await fetchCart();
await submitOrder();
}, { route: '/checkout' });
Same backend (POST /api/profiles), same retention model (raw 7 d,
aggregates 90 d), same flame graph view in the mobile dashboard.
If you need profiling today in a browser, you can post your own samples to the endpoint directly — the JSON shape is documented at pionne.agkgcreations.fr/profiling/intro.
The "Bundle ID" anti-token-theft check on Pionne projects is mobile only
(iOS/Android/RN/Flutter). On the web, your token is shipped in the bundled
JS and trivially extractable from the browser — bundle pinning can't help.
The field is hidden in the mobile dashboard for Web projects; don't set
it manually via the API — the SDK does not send a top-level app_id,
so a non-null bundle_id would 403 every event. To limit abuse: regenerate
the token (24 h grace period) if you suspect a leak, and rely on the
per-token rate limit server-side. Use tags for
deployment/tenant differentiation. See the
Bundle ID Pinning docs.
Approximate visitor location (city, region, country) attached to every event,
just like Sentry. Off by default for privacy — flip sendGeography to enable:
Pionne.init({
token: 'pio_live_xxx',
sendGeography: true,
});
Resolved once at startup via a free IP→geo lookup (ipapi.co/json by
default), with a 4 s timeout. If the lookup fails the SDK silently keeps
shipping events without geo. Override the endpoint via geographyEndpoint
if you have your own.
| Option | Type | Default |
|---|---|---|
token |
string (required) |
— |
endpoint |
string |
Pionne production |
release |
string |
unset |
environment |
string |
localhost-detected |
enabled |
boolean |
true |
captureUncaughtErrors |
boolean |
true |
captureUnhandledRejections |
boolean |
true |
autoContext |
boolean |
true |
userIdAnon |
string |
unset |
tags |
Record<string, string> |
unset |
maxStackFrames |
number |
50 |
beforeSend |
(event) => event | null |
unset (drop if null) |
sendGeography |
boolean |
false |
geographyEndpoint |
string |
https://ipapi.co/json/ |
releaseHealth |
boolean |
true |
maxEventsPerSecond |
number |
10 |
maxEventsPerSecond — token-bucket client. Au-delà, les events sont droppés silencieusement. Protège contre les setInterval qui throw en boucle. 0 désactive (déconseillé).releaseHealth — ouvre une session à init() pour calculer le crash-free user rate. Désactivable.sendGeography — opt-in : attache contexts.geo (city/region/country/country_code) résolu IP-side. Pas de geolocation API, pas de permission.Indépendamment du maxEventsPerSecond client, l'API Pionne applique un rate-limit par token sur tous les endpoints publics. Au-delà → HTTP 429 avec un header Retry-After. Empêche un token leaké de drainer ton quota mensuel. Voir doc rate limits.
MIT