npm.io
0.1.10 • Published 1h ago

@newtalaria/browser

Licence
MIT
Version
0.1.10
Deps
1
Size
2.9 MB
Vulns
0
Weekly
0

@newtalaria/browser

Browser SDK for Talaria — error capture and session replay (rrweb event streams, not video).

Install

npm install @newtalaria/browser

From this monorepo:

cd new_talaria_js/packages/browser
npm install
npm run build

Quick start

import { Talaria } from '@newtalaria/browser';

Talaria.init({
  // Serverpod base URL (no trailing path)
  dsn: 'http://localhost:8080',
  apiKey: 'tal_live_…', // full raw key from createApiKey
  environment: 'development',
  release: '1.0.0',
  // Continuous session upload (0–1). Default 0 = buffer only until an error.
  replaysSessionSampleRate: 0,
  // On error, promote the ~60s ring buffer (0–1). Default 1.
  replaysOnErrorSampleRate: 1,
  // Post-error upload window. Default 15000 (cheap clip). Use 0 to continue
  // until the 5-minute max duration (Sentry-like, more expensive).
  replaysErrorAfterMs: 15_000,
  maskAllInputs: true, // default
  // Embed same-origin CSS into the snapshot (needed for auth-gated UIs like CMS).
  // Default false — player re-fetches public stylesheet hrefs instead.
  // inlineStylesheet: true,
});

try {
  throw new Error('Something broke');
} catch (error) {
  await Talaria.captureException(error);
}

await Talaria.captureMessage('Checkout opened', 'info');
console.log('replay', Talaria.getReplayId());

await Talaria.flush();
await Talaria.close();

Talaria.init also installs window.onerror / unhandledrejection handlers unless you pass disableDefaultIntegrations: true. Opaque cross-origin "Script error." events (no usable Error object) are ignored by default.

Optional init tags are merged into every captured event (per-call tags win on key conflict).

Script tag (IIFE)

For hosts without a bundler (e.g. Silverstripe Requirements):

<script src="/path/to/talaria.browser.iife.js"></script>
<script>
  Talaria.init({
    dsn: 'https://api.example.com',
    apiKey: 'tal_live_…',
    environment: 'production',
    replaysSessionSampleRate: 0,
    replaysOnErrorSampleRate: 0,
  });
</script>

Build output: dist/talaria.browser.iife.js (also produced by npm run build / npm run build:iife).

You pay for uploaded + retained bytes, not for local buffering. Prefer error clips in production; keep full-session sampling low.

Traffic replaysSessionSampleRate replaysOnErrorSampleRate replaysErrorAfterMs
High (100k+/day) 0.01 1.0 15000 (default clip)
Medium (10k–100k/day) 0.1 1.0 15000
Low (under 10k/day) 0.25 1.0 15000
Marketing / docs site 0 1.0 15000
Rich post-error context 0 1.0 0 (continue to 5 min cap)

Defaults (session=0, onError=1, errorAfterMs=15000) are the cheapest useful profile: quiet traffic costs nothing; each sampled error keeps ~60s before + ~15s after.

Replay sampling behavior

Mode Behavior
Session sample hit replays/start immediately; segments upload every ~5s or ~100KB until unload or 5 min max
Session sample miss Record into a ~60s ring buffer; nothing uploaded until an error sample hits
Error sample + replaysErrorAfterMs > 0 Upload buffer (≤~960KiB gzip pack target / 1MiB server cap) + trailing window, hard caps 12 segments or 2MiB compressed, attach replayId only if segments landed, finish, return to buffer mode
Error sample + replaysErrorAfterMs = 0 Upload buffer then continue like session mode until 5 min / unload / size caps
Server limit (replay segments / total size / duration) Stop uploading that replay; no retries
Oversized FullSnapshot on segment 0 Abort the clip (no blank orphan upload); error event gets replay.capture=failed + reason. Meta+FullSnapshot are taken/fitted atomically so a soft estimated take window cannot orphan the snapshot.
Oversized single non-FS rrweb event Dropped with a console warning (cannot fit under segment cap)
pagehide / close Flush pending segments with fetch keepalive, then replays/finish; close fully resets so init() works again (React Strict Mode)
Replay capture outcome tags

When an error/fatal event attempts an on-error clip (replaysSessionSampleRate miss), the SDK may attach:

Tag Values
replay.capture ok | failed | skipped
replay.capture_reason oversized_full_snapshot | no_full_snapshot | upload_failed | not_sampled | buffer_empty

Failed captures do not set replayId (avoids linking a blank player). Details are also under extra.replayCapture.

When the paint base cannot ship, extra.replayCapture also includes size diagnostics where available:

Field Meaning
fullSnapshotEstimatedBytes JSON size estimate of the FullSnapshot
fullSnapshotCompressedBytes Gzip size of the FullSnapshot alone (when measured)
metaEstimatedBytes JSON size estimate of the Meta event
maxUncompressedSegmentBytes / maxCompressedSegmentBytes Current pack caps

Use these to see how far over budget a CMS/admin snapshot was when a clip was aborted.

Serverpod URL pattern

Talaria uses Serverpod RPC, not REST resource URLs:

Call Method URL
Start replay POST {baseUrl}/replays/start
Upload segment POST {baseUrl}/replays/ingestSegment
Finish replay POST {baseUrl}/replays/finish
Ingest event POST {baseUrl}/events/ingest

Local default: http://localhost:8080.

Bodies are JSON with named parameters and __className__ on typed inputs:

{
  "input": {
    "__className__": "StartReplayInput",
    "replayId": "…",
    "environment": "development",
    "sessionId": "…",
    "url": "http://localhost:5173/"
  }
}

Auth (ingest):

  • X-API-Key: tal_live_… (preferred)
  • or Authorization: Bearer tal_live_…
ByteData (gzipBytes)

Serverpod serializes ByteData as a wrapped base64 string:

decode('<base64>', 'base64')

Segment payloads are a gzip-compressed JSON array of rrweb events.

Custom rrweb events:

  • talaria-console — console level + args (truncated)
  • talaria-network — method, redacted URL, status, duration (no bodies / no auth headers)

Privacy defaults: maskAllInputs: true, password fields masked, [data-talaria-mask] blocked.

Auth-gated CSS (CMS / admin)

By default inlineStylesheet is false: linked stylesheets stay as hrefs and are re-fetched when you watch the replay. That works for public CSS, but fails for login-protected admin CSS (player has no session cookies).

Set inlineStylesheet: true so same-origin stylesheet rules are embedded while the user is logged in. Cross-origin sheets without CORS still cannot be inlined (browser cssRules restriction).

Failed HTTP requests → events

By default, completed fetch / XHR responses with status 500–599 are also sent as Talaria events (message like HTTP 500: GET /api/...). Replay still gets talaria-network breadcrumbs for all requests.

Talaria.init({
  dsn: 'https://api.example.com',
  apiKey: 'tal_live_…',
  environment: 'production',
  // Default true
  captureFailedRequests: true,
  // Default [[500, 599]]. CMS admin often wants 4xx too:
  failedRequestStatusCodes: [[400, 599]],
  // Extra URL substrings to skip (Talaria /events and /replays are always skipped)
  failedRequestIgnoreUrls: ['/health'],
});

Transport errors (network down, no status) are not promoted as HTTP events; uncaught exceptions from failed fetches still go through normal error / unhandledrejection handlers when the app rethrows.

Public API

API Description
Talaria.init(options) Configure + start recording
Talaria.captureException(error) Ingest error (+ replay link when sampled)
Talaria.captureMessage(message, level?) Ingest message
Talaria.getReplayId() Active upload replay id, or null
Talaria.flush() Upload buffered segments
Talaria.close() Stop recording, flush, finish

Example

See ../../examples/sdk-spa for a minimal page wired to localhost:8080.

Local file: install (marketing / monorepo)

dist/ is gitignored. After editing SDK src/, rebuild before the consumer picks up changes:

cd new_talaria_js/packages/browser
npm run build
# or: npm install in the consumer — `prepare` runs build for file: installs

Then restart the Next app with a clean cache:

cd ../new_talaria_marketing   # sibling repo
rm -rf .next
npm run dev

Serverpod must be restarted after changing ReplayLimits (compressed cap is 512KiB). A live process still on 256KiB will 400 every near-max segment.

Verify error-clip ingest
  1. Restart Serverpod (512KiB live).
  2. Rebuild the browser package; restart marketing with clean .next.
  3. Browse /docs/** ~30s, throw one test exception.
  4. Expect: a few ingestSegment 200s, one finish, zero compressed-size 400 spam, dashboard replay plays.
  5. Throw again later: a new bounded clip is allowed (buffer mode reset).

Keywords