Getting started
Install
Section titled “Install”npm install crxtraceNo bundler? Grab the single-file IIFE build from
node_modules/crxtrace/dist/crxtrace.global.js and see
Without a bundler below.
Manifest permissions
Section titled “Manifest permissions”{ "permissions": ["storage", "alarms"], "host_permissions": ["https://your-ingest-endpoint/*"]}| Permission | Why |
|---|---|
storage |
Required. It’s what makes the queue durable across worker death. Without it, CrxTrace is just a slower console.error. |
alarms |
Strongly recommended. A mostly-idle worker may never wake on its own; the alarm drains the queue on a schedule. |
host_permissions |
Your ingest origin, so the worker is allowed to fetch it. |
Initialise every surface
Section titled “Initialise every surface”Call init() once per surface — the service worker, each content script, and
any extension page you want covered. It’s the same call everywhere; the SDK
detects where it is running.
import * as CrxTrace from "crxtrace";
CrxTrace.init({ dsn: "https://your-ingest-endpoint/pk_live_..." });import * as CrxTrace from "crxtrace";
CrxTrace.init({ dsn: "https://your-ingest-endpoint/pk_live_..." });Only the service worker owns the durable queue and talks to the network. Content scripts and extension pages relay their events through the worker — they never send from a page they don’t control. That’s deliberate: a content script issuing cross-origin requests from someone else’s site is both a privacy problem and a CSP problem.
Name your async work
Section titled “Name your async work”This is the single highest-value thing you can do, and it takes one line.
const transcript = await CrxTrace.track("fetchTranscript", getTranscript(id));If Chrome terminates the worker while that promise is outstanding, the next
boot reports an sw_terminated event naming fetchTranscript. Without it you
get silence, because nothing threw — the worker simply stopped.
See Service worker deaths for what this catches and why nothing else catches it.
Report errors you handle
Section titled “Report errors you handle”Uncaught errors and unhandled rejections are captured automatically. For the ones you catch yourself:
try { await syncBookmarks();} catch (error) { CrxTrace.captureException(error, { tags: { feature: "sync" } });}Verify it works
Section titled “Verify it works”Set debug: true and the SDK narrates its decisions to the console:
CrxTrace.init({ dsn: "https://your-ingest-endpoint/pk_live_...", debug: true,});Then throw something on purpose from your service worker console:
throw new Error("crxtrace smoke test");By default events are batched with a 1.5 second debounce, so give it a moment —
or call await CrxTrace.flush() to send immediately.
Without a bundler
Section titled “Without a bundler”The IIFE build exposes a CrxTrace global and works with importScripts():
importScripts("vendor/crxtrace.global.js");
CrxTrace.init({ dsn: "https://your-ingest-endpoint/pk_live_..." });<script src="vendor/crxtrace.global.js"></script><script> CrxTrace.init({ dsn: "https://your-ingest-endpoint/pk_live_..." });</script>Copy the file out of node_modules/crxtrace/dist/crxtrace.global.js as part of
your build.
Try the demo extension
Section titled “Try the demo extension”The repository ships a real MV3 extension that triggers each failure mode on demand, plus a throwaway server that pretty-prints arriving events:
git clone https://github.com/sabbir-offc/crxtrace.gitcd crxtracenpm installnpm run demo:sync # build the SDK into the demonpm run demo:ingest # start the receiverLoad examples/demo-extension unpacked at chrome://extensions and press the
buttons. The one worth waiting for is Start task, then let worker die —
close the popup, wait about 30 seconds, then wake the extension:
⚠ worker terminated after 31s while running: fetchTranscript- Service worker deaths — the failure mode this exists for
- Configuration — every option and its default
- Self-hosting — run your own backend