JavaScript / TypeScript

The tinymon package works in browsers, Node, Deno, Bun, and Cloudflare Workers. It has zero runtime dependencies and ships as both ESM and CJS with TypeScript types.

Install

$ npm install tinymonjs
# or:
$ pnpm add tinymonjs
$ yarn add tinymonjs
$ bun add tinymonjs

init()

Call init() once, as early as possible in your app's startup. It installs global handlers for window.onerror / unhandledrejection in browsers, and process.on('uncaughtException') / 'unhandledRejection' in Node.

import { init } from 'tinymonjs';

init({
  dsn:         process.env.TINYMON_DSN,
  environment: 'production',
  release:     '1.0.0',
  sampleRate:  1.0,
});

Options

FieldTypeDescription
dsnstringRequired. Your project DSN, e.g. tm_pub_….
endpointstringOverride the ingest URL. Defaults to https://console.tinymon.dev/api/ingest.
environmentstringFree-form tag — typically production, staging, development.
releasestringA version string for your app — git SHA, semver, anything. Used to scope source maps.
sampleRatenumber0 to 1. Fraction of events to send. Default 1 (send all).
beforeSend(e) => e | nullMutate or drop events before they go out. Return null to drop.

Capturing errors

Most errors are caught automatically. For errors you handle but still want to report, use captureException:

import { captureException, captureMessage } from 'tinymonjs';

try {
  riskyThing();
} catch (err) {
  captureException(err);
}

// Or a plain message, no exception object:
captureMessage('cron job took 28 seconds', 'warning');

captureMessage takes a level: 'error', 'warning', or 'info'.

User & tag context

Attach a user identifier and arbitrary tags to subsequent events. Useful for filtering in the dashboard.

import { setUser, setTag } from 'tinymonjs';

setUser({ id: user.id });
setTag('plan', user.plan);
setTag('feature_flag.new_checkout', 'on');
Privacy. Only send identifiers you're comfortable storing. setUser takes { id } by design — no email, no name, no IP.

Breadcrumbs are short notes about what happened before an error. The SDK keeps the last 30; when an error fires, they're attached to the event.

import { addBreadcrumb } from 'tinymonjs';

addBreadcrumb({
  timestamp: Date.now() / 1000,
  category:  'http',
  message:   'POST /api/orders → 500',
  level:     'error',
});

Filtering with beforeSend

Drop noisy errors, redact fields, or sample by error type:

init({
  dsn: process.env.TINYMON_DSN,
  beforeSend: (event) => {
    // Drop ResizeObserver loop spam from old browsers.
    if (event.exception.value.includes('ResizeObserver')) return null;
    // Redact email-looking strings from the breadcrumbs.
    return event;
  },
});

Frameworks

React

Call init() in index.tsx before ReactDOM.createRoot. Wrap routes in an error boundary that calls captureException:

class ErrorBoundary extends React.Component {
  componentDidCatch(err) {
    captureException(err);
  }
  render() { return this.props.children; }
}

Next.js

Call init() in instrumentation.ts for server-side, and in a top-level client component for the browser. Use the NEXT_PUBLIC_TINYMON_DSN env var for the client side.

Express

import { captureException } from 'tinymonjs';

app.use((err, req, res, next) => {
  captureException(err);
  res.status(500).send('Internal Server Error');
});

Cloudflare Workers

Workers don't have a global process. Pass the DSN as a binding and call captureException manually in your handler's catch block. Add ctx.waitUntil(...) so the event flushes before the request ends.

Source maps

For minified browser bundles, upload source maps so stack traces are readable. Set a unique release on each build, then upload your .js.map files via the dashboard or the upcoming CLI. The server will resolve frames automatically at view time.

Don't ship sourcemaps to users. Either upload to tinymon and serve them with X-Content-Source-Map from a non-public URL, or strip the //# sourceMappingURL= comment from the production bundle.