npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@miradorlabs/web-sdk

v2.0.0

Published

Mirador Client Side Web SDK

Readme

Mirador Web Client SDK

Web browser SDK for the Mirador tracing platform. This package provides a browser-compatible client using gRPC-Web to interact with the Mirador Ingest Gateway API.

Installation

npm install @miradorlabs/web-sdk

Features

  • Keep-Alive - Automatic periodic pings with in-flight guard, single retry, and auto-stop after 3 consecutive failures
  • Trace Lifecycle - Explicit close trace method with automatic cleanup and flush queue draining
  • Fluent Builder Pattern - Method chaining for building traces
  • Browser-optimized - Automatic client metadata collection (browser, OS, etc.)
  • Blockchain Integration - Built-in support for correlating traces with blockchain transactions
  • Stack Trace Capture - Automatic or manual capture of call stack for debugging
  • TypeScript Support - Full type definitions included
  • Strict Ordering - Flush calls maintain strict ordering even when async
  • Cross-SDK Trace Sharing - Resume traces across frontend and backend SDKs
  • Safe Multisig Tracking - Track Safe message and transaction confirmations via web3.safe.addMsgHint() and web3.safe.addTxHint()
  • EIP-1193 Provider Integration - Send transactions directly through traces with sendTransaction()
  • Configurable Logger - Pluggable Logger interface (defaults to no-op; enable with debug: true or provide custom logger)
  • Lifecycle Callbacks - TraceCallbacks for observing flush success/failure, close, and dropped items
  • Sampling - sampleRate (0–1) or custom sampler function; sampled-out traces return NoopTrace
  • Rate Limiting - Automatic 30s client-wide backoff on RESOURCE_EXHAUSTED (gRPC code 8)
  • Retry with Jitter - Full jitter backoff (random(0, base * 2^attempt)) on retryable gRPC errors
  • Queue Size Limits - Configurable maxQueueSize (default 4096) with onDropped callback
  • Flush Batch Splitting - Large flushes automatically split at 100 items with overflow re-scheduling
  • Trace Abandonment - After retry exhaustion, trace stops all API calls to prevent runaway retries
  • Max Trace Lifetime - Optional maxTraceLifetimeMs to auto-close long-running traces
  • Call Timeout - Per-call callTimeoutMs (default 5000ms) wrapping all gRPC operations

Quick Start (Default)

import { Client } from '@miradorlabs/web-sdk';

const client = new Client('your-api-key');

const trace = client.trace({ name: 'SwapExecution' })
  .addAttribute('from', '0xabc...')
  .addTags(['dex', 'swap'])
  .info('quote_received');
// → FlushTrace sent after 50ms of inactivity

trace.info('transaction_signed')
     .web3.evm.addTxHint('0xtxhash...', 'ethereum');
// → FlushTrace sent after 50ms of inactivity

// You can still call flush() explicitly to send immediately
trace.info('confirmed');
trace.flush();  // → FlushTrace sent immediately

Manual Flush Mode

import { Client } from '@miradorlabs/web-sdk';

const client = new Client('your-api-key');

const trace = client.trace({ name: 'SwapExecution', })
  .addAttribute('from', '0xabc...')
  .addTags(['dex', 'swap'])
  .info('quote_received');

trace.flush();  // → FlushTrace

trace.info('transaction_signed')
     .web3.evm.addTxHint('0xtxhash...', 'ethereum');

trace.flush();  // → FlushTrace

Keep-Alive & Trace Lifecycle

Automatic Keep-Alive

The SDK automatically sends keep-alive pings to the server every 10 seconds (configurable) to maintain trace liveness. This starts automatically after the first successful trace creation.

// Use default 10-second interval
const client = new Client('your-api-key');

// Or customize the interval
const client = new Client('your-api-key', {
  keepAliveIntervalMs: 15000  // Ping every 15 seconds
});

const trace = client.trace({ name: 'MyTrace' });
// Keep-alive starts automatically after first flush completes

Closing Traces

Always close traces when you're done to clean up resources and notify the server:

const trace = client.trace({ name: 'UserSession' });

// ... add events, attributes, etc ...

// Close when done
await trace.close('Session ended');

// All subsequent operations are ignored
trace.info('ignored');  // Logs warning, does nothing

Best Practices:

  • Always call close() when you're done with a trace
  • Use try-catch blocks to ensure traces are closed even on errors
  • Provide a meaningful reason to help with debugging
const trace = client.trace({ name: 'CheckoutFlow' });

try {
  // ... trace user checkout flow ...
  await trace.close('Checkout completed');
} catch (error) {
  trace.error('error', { message: error.message });
  await trace.close('Checkout failed');
}

Auto-Close on Page Visibility Change

For browser-based applications, you can enable automatic trace closing when the page becomes hidden:

const trace = client.trace({
  name: 'UserSession',
  autoClose: true  // Automatically close when page becomes hidden
});

// Trace will automatically close with reason "Page hidden" when:
// - User switches to a different tab
// - User minimizes the browser
// - User navigates to a different page
// - User closes the tab/window

Important Notes:

  • Auto-close uses the visibilitychange event on document
  • The trace will be closed with the reason "Page hidden"
  • You can still manually call close() before the page becomes hidden
  • The event listener is automatically cleaned up when you manually close the trace

API Reference

Client

The main client for interacting with the Mirador Ingest Gateway.

Constructor

new Client(apiKey: string, options?: ClientOptions)

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | apiKey | string | Yes | API key for authentication (sent as x-ingest-api-key header) | | options | ClientOptions | No | Configuration options |

Options

interface ClientOptions {
  apiUrl?: string;              // Gateway URL (defaults to https://ingest.mirador.org:443)
  keepAliveIntervalMs?: number; // Keep-alive ping interval in milliseconds (default: 10000)
  provider?: EIP1193Provider;   // EIP-1193 provider for transaction operations
  callTimeoutMs?: number;       // Per-call timeout for gRPC operations (default: 5000)
  debug?: boolean;              // Enable debug logging via console (default: false)
  logger?: Logger;              // Custom logger implementation (defaults to no-op)
  callbacks?: TraceCallbacks;   // Default lifecycle callbacks for all traces
  sampleRate?: number;          // Sample rate 0–1 (default: 1 = send all)
  sampler?: (options: TraceOptions) => boolean; // Custom sampler (overrides sampleRate)
}

Methods

trace(options?)

Creates a new trace builder.

const trace = client.trace({ name: 'MyTrace' });
const trace = client.trace({ name: 'MyTrace', });

// Stack trace capture is enabled by default - to disable:
const trace = client.trace({ name: 'MyTrace', captureStackTrace: false });

| Option | Type | Default | Description | |--------|------|---------|-------------| | name | string | undefined | Optional name of the trace | | traceId | string | auto-generated | Resume an existing trace by ID, or auto-generated W3C trace ID (32 hex chars) | | includeUserMeta | boolean | true | Include browser/OS metadata | | maxRetries | number | 2 | Maximum retry attempts on retryable gRPC errors | | retryBackoff | number | 500 | Base delay in ms for full jitter backoff | | autoClose | boolean | false | Automatically close trace on page visibility change | | provider | EIP1193Provider | undefined | EIP-1193 provider for transaction operations | | autoKeepAlive | boolean | true/false | Auto keep-alive (default: true for new, false when resuming) | | maxTraceLifetimeMs | number | 0 | Max trace lifetime in ms (0 = disabled). Auto-closes trace after this duration | | maxQueueSize | number | 4096 | Max pending items before dropping | | callbacks | TraceCallbacks | undefined | Per-trace lifecycle callbacks (overrides client-level) |

Note: A W3C-compatible trace ID (32 hex chars) is automatically generated when you call client.trace(). If you pass traceId, the trace resumes an existing trace instead.

Returns: Trace builder instance

Trace (Builder)

Fluent builder for constructing traces. All builder methods return this for chaining.

addAttribute(key, value)

Add a single attribute. Objects are automatically stringified.

trace.addAttribute('user', '0xabc...')
     .addAttribute('amount', 1.5)
     .addAttribute('config', { slippage: 50, deadline: 300 })  // stringified to JSON

addAttributes(attrs)

Add multiple attributes at once. Objects are automatically stringified.

trace.addAttributes({
  from: '0xabc...',
  to: '0xdef...',
  value: 1.0,
  metadata: { source: 'web', version: '1.0' }  // stringified to JSON
})

addTag(tag) / addTags(tags)

Add tags to categorize the trace.

trace.addTag('transaction')
     .addTags(['ethereum', 'send'])

info(name, details?, options?) / warn(...) / error(...)

Record an event with the corresponding severity level. All three share the same signature.

trace.info('wallet_connected', { wallet: 'MetaMask' })
     .info('transaction_initiated')
     .info('transaction_confirmed', { blockNumber: 12345 })

trace.warn('rate_limit', 'approaching limit')

trace.error('processing_failed', { code: 500 })

// With stack trace capture
trace.error('crash', { code: 500 }, { captureStackTrace: true })

| Parameter | Type | Description | |-----------|--------------------|--------------------------------------------------| | name | string | Event name | | details | string \| object | Optional event details (objects are stringified) | | options | object | Optional settings (e.g. { captureStackTrace: true }) |

addEvent(name, details?, options?)

Low-level event method. Prefer info(), warning(), error() for clarity.

trace.addEvent('custom_event', 'details', { severity: Severity.Warn })

// Legacy: timestamp can still be passed as third parameter for backward compatibility
trace.addEvent('custom_event', 'details', new Date())

addStackTrace(eventName?, additionalDetails?)

Capture and add the current stack trace as an event. Useful for debugging or tracking code paths.

trace.addStackTrace()  // Creates event named "stack_trace"
trace.addStackTrace('checkpoint', { stage: 'validation' })

| Parameter | Type | Description | |---------------------|----------|--------------------------------------------------| | eventName | string | Event name (defaults to "stack_trace") | | additionalDetails | object | Optional additional details to include |

addExistingStackTrace(stackTrace, eventName?, additionalDetails?)

Add a previously captured stack trace as an event. Useful when you need to capture a stack trace at one point but record it later.

import { captureStackTrace } from '@miradorlabs/web-sdk';

// Capture stack trace now
const stack = captureStackTrace();

// ... later ...
trace.addExistingStackTrace(stack, 'deferred_location', { reason: 'async operation' })

| Parameter | Type | Description | |---------------------|--------------|--------------------------------------------------| | stackTrace | StackTrace | Previously captured stack trace | | eventName | string | Event name (defaults to "stack_trace") | | additionalDetails | object | Optional additional details to include |

Web3Plugin Methods

The following methods are available when Web3Plugin is registered. They are accessed via the web3.evm and web3.safe namespaces on the trace.

web3.evm.addTxHint(txHash, chain, options?)

Add a transaction hash hint for blockchain correlation. Accepts Chain enum or chain name string.

import { Chain } from '@miradorlabs/web-sdk';

trace.web3.evm.addTxHint('0x123...', Chain.Ethereum, 'Main transaction');
trace.web3.evm.addTxHint('0x456...', 'polygon', 'Bridge transaction'); // string also works

| Parameter | Type | Description | |-----------|------|-------------| | txHash | string | Transaction hash | | chain | Chain \| ChainName | Chain enum value or name string | | options | string \| TxHintOptions | Optional details string, or options with input and details |

web3.safe.addMsgHint(msgHash, chain, details?)

Add a Safe message hint for tracking Safe multisig message confirmations.

trace.web3.safe.addMsgHint('0xmsgHash...', Chain.Ethereum);
trace.web3.safe.addMsgHint('0xotherHash...', Chain.Base, 'Token approval');
web3.safe.addTxHint(safeTxHash, chain, details?)

Add a Safe transaction hint for tracking Safe multisig transaction executions.

trace.web3.safe.addTxHint('0xsafeTxHash...', Chain.Ethereum);
trace.web3.safe.addTxHint('0xotherHash...', Chain.Base, 'Token transfer');
web3.evm.addTx(tx, chain?)

Add a transaction object, automatically extracting hash, chain, and input data.

const tx = await signer.sendTransaction({ to, data });
trace.web3.evm.addTx(tx, Chain.Ethereum);

// Chain inferred from tx.chainId if not provided
trace.web3.evm.addTx({ hash: txHash, data: calldata, chainId: 1 });
web3.evm.sendTransaction(tx, provider?)

Send a transaction through the trace's EIP-1193 provider, automatically capturing events (tx:send, tx:sent, tx:error), input data, and tx hint.

const client = new Client('key', { plugins: [Web3Plugin({ provider: window.ethereum })] });
const trace = client.trace({ name: 'Swap' });

const txHash = await trace.web3.evm.sendTransaction({
  from: '0xabc...',
  to: '0xRouterAddress...',
  data: '0x38ed1739...',
});
web3.evm.setProvider(provider)

Set an EIP-1193 provider for transaction operations. Automatically detects chain ID.

trace.web3.evm.setProvider(window.ethereum);
web3.evm.addInputData(inputData)

Add transaction input data (calldata) as a trace event.

trace.web3.evm.addInputData('0xa9059cbb000000000000000000000000...')

flush()

Flush pending data to the gateway. Fire-and-forget - returns immediately but maintains strict ordering.

Each flush sends FlushTrace (an idempotent create-or-update RPC).

trace.flush();

Returns: void

getTraceId()

Get the trace ID. Always available immediately since trace IDs are generated at client.trace() time.

const traceId = trace.getTraceId();  // string

Returns: string

close(reason?)

Close the trace and stop all timers (flush timer and keep-alive timer). After calling this method, all subsequent operations will be ignored.

await trace.close();
await trace.close('User completed workflow');

| Parameter | Type | Description | |-----------|------|-------------| | reason | string | Optional reason for closing the trace |

Returns: Promise<void>

Important: Once a trace is closed:

  • All method calls (addAttribute, addEvent, addTag, web3.evm.addTxHint, web3.safe.addMsgHint, web3.safe.addTxHint, flush) will be ignored with a warning
  • The keep-alive timer will be stopped
  • A close request will be sent to the server

isClosed()

Check if the trace has been closed.

const closed = trace.isClosed();  // boolean

Logger

By default, the SDK silences all log output. Enable logging with debug: true for console output, or provide a custom Logger:

// Debug mode — logs to console.debug/warn/error
const client = new Client('key', { debug: true });

// Custom logger
const client = new Client('key', {
  logger: {
    debug: (...args) => myLogger.debug(...args),
    warn:  (...args) => myLogger.warn(...args),
    error: (...args) => myLogger.error(...args),
  },
});

Lifecycle Callbacks (TraceCallbacks)

Observe trace lifecycle events programmatically:

const client = new Client('key', {
  callbacks: {
    onFlushed:    (traceId, itemCount) => console.log(`Flushed ${itemCount} items`),
    onFlushError: (error, operation)   => console.error(`${operation} failed:`, error),
    onClosed:     (traceId, reason)    => console.log(`Trace closed: ${reason}`),
    onDropped:    (count, reason)      => console.warn(`Dropped ${count} items: ${reason}`),
  },
});

// Per-trace overrides
const trace = client.trace({
  name: 'ImportantFlow',
  callbacks: { onFlushed: (id, n) => analytics.track('flush', { id, n }) },
});

Sampling

Control which traces are actually sent:

// Fixed sample rate — send 10% of traces
const client = new Client('key', { sampleRate: 0.1 });

// Custom sampler — full control
const client = new Client('key', {
  sampler: (options) => {
    // Always sample traces named "critical"
    if (options.name === 'critical') return true;
    return Math.random() < 0.1;
  },
});

When a trace is sampled out, client.trace() returns a NoopTrace — a zero-cost stub with the same API surface. All method calls are no-ops, and getTraceId() returns a sentinel value ('0'.repeat(32)).

import { NoopTrace } from '@miradorlabs/web-sdk';

const trace = client.trace({ name: 'MaybeSampled' });
if (trace instanceof NoopTrace) {
  // This trace was sampled out — no network calls will be made
}

Complete Example: Transaction Tracking

import { Client } from '@miradorlabs/web-sdk';

// Create client with custom keep-alive interval (optional)
const client = new Client('your-api-key', {
  keepAliveIntervalMs: 15000  // Override default 10s interval
});

async function handleWalletTransaction(userAddress: string, recipientAddress: string, amount: string) {
  const trace = client.trace({ name: 'SendETH' })
    .addAttribute('from', userAddress)
    .addAttribute('to', recipientAddress)
    .addAttribute('value', amount)
    .addTags(['transaction', 'send', 'ethereum'])
    .info('wallet_connected', { wallet: 'MetaMask' });
  // → FlushTrace sent automatically
  // → Keep-alive timer starts automatically

  trace.info('user_signed');

  try {
    const receipt = await sendTransaction();

    trace.info('transaction_sent', { txHash: receipt.hash })
         .web3.evm.addTxHint(receipt.hash, 'ethereum');
    // → FlushTrace sent automatically

    // Close the trace when done
    await trace.close('Transaction completed successfully');
  } catch (error) {
    trace.error('transaction_failed', { error: error.message });
    await trace.close('Transaction failed');
  }
}

Tracing Transaction Input Data with ethers.js

When a transaction fails on-chain, the input data (calldata) is still available and contains the encoded function call and parameters. Recording it with addTxInputData() lets you decode and debug the failure later in the Mirador dashboard.

import { Client } from '@miradorlabs/web-sdk';
import { BrowserProvider, parseEther } from 'ethers';

const client = new Client('your-api-key');

async function sendTracedTransaction() {
  const provider = new BrowserProvider(window.ethereum);
  const signer = await provider.getSigner();

  const trace = client.trace({ name: 'TokenTransfer' })
    .addAttribute('from', signer.address)
    .addTags(['transfer', 'ethereum']);

  try {
    const tx = await signer.sendTransaction({
      to: '0xRecipientAddress...',
      value: parseEther('0.1'),
      data: '0xa9059cbb000000000000000000000000...', // encoded ERC-20 transfer
    });

    trace.info('transaction_sent', { txHash: tx.hash })
         .web3.evm.addTxHint(tx.hash, 'ethereum')
         .web3.evm.addInputData(tx.data);  // record the calldata for debugging

    const receipt = await tx.wait();
    trace.info('transaction_confirmed', { blockNumber: receipt.blockNumber });
    await trace.close('Transfer completed');
  } catch (error) {
    // Even on failure, tx.data may be available from the error or the sent tx
    trace.error('transaction_failed', { error: error.message });
    await trace.close('Transfer failed');
  }
}

Cross-SDK Trace Sharing

You can share a trace ID between the web SDK and the Node.js SDK to create a unified trace that spans both frontend and backend. This is useful when a user action in the browser triggers server-side processing that should be correlated under the same trace.

Frontend → Backend

Pass the trace ID from the browser to your backend via an HTTP header. Trace IDs are available immediately since they're generated at client.trace() time:

import { Client } from '@miradorlabs/web-sdk';

const client = new Client('your-api-key');
const trace = client.trace({ name: 'Checkout' })
  .addAttribute('user', '0xabc...')
  .info('checkout_started');

// Trace ID is available immediately — no need to wait for flush
const traceId = trace.getTraceId();

// Pass trace ID to your backend
const response = await fetch('/api/process-order', {
  headers: { 'x-mirador-trace-id': traceId },
});

On the backend (Node.js SDK):

import { Client } from '@miradorlabs/nodejs-sdk';

const client = new Client('your-api-key');

app.post('/api/process-order', async (req, res) => {
  const traceId = req.headers['x-mirador-trace-id'];

  // Resume the trace — FlushTrace is idempotent, so this adds to the existing trace
  const trace = client.trace({ name: 'ProcessOrder', traceId })
    .addAttribute('step', 'backend')
    .info('order_processing');
  // → auto-flushed via FlushTrace

  // ...
  await trace.close('Order processed');
});

Backend → Frontend

Pass a trace ID from your backend to the browser and resume it:

// Backend creates the trace and returns the ID
const traceId = await getTraceIdFromBackend();

// Resume trace by passing traceId at creation time
const trace = client.trace({ name: 'Dashboard', traceId });

MiradorProvider

MiradorProvider is an EIP-1193 provider wrapper that automatically captures transaction data for Mirador traces. Wrap any existing provider to get automatic tracing for eth_sendTransaction and eth_sendRawTransaction calls.

import { Client, MiradorProvider } from '@miradorlabs/web-sdk';

const client = new Client('your-api-key');

// Option 1: Auto-create a new trace per transaction
const provider = new MiradorProvider(window.ethereum, client);

// Option 2: Bind to an existing trace
const trace = client.trace({ name: 'Swap' });
const provider = new MiradorProvider(window.ethereum, client, { trace });

// Option 3: Configure trace options for auto-created traces
const provider = new MiradorProvider(window.ethereum, client, {
  traceOptions: { name: 'WalletTx' }
});

// Use like any EIP-1193 provider — transactions are automatically traced
const txHash = await provider.request({
  method: 'eth_sendTransaction',
  params: [{ from: '0xabc...', to: '0xdef...', value: '0x0' }],
});

For each intercepted transaction, MiradorProvider:

  • Sets the underlying provider on the trace for chain detection
  • Captures tx:sent event with transaction hash on success
  • Captures tx:error event with error details on failure
  • Adds transaction hash hint and input data automatically

Chain Utilities

Chain Enum

Supported EVM chains, keyed by chain ID:

import { Chain } from '@miradorlabs/web-sdk';

Chain.Ethereum  // 1
Chain.Polygon   // 137
Chain.Arbitrum  // 42161
Chain.Base      // 8453
Chain.Optimism  // 10
Chain.BSC       // 56

All chain parameters accept ChainInput — either a Chain enum value or a chain name string ('ethereum', 'polygon', etc.).

toChain(chainId)

Convert a raw chain ID to a Chain enum value.

import { toChain, Chain } from '@miradorlabs/web-sdk';

toChain(1);     // Chain.Ethereum
toChain(137);   // Chain.Polygon
toChain(42161); // Chain.Arbitrum
toChain('0x1'); // Chain.Ethereum (hex string)
toChain(999);   // undefined

Automatic Client Metadata Collection

When includeUserMeta: true is set (default), the SDK automatically collects:

| Metadata | Description | |----------|-------------| | client.browser | Chrome, Firefox, Safari, Edge | | client.browserVersion | Browser version number | | client.os | Windows, macOS, Linux, Android, iOS | | client.osVersion | Operating system version | | client.deviceType | desktop, mobile, or tablet | | client.userAgent | Full user agent string | | client.language | Primary browser language | | client.languages | All preferred languages (comma-separated) | | client.screenWidth / client.screenHeight | Screen dimensions | | client.viewportWidth / client.viewportHeight | Viewport dimensions | | client.colorDepth | Screen color depth | | client.pixelRatio | Device pixel ratio (for retina displays) | | client.cpuCores | Number of CPU cores | | client.deviceMemory | Device memory in GB (if available) | | client.touchSupport | Whether touch is supported | | client.connectionType | Network connection type (4g, 3g, wifi, etc.) | | client.cookiesEnabled | Whether cookies are enabled | | client.online | Whether browser is online | | client.doNotTrack | Do Not Track preference | | client.timezone | User's timezone | | client.timezoneOffset | Timezone offset from UTC | | client.url | Current page URL | | client.origin | Page origin | | client.pathname | Page pathname | | client.referrer | Page referrer | | client.documentVisibility | Document visibility state |

Note: IP address is captured by the backend from request headers.

Metadata Utilities

The SDK exports the metadata collection functions for advanced usage:

import {
  getClientMetadata,
  detectBrowser,
  detectOS,
  detectDeviceType,
} from '@miradorlabs/web-sdk';

const metadata = getClientMetadata();  // Full ClientMetadata object
const browser = detectBrowser();       // e.g., { name: 'Chrome', version: '120.0' }
const os = detectOS();                 // e.g., { name: 'macOS', version: '14.0' }
const device = detectDeviceType();     // 'desktop' | 'mobile' | 'tablet'

Stack Trace Utilities

The SDK exports utilities for capturing and formatting stack traces:

import {
  captureStackTrace,
  formatStackTrace,
  formatStackTraceReadable
} from '@miradorlabs/web-sdk';

// Capture current stack trace
const stack = captureStackTrace();
// stack.frames: Array of { functionName, fileName, lineNumber, columnNumber }
// stack.raw: Original Error.stack string

// Format for storage (JSON string)
const json = formatStackTrace(stack);

// Format for display (human-readable)
const readable = formatStackTraceReadable(stack);
// Output:
//   at myFunction (/path/to/file.js:42:10)
//   at caller (/path/to/other.js:15:5)

TypeScript Support

Full TypeScript support with exported types:

import {
  // Classes
  Client,
  Trace,
  NoopTrace,
  MiradorProvider,

  // Utilities
  captureStackTrace,
  formatStackTrace,
  formatStackTraceReadable,
  toChain,
  Chain,
  getClientMetadata,
  detectBrowser,
  detectOS,
  detectDeviceType,

  // Types
  ClientOptions,
  TraceOptions,             // { name?, traceId?, includeUserMeta?, autoClose?, provider?, autoKeepAlive?, maxTraceLifetimeMs?, maxQueueSize?, callbacks?, ... }
  Severity,                 // Info, Warn, Error
  AddEventOptions,          // { captureStackTrace?: boolean, severity?: Severity } (for addEvent)
  StackFrame,               // { functionName, fileName, lineNumber, columnNumber }
  StackTrace,               // { frames: StackFrame[], raw: string }
  ChainName,                // 'ethereum' | 'polygon' | 'arbitrum' | 'base' | 'optimism' | 'bsc'
  ChainInput,               // Chain | ChainName
  TraceEvent,               // { eventName, details?, timestamp }
  TxHashHint,               // { txHash, chain, details?, timestamp }
  SafeTxHintData,           // { safeTxHash, chain, details?, timestamp }
  SafeMsgHintData,          // { messageHash, chain, details?, timestamp }
  ClientMetadata,           // Browser/OS metadata fields
  EIP1193Provider,          // { request(args): Promise<unknown> }
  TxHintOptions,            // { input?, details? }
  TransactionLike,          // { hash, data?, input?, chainId? }
  TransactionRequest,       // { from, to?, data?, value?, ... }
  MiradorProviderOptions,   // { trace?, traceOptions? }
  Logger,                   // { debug(), warn(), error() }
  TraceCallbacks,           // { onFlushed?, onFlushError?, onClosed?, onDropped? }
} from '@miradorlabs/web-sdk';

Browser Compatibility

This SDK uses modern browser APIs and is compatible with:

  • ES2020+
  • Fetch API
  • Promises
  • Modern browsers (Chrome, Firefox, Safari, Edge)

For older browsers, you may need polyfills.

Module Formats

The package provides multiple module formats:

  • ESM (dist/index.esm.js): For modern bundlers (Webpack, Vite, Rollup)
  • UMD (dist/index.umd.js): For browser globals and older module systems
  • TypeScript (dist/index.d.ts): Type definitions

Development

Building

npm run build

Testing

npm test
npm run test:watch
npm run test:coverage

Publishing

npm run release:patch  # 1.0.x
npm run release:minor  # 1.x.0
npm run release:major  # x.0.0

Example Application

A complete working example is available in the example/ directory. It demonstrates:

  • Wallet connection using EIP-6963 (Multi Injected Provider Discovery)
  • Creating and managing traces
  • Adding attributes, tags, and events
  • Blockchain transaction correlation with web3.evm.addTxHint()
  • Safe multisig tracking with web3.safe.addMsgHint() and web3.safe.addTxHint()
  • Network switching and balance display

To run the example:

cd example
npm install
npm run build
npm start

Then open http://localhost:8000 in your browser.

License

MIT

Author

@miradorlabs