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

@mirralabs/sdk

v0.7.0

Published

Run real third-party APIs in test sessions — Resend, Twilio, Stripe — with first-class assertions. Per-test isolation, no mocks.

Readme

@mirralabs/sdk

Run real third-party APIs in test sessions — Resend, Twilio, Stripe — with first-class assertions. Per-test isolation, no mocks.

import { test } from 'vitest';
import { MirraClient } from '@mirralabs/sdk';

const mirra = new MirraClient(); // reads MIRRA_API_KEY + MIRRA_API_URL

test('signup sends a welcome email', async () => {
  await using session = await mirra.sessions.create({
    projectId: process.env.MIRRA_PROJECT!,
    mirrors: ['resend'],
    mode: 'ephemeral',
  });
  await session.ready();

  process.env.RESEND_API_KEY = 'any';
  process.env.RESEND_BASE_URL = session.mirror('resend').url;

  await myApp.signup({ email: '[email protected]' });

  await session.mirror('resend').expect.email.sent({
    to: '[email protected]',
  });
});

Install

pnpm add @mirralabs/sdk

Requires Node 24 or later. Ships ESM + CJS dual output with bundled TypeScript declarations.

Configuration

Environment variables

| Variable | Purpose | Default | | --------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | | MIRRA_API_KEY | Project API key. Format mirra_live_<hex>.<hex>. Mint one in the dashboard under Project Settings → API keys. | — (required) | | MIRRA_API_URL | Control plane URL. Override for self-hosted dev. | http://localhost:4400 | | MIRRA_WEB_URL | Dashboard URL — used to build session.dashboardUrl. | https://app.mirra.run | | MIRRA_PROJECT | Convention used in examples. The SDK does NOT read this directly; pass projectId to sessions.create. | — |

Constructor options

import { MirraClient } from '@mirralabs/sdk';

const mirra = new MirraClient({
  apiUrl: 'https://api.mirra.run', // override env / default
  apiKey: process.env.MIRRA_API_KEY,
  timeoutMs: 30_000,               // per-request HTTP timeout
  userAgent: 'my-tests/1.0',       // appended to default UA
  webPublicUrl: 'https://app.mirra.run',
});

All options are optional. Env vars are read at new MirraClient() time if the corresponding option isn't passed.

Concepts

Sessions are isolation

A session is the unit of test isolation. Every session has its own database, its own webhooks, its own request history. Sessions running in parallel never see each other's state.

await using session = await mirra.sessions.create({ projectId, mirrors: ['resend'], mode: 'ephemeral' });
await session.ready();
// ... test body ...
// `await using` cancels the session on scope exit. No teardown code needed.

await using requires Node 20.18+ (or TypeScript 5.2+ with downlevel). If you can't use it, call await session.cancel() in afterEach.

Mirrors are vendor stand-ins

A mirror is a real HTTP service running real database state, real fixture data, and emitting real webhooks. Point your vendor SDK at session.mirror(slug).url and it works as if it were the real vendor — except it lives entirely inside your test session.

import { Resend } from 'resend';

const resend = new Resend('any-key', {
  baseUrl: session.mirror('resend').url,
});
await resend.emails.send({ from, to, subject, html });

Mirrors are not mocks. They have schemas, run migrations, persist between requests within a session, and fire webhooks back to your app.

Assertions are async and return matched data

session.mirror('resend').expect.email.sent({ to }) polls the session's traffic until a matching event lands, then returns the matched record so you can inspect it further.

const req = await session.mirror('resend').expect.email.sent({
  to: '[email protected]',
});
expect(req.requestBody).toMatchObject({ subject: 'Welcome!' });

Assertions throw MirraTimeoutError if the deadline (default 5s) passes without a match.

Sessions

mirra.sessions.*

mirra.sessions.create(input: CreateSessionInput): Promise<SessionHandle>
mirra.sessions.get(id: string): Promise<SessionHandle>
mirra.sessions.cancel(id: string): Promise<Session>
mirra.sessions.list(projectId, opts?): Paginator<Session>

CreateSessionInput:

{
  projectId: string;
  mirrors: string[];        // e.g. ['resend', 'twilio']
  mode: 'ephemeral' | 'persistent';
  name?: string;            // human label; null → auto-numbered sequence
  environmentId?: string;   // pin to a specific env; defaults to project's first
}

SessionHandle

session.id              // 'ses_01ARZ3...'
session.status          // 'provisioning' | 'ready' | 'active' | 'ended' | 'failed' | ...
session.mirrorInstances // attached mirrors
session.urls            // { resend: 'https://resend-abc.mirra.run', ... }
session.dashboardUrl    // safe to print to CI logs

await session.refresh();                // re-fetch state
await session.ready({ timeoutMs: 60_000 });
await session.cancel();
session.mirror('resend');               // → MirrorHandle, see below
session.listRequests({ method: 'POST' }); // → Paginator<RequestRow>
session.listWebhookDeliveries({ eventType: 'email.delivered' });

await session.waitFor(
  async () => /* predicate */,
  { timeoutMs: 5_000, pollMs: 200 },
);

await session[Symbol.asyncDispose]();   // implicit via `await using`

waitFor is the polling primitive every expect.* assertion is built on. Returns whatever the predicate returns truthy; throws MirraTimeoutError on deadline.

Mirror handles

const resend = session.mirror('resend');

resend.url           // point your vendor SDK here
resend.id            // mirror_instance row id ('mir_…')
resend.status        // 'ready' | 'provisioning' | 'failed' | 'destroyed'
resend.port          // mirror engine's internal port

await resend.state<ResendState>();        // raw state read
await resend.reset();                     // clear DB + re-run migrations
await resend.simulate('email.bounced', { /* payload */ });
const blob = await resend.snapshot();     // binary SQLite snapshot
await resend.restore(blob);

resend.expect.email.sent({ to: '[email protected]' });
// .expect is per-mirror — see "Resend assertions" below.

session.mirror<S>(slug) is typed via a registry. Built-in mirrors register themselves on import. Pass an unknown slug and .expect resolves to Record<string, never> so typos fail to compile.

Resend assertions

session.mirror('resend').expect.* exposes domain-level assertions for the Resend mirror. Each one polls the session's traffic and returns the matched record.

await resend.expect.email.sent(match, opts?)         // → RequestRow (POST /emails)
await resend.expect.email.delivered(match, opts?)    // → WebhookDelivery (email.delivered)
await resend.expect.email.bounced(match, opts?)      // → WebhookDelivery (email.bounced)
await resend.expect.email.complained(match, opts?)   // → WebhookDelivery (email.complained)

EmailMatch:

{
  to?: string | string[];
  from?: string;
  subject?: string;
  subjectIncludes?: string;
}

opts: { timeoutMs?: number; pollMs?: number; signal?: AbortSignal }. Defaults: timeoutMs: 5_000, pollMs: 200.

Worked example

import { test, expect } from 'vitest';
import { MirraClient } from '@mirralabs/sdk';
import { Resend } from 'resend';

const mirra = new MirraClient();

test('welcome email lands, then bounces if address is invalid', async () => {
  await using session = await mirra.sessions.create({
    projectId: process.env.MIRRA_PROJECT!,
    mirrors: ['resend'],
    mode: 'ephemeral',
  });
  await session.ready();
  const resend = session.mirror('resend');
  const client = new Resend('any', { baseUrl: resend.url });

  await client.emails.send({
    from: '[email protected]',
    to: '[email protected]',
    subject: 'Welcome!',
    html: '<p>hi</p>',
  });

  const req = await resend.expect.email.sent({ to: '[email protected]' });
  expect(req.requestBody).toMatchObject({ subject: 'Welcome!' });

  // Trigger a bounce webhook against the email we just sent
  await resend.simulate('email.bounced', { to: '[email protected]' });
  const bounce = await resend.expect.email.bounced({ to: '[email protected]' });
  expect(bounce.eventType).toBe('email.bounced');
});

.sent reads the session's requests traffic (the POST /emails the user's app made). .delivered, .bounced, .complained read the session's webhook deliveries (events the mirror fired back to the user's app).

Scenarios

A scenario is a markdown file describing what the system under test should do. The SDK runs it inside a session, and you assert against the result.

Scenario file format

# Welcome email

## Setup
A new user signs up with a valid email address.

## Expected Behavior
The app sends a welcome email immediately.

## Criteria
- email sent within 5 seconds of signup
- subject contains "Welcome"
- body addresses the user by name

## Config
mirrors: resend
runs: 1
judgeModel: claude-haiku-4-5
failBelow: 0.8

Each bullet under ## Criteria is a single criterion. Some are deterministic checks (the executor reduces them to assertions); others escalate to an LLM judge. The ## Config block tunes the run — runs > 1 runs the scenario multiple times for flake-resilience.

Running a scenario

session.runScenario(content: string, opts?): Promise<ScenarioHandle>
session.runScenarioFile(path: string, opts?): Promise<ScenarioHandle>

opts: { judgeModel?: string }.

import { test, expect } from 'vitest';
import { MirraClient } from '@mirralabs/sdk';

const mirra = new MirraClient();

test('signup matches the welcome scenario', async () => {
  await using session = await mirra.sessions.create({
    projectId: process.env.MIRRA_PROJECT!,
    mirrors: ['resend'],
    mode: 'ephemeral',
  });
  await session.ready();
  process.env.RESEND_BASE_URL = session.mirror('resend').url;

  await myApp.signup({ email: '[email protected]', name: 'Alice' });

  const scenario = await session.runScenarioFile('./scenarios/welcome.md');
  await scenario.expect.toPass();
});

ScenarioHandle

scenario.id                 // 'run_…' (single) or 'grp_…' (group)
scenario.isGroup            // true when scenario.runs > 1
scenario.satisfactionScore  // single's score, or group's mean
scenario.runs               // ScenarioRunResult[]
scenario.criteria           // unified per-criterion view
scenario.scenario           // the parsed scenario file
scenario.raw                // raw API response

await scenario.wait();      // resolve a multi-run group
await scenario.refresh();   // re-fetch group snapshot
await scenario.cancel();    // group only — single-run throws

For multi-run groups, raw getters (satisfactionScore, criteria, etc.) throw MirraNotReadyError until wait() (or any expect.* call) resolves the group.

Assertions

await scenario.expect.toPass(opts?)         // → ScenarioHandle
await scenario.expect.allRunsPass()         // → ScenarioHandle
const view = await scenario.expect.criterion(textOrIndex).toPass(opts?);
const view = await scenario.expect.criterion(textOrIndex).toFail(opts?);

toPass({ minSatisfaction? }) — passes when satisfaction is at or above the threshold. Threshold precedence: explicit opts.minSatisfaction → scenario file's failBelow → default 1.0 (every criterion must pass). For groups, compares the mean across runs; for single runs, compares the run's score.

allRunsPass() — strict mode. Every run must pass every criterion. Throws on the first per-criterion passRate < 1.0. Use when you don't want to tolerate any flake.

criterion(textOrIndex) — sync lookup by exact text or 0-based index. Throws ScenarioCriterionNotFound (with .available[] listing every criterion) on miss.

criterion().toPass({ minPassRate? }) — for groups, defaults minPassRate: 1.0 (every run passed). For single runs, passes when criteria[i].result === 'pass'.

criterion().toFail({ maxPassRate? }) — inverse. Defaults maxPassRate: 0 (no run passed it). Useful for negative-test scenarios.

Single-run vs group

When the scenario file has runs: 1, runScenario returns a ScenarioHandle wrapping a ScenarioRunResult — already resolved, raw fields work immediately.

When runs > 1, the API queues a group and the handle wraps a ScenarioRunGroupHandle. Raw fields throw until wait() (or expect.*) resolves the snapshot. The same expect.* API works for both — assertion code doesn't branch.

Errors

All SDK errors extend MirraError. Narrow with instanceof.

| Error | Fires when | What to do | | --------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | MirraAuthError | 401/403 — bad / missing API key, or key not authorized for the project | Mint a fresh key in the dashboard. | | MirraValidationError | 4xx (incl. 422 with errors: ParseError[] for scenario parse failures) | Read err.message. For parse failures, inspect err.errors. | | MirraServerError | 5xx | Retry with backoff or check status. | | MirraNetworkError | DNS / abort / connection failure | Check connectivity to MIRRA_API_URL. | | MirraNotReadyError | Session still provisioning past timeout, or scenario group not yet resolved | await session.ready({ timeoutMs }) or await scenario.wait(). | | MirraTimeoutError | waitFor deadline exceeded | Increase timeoutMs or check whether the expected event is firing. | | ScenarioAssertionError | scenario.expect.* failed | Inspect err.scenario, err.reason, err.details. | | ScenarioCriterionNotFound | scenario.expect.criterion(text\|index) lookup miss | Check err.available for the full list of criteria. |

Cheat sheet

import {
  MirraAuthError,
  MirraValidationError,
  MirraTimeoutError,
  ScenarioAssertionError,
  ScenarioCriterionNotFound,
} from '@mirralabs/sdk';

try {
  await session.runScenarioFile('./scenarios/welcome.md');
  await scenario.expect.criterion('email sent within 5 seconds of signup').toPass();
} catch (err) {
  if (err instanceof ScenarioCriterionNotFound) {
    console.error('Available criteria:', err.available);
    throw err;
  }
  if (err instanceof ScenarioAssertionError) {
    console.error('Failed:', err.reason, err.details);
    throw err;
  }
  if (err instanceof MirraTimeoutError) {
    console.error('Polled past deadline; nothing matched.');
    throw err;
  }
  if (err instanceof MirraAuthError) {
    console.error('Check MIRRA_API_KEY.');
    throw err;
  }
  if (err instanceof MirraValidationError) {
    if (err.errors.length > 0) {
      for (const e of err.errors) console.error(`${e.kind}: ${e.message}`);
    }
    throw err;
  }
  throw err;
}

Other resources

// Scenarios — parse, evaluate, async run-groups
mirra.scenarios.parse(content)
mirra.scenarios.evaluate({ sessionId, content, options? })
mirra.scenarios.runGroup({ projectId, content, sessions, totalRuns?, options? })
mirra.scenarios.group(groupId)         // rehydrate handle by id
mirra.scenarios.run(runId)             // fetch a single run

// Workspaces / catalog / keys
mirra.workspaces.me()                  // current user + workspaces
mirra.mirrors.list({ status? })        // catalog
mirra.mirrors.get(slug)
mirra.apiKeys.create(projectId, { name })
mirra.apiKeys.list(projectId)
mirra.apiKeys.revoke(id)

// Webhook traffic
mirra.webhookDeliveries.list(sessionId, { eventType?, finalStatus?, since?, until? })
mirra.webhookDeliveries.get(id)

// Device-flow auth (used by @mirralabs/cli; not for browser apps)
mirra.cliAuth.start({ clientName })
mirra.cliAuth.poll({ code, verifier })

Streaming

session.subscribe(handler, opts?) opens a Socket.io connection to the session's event stream.

const sub = session.subscribe(
  (event) => console.log(event.type, event.data),
  { prefix: 'email.' }, // optional event-type filter
);

// later
sub.close();

Use this for live CLI dashboards or long-running watchers. Don't use it in testsexpect.* assertions poll the same data and are deterministic, which is what tests want.

Requirements

  • Node 24 or later (per engines.node).
  • ESM and CJS both supported. TypeScript declarations bundled.
  • Browser is not supported in 0.2.0 — the SDK uses undici and node:fs.

Support

Email [email protected] for bugs, questions, and feature requests.

Dashboard: https://app.mirra.run.