Menu

Choose a theme and configure high-contrast mode. Preferences are saved in your browser only.

User preferences

Theme

Pick a palette or follow your system preference.

High contrast

Sharper text and borders. System follows your OS setting.

Copy page

JavaScript SDK

Install the Bicho JavaScript SDK to capture browser errors and report Node.js exceptions. Configure filtering, batching, retries, and delivery to your server.

@bicho-fyi/javascript reports errors over OTLP/HTTP JSON without runtime dependencies. It includes TypeScript declarations, an ESM entry point for browsers and Node.js 24+, and a standalone browser script. Importing the ESM package does not initialize collection.

For other ways to send errors, see SDKs and integrations.

Browser setup

npm install @bicho-fyi/javascript

Initialize once in your browser entry point:

import * as Bicho from '@bicho-fyi/javascript';

Bicho.init({
  endpoint: 'https://your-bicho-worker.example/v1/logs',
  key: 'YOUR_PUBLIC_KEY',
  serviceName: 'storefront',
  environment: 'production',
  release: 'storefront@1.0.0',
  tags: { app: 'storefront' },
});

Replace the endpoint and key with otlp.endpoint and otlp.headers['X-Bicho-Key'] from project registration. The key identifies the project; there is no separate project-ID option.

After initialization, browser listeners capture uncaught JavaScript errors and unhandled promise rejections. They do not suppress the browser’s normal error handlers. Errors that happened before initialization are not captured. Caught exceptions, console.error(), failed HTTP requests, and resource-loading failures without JavaScript error data require explicit reporting.

Set autoCapture: false for manual capture only. If collection must wait for consent, delay init() itself. Disabling listeners does not prevent explicit capture calls from sending data.

Load a standalone script

The standalone bundle exposes window.Bicho and reads attributes from its own script element:

<script
  src="https://cdn.jsdelivr.net/npm/@bicho-fyi/javascript@1.1.0/dist/bicho.min.js"
  data-endpoint="https://your-bicho-worker.example/v1/logs"
  data-key="YOUR_PUBLIC_KEY"
  data-service-name="storefront"
  data-environment="production"
  data-release="storefront@1.0.0"
  data-tags='{"app":"storefront"}'
  data-contexts='{"checkout":{"stage":"payment"}}'
  defer
></script>

This pins the SDK source version, 1.1.0. The CDN path requires that version to be published to npm. You can also host dist/bicho.min.js yourself. For native browser ESM, use dist/index.js and call init() explicitly.

Attribute Behavior
data-endpoint Collector URL. Required with data-key for automatic initialization.
data-key Public ingest key. Required with data-endpoint.
data-service-name Sets service.name; defaults to app/browser.
data-environment Sets deployment.environment.name.
data-release Sets service.version.
data-tags JSON object of scalar labels.
data-contexts JSON object of named context objects.
data-auto-capture Exactly true or false. Defaults to true.

If neither endpoint nor key attribute is present, the bundle waits for Bicho.init(). If either is present, it attempts initialization and requires both. Invalid configuration, malformed JSON, arrays instead of JSON objects, or an invalid data-auto-capture value throw before collection starts.

Use explicit initialization when you need a beforeSend function, a default level, or consent control. These have no script attributes. Use one SDK build per page, rather than loading ESM and the standalone bundle together. Calls to window.Bicho must run after the script has loaded; a deferred script has not executed when a following inline script runs during parsing.

Capture explicitly

This browser example reports a failed request because failed HTTP requests are not captured automatically:

import * as Bicho from '@bicho-fyi/javascript';

export async function refreshCatalog() {
  try {
    const response = await fetch('/api/catalog');
    if (!response.ok) throw new Error(`Catalog returned HTTP ${response.status}`);
    return await response.json();
  } catch (error) {
    Bicho.captureException(error, {
      tags: { operation: 'catalog-refresh' },
      contexts: { catalog: { stage: 'fetch' } },
    });
    return null;
  }
}

Call this after the browser setup above. To report a failure without an exception object:

Bicho.captureMessage('Catalog refresh failed', {
  tags: { operation: 'catalog-refresh' },
});

Both methods return a 32-character hexadecimal occurrence UID when queued, or undefined if dropped. The UID is not delivery confirmation or the server’s event ID. Bicho derives a project-scoped event ID from the UID. Equal errors can group into one issue while remaining separate occurrences.

captureException() accepts any thrown value. An Error supplies its name, message, and stack. Other values may have less evidence; an arbitrary object is not serialized as an exception message. Pass useful structured details in contexts instead.

Capturing an error and then rethrowing it can produce a second occurrence if it reaches an automatic browser listener. Choose where your application reports it.

Exceptions and messages default to error. Available levels are fatal, error, warning, info, debug, and log. The server accepts exceptions regardless of severity, but rejects ordinary messages below ERROR. The SDK preserves your chosen level; it does not promote a low-severity message to ERROR.

Backend reporting

Use createClient() for isolated reporting. Each client owns its configuration, queue, retry state, and shutdown. Creating a client per request avoids sharing request context between concurrent requests.

This Node.js ESM example reports a failed upstream request and keeps the original failure visible to its caller:

import { createClient } from '@bicho-fyi/javascript';

export async function loadCatalog() {
  const tracker = createClient({
    endpoint: 'https://your-bicho-worker.example/v1/logs',
    key: 'YOUR_PUBLIC_KEY',
    serviceName: 'storefront/api',
    environment: 'production',
  });

  try {
    const response = await fetch('https://example.com/catalog.json');
    if (!response.ok) throw new Error(`Catalog returned HTTP ${response.status}`);
    return await response.json();
  } catch (error) {
    tracker.captureException(error, {
      contexts: { request: { operation: 'load-catalog' } },
    });
    throw error;
  } finally {
    const delivered = await tracker.close(5000);
    if (!delivered) console.error('Bicho delivery did not complete successfully');
  }
}

Use Node.js 24+ with ESM, for example an .mjs file or a package with "type": "module". Backend capture is manual: the SDK installs no uncaughtException or unhandledRejection process listeners and does not change crash behavior. Integrate it with your framework’s error handler and await flush() or close() before the process exits. Do not assume a short-lived runtime will keep delivery work alive after your handler returns.

The default singleton API also works on the backend, but shares state across its callers. createClient() returns only captureException, captureMessage, flush, and close; an instance has no public init() method. In a browser, isolated clients also install automatic listeners by default, so use autoCapture: false if an existing client already handles global errors.

This public-key SDK cannot authenticate secret-enabled projects, even on the backend. For those projects, use a logs exporter that can send both required headers, as described in OTLP ingestion.

Configuration and filtering

init(options) initializes the default client and returns nothing. createClient(options) initializes and returns an independent client. Both accept the same options:

Option Type and default Purpose
endpoint Required string HTTPS collector origin followed by exactly /v1/logs. HTTP is allowed only for localhost, 127.0.0.1, and [::1].
key Required string Public ingest key, 1 to 128 ASCII letters, digits, underscores, or hyphens.
serviceName Optional string Defaults to app/browser with a browser window, otherwise app/backend.
environment Optional string Deployment environment label.
release Optional string Application version label.
level Optional Level, default error Default capture severity.
tags Optional Record<string, string | number | boolean> Scalar labels on each occurrence.
contexts Optional Record<string, Record<string, unknown>> Named structured context objects.
autoCapture Optional boolean, default true Enables browser error and rejection listeners. Has no effect on the backend.
beforeSend Optional (event: Event) => Event | null Synchronous filtering or editing before serialization.

serviceName, environment, and release must be strings of at most 512 characters. Endpoint credentials, queries, fragments, extra path prefixes, and trailing slashes are rejected. Invalid initialization throws. Await Bicho.close() before calling Bicho.init() again.

The exported TypeScript types are Level, CaptureOptions, Event, Options, and Client. Each capture method accepts an optional CaptureOptions object with level, tags, and contexts. Per-capture tags and contexts merge shallowly with configured values; matching keys replace the configured value. A named context object is replaced, not deeply merged. A per-capture level overrides the configured default. Capture options do not include per-event environment, release, or serviceName.

Filter with beforeSend

import * as Bicho from '@bicho-fyi/javascript';

Bicho.init({
  endpoint: 'https://your-bicho-worker.example/v1/logs',
  key: 'YOUR_PUBLIC_KEY',
  beforeSend(event) {
    if (event.message === 'ignore this message') return null;
    if (event.contexts) delete event.contexts.request;
    return event;
  },
});

Use this initialization instead of the earlier one, not alongside it. The hook receives a local event with event_id, a timestamp in seconds, platform, level, optional message or exception, optional environment, release, tags, and contexts, plus SDK metadata. An exception has { type, message, stacktrace? }, with stacktrace as a string.

Return the event or null to drop it. Throwing, returning a promise, or returning an invalid event drops the occurrence. The SDK does not await asynchronous hooks. Recursive capture calls from the same client’s hook are dropped.

The SDK filters the final event after the hook. It preserves its generated occurrence identity, platform, and SDK metadata, ignores unknown top-level fields, and validates the timestamp and level. Adding fields such as user, request, or debug_meta does not transmit them. Tags cannot override exception.type, exception.message, exception.stacktrace, log.record.uid, or bicho.contexts.

Delivery and limits

The default client and each isolated client expose these methods:

Method Return value Behavior
captureException(value, options?) string | undefined Queues an exception occurrence and starts delivery.
captureMessage(message, options?) string | undefined Queues a message occurrence and starts delivery.
flush(timeoutMs?) Promise<boolean> Waits for the queue to empty without stopping capture. Defaults to 2,000 ms.
close(timeoutMs?) Promise<boolean> Stops capture, removes this client’s listeners, and waits for pending delivery. Defaults to 2,000 ms.

These are named ESM exports, not a JavaScript default export. init() and createClient() are additional named exports. The standalone Bicho global exposes the same API.

Flush without stopping capture

const delivered = await Bicho.flush(5000);

true means the queue is empty and no queued occurrence has been permanently lost during that client’s lifetime. false means the wait timed out or a queued occurrence failed permanently. A timeout leaves sending active. Later successful sends do not clear the client’s remembered delivery failure.

Drops before queueing, such as a full queue or a hook returning null, return undefined but do not set that remembered failure flag. A successful flush therefore does not prove that every capture attempt was accepted. Before initialization, singleton capture returns undefined and singleton flush returns true.

Close and release the client

const delivered = await Bicho.close(5000);

Closing immediately rejects new captures and removes automatic listeners, then waits for queued work. At its deadline, it aborts the active request and discards the remaining queue. It returns the delivery status from that wait. Repeated closes on an isolated client share its closing promise. A closed isolated client cannot be reopened; create another client. The singleton can be initialized again after its close has completed.

Timeouts must be finite, nonnegative milliseconds. An invalid flush timeout resolves to false; passing one to close still shuts down the client and discards pending work. Neither method guarantees delivery during page unload or abrupt process termination.

Transport, queue, and retry limits

Each occurrence is one OTLP/HTTP JSON POST with Content-Type: application/json and X-Bicho-Key. The SDK does not expose custom headers, an ingest-secret option, a protobuf transport, or a custom transport callback.

Limit Behavior
Queue At most 30 occurrences per client, including the active request. Delivery is serial and FIFO. New captures are dropped when full.
Payload Serialized UTF-8 OTLP payloads over 60 KiB are dropped before queueing.
Request Times out after at most five seconds, or sooner when the occurrence expires.
Queue lifetime Each queued occurrence expires after 60 seconds.
Retries At most two retries after the initial attempt, preserving the occurrence UID and exact serialized payload.

Network failures and HTTP 429, 502, 503, and 504 can retry. The base retry delays are one and two seconds. Retry-After can extend them using seconds or an HTTP date; a longer delay is not shortened to fit the queue lifetime. A 429 also blocks new capture attempts until its delay has passed. Missing or invalid Retry-After on a 429 uses a 60-second fallback, which can exhaust the queued occurrence’s lifetime.

Other HTTP statuses, including 500, are terminal failures. HTTP 200 must contain a valid JSON export-response object to count as success. Invalid JSON, an error response, or a nonzero rejected-record count fails delivery without retry. Populated OTLP partial-success responses are never retried, including responses reporting rejected records. A partial-success response with zero rejected records counts as success.

There is no persistent offline queue or delivery guarantee. Rate limits, request failures, expiry, shutdown, and queue overflow can lose events. Collector failures are not captured as new errors by the SDK. See operations for collector-side quotas and deployment controls.

Runtime, security, and privacy

Modern browsers need Fetch, AbortController, Web Crypto, and TextEncoder. The SDK includes no polyfills. The Node package requires Node.js 24+. An ESM import is safe during server rendering because it does not start capture; initialize in the runtime where you intend to collect. These requirements do not establish compatibility with every edge runtime or framework.

Cross-origin JSON requests with X-Bicho-Key require CORS preflight, which the Bicho collector supports. With CSP, allow your script source in script-src and the collector origin in connect-src. Requests omit credentials and referrers, disable caching, and do not follow redirects to another collector URL. Fetch uses keepalive, but that does not make unload delivery reliable.

Public keys are ingest-only and visible in browser bundles. Never embed an admin token or backend ingest secret. Public keys do not prevent abuse or cap incoming request costs; configure deployment protections separately.

The SDK does not automatically collect cookies, storage, page URLs, DOM content, user identity, console output, or network activity. A captured stack can still contain a script URL. The collector receives the connection IP and browser-managed request headers even though the SDK does not add those to the event.

Filtering is best effort:

  • Sensitive tag and context keys, including common password, secret, token, authorization, cookie, API-key, email, phone, and address names, become [Filtered].
  • Captured strings lose HTTP(S) URL credentials, query strings, and fragments. Recognized Basic and Bearer credential strings are filtered; inline data and blob URLs become [inline].
  • Tags inspect at most 100 keys. Keys longer than 128 characters are skipped; string values are limited to 512 characters.
  • Context normalization limits depth to five, visits to 300 values, and keys per object or array to 50. Strings are limited to 2,048 characters and keys to 128. Cycles, accessors, and excessive data become markers rather than arbitrary object serialization.
  • Message text is limited to 16,384 characters, exception type to 512, and stack text to 32,768. The serialized payload cap still applies.

Avoid sending raw request bodies, authorization headers, or private data. A sensitive value under an unrecognized key can survive filtering. Review your own context and use beforeSend for application-specific rules. See privacy for collector-side handling.

Metadata and source maps

serviceName, release, and environment map to OTLP resource attributes service.name, service.version, and deployment.environment.name. Scalar tags become log attributes; normalized contexts become bounded JSON in bicho.contexts. SDK name and version are instrumentation-scope metadata. Project routing comes from the key, not from service labels.

The SDK sends exception attributes and sanitized raw stack text. Bicho derives recognized stack locations on the server. The SDK does not upload maps, inject or attach source-map debug IDs, or discover sourceMappingURL. A release label and generated stack coordinates alone do not enable source-map enrichment. The hook cannot add unsupported top-level debug metadata to work around this limitation. See source maps for the server’s matching requirements.

This SDK does not collect traces, sessions, page views, Web Vitals, breadcrumbs, or replay. For protocol details and exporter alternatives, see OTLP ingestion.

Type to search the documentation.