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

msw-director

v0.1.0

Published

A flexible MSW wrapper for testing and development with runtime control and scenarios

Readme

msw-director

Stop hardcoding mocks. Control your API state dynamically in the browser and tests.

A flexible wrapper for Mock Service Worker (MSW) designed to solve the "Mock Management Hell".

The Problem

Standard mocking is great until you need to test edge cases.

  • How do I test a 500 error without changing the code?
  • How do I switch the user to "Suspended" state for a demo?
  • How do I run parallel tests with different mock states?

The Solution

msw-director separates the Mock Definition from the Mock State.

  1. Define all possible responses (Success, Error, Loading) once.
  2. Switch between them at runtime using the Console or Test Controller.

Features

  • 🛠 Fluent Handler Builder: Define variants cleanly (.add(200, ...).add(500, ...)).
  • 🎮 Runtime Control: Switch states instantly via window.mswController in the browser.
  • 🧪 Test Isolation: Thread-safe controllers for parallel Jest/Vitest tests.
  • 📦 Scenarios: Group complex app states (e.g., "Maintenance Mode") into presets.
  • 🍪 SSR Support: Persist mock state via cookies for Server-Side Rendering.

When to use (and when NOT to)

| ✅ Use this if... | ❌ Do NOT use if... | | :--- | :--- | | You need to manually test edge cases (QA/Demo). | You need a full-blown Backend-for-Frontend (BFF). | | You have flaky tests due to shared mock state. | You are mocking WebSockets (not supported yet). | | You want to simulate network delays dynamically. | You want to use this in Production code. |


Installation

First, ensure msw is installed.

yarn add -D msw msw-director
# or
npm install --save-dev msw msw-director

Quick Start

1. Define Your Handlers

// src/mocks/handlers.ts
import { HandlerBuilder, corsHandler } from 'msw-director';
import { HttpResponse } from 'msw';

export const handlers = [
  corsHandler,

  HandlerBuilder.get('/api/user', 'getUser')
    .add(200, () => HttpResponse.json({ name: 'John Doe' }))
    .add(404, () => new HttpResponse(null, { status: 404 }))
    .add('suspended', () => HttpResponse.json({ status: 'suspended' }))
    .withDelay(300),
];

2. Initialize in Browser

// src/main.tsx
import { initBrowserMocks } from 'msw-director';
import { handlers } from './mocks/handlers';

if (process.env.NODE_ENV === 'development') {
  initBrowserMocks({ handlers });
}

3. Control it!

Open your browser console:

// Switch to 404 error
window.mswController.setHandler('getUser', 404);

// Switch to custom state
window.mswController.setHandler('getUser', 'suspended');

// Reset to default
window.mswController.clearHandler('getUser');

Advanced Usage: Scenarios

Scenarios allow you to activate a group of handlers at once.

Defining Scenarios

// src/mocks/scenarios.ts
import type { TScenarios } from 'msw-director';

export const scenarios: TScenarios = {
  'auth-error': {
    type: 'auth', // Mutually exclusive group
    handlers: {
      getUser: 401,
      getSettings: 401,
    },
  },
  'maintenance-mode': {
    type: 'global',
    handlers: {
      getUser: 503,
      getSettings: 503,
    },
  },
};

Activating Scenarios

You can activate scenarios programmatically (e.g., in tests) or via the console.

In Browser Console:

window.mswController.setScenario('auth-error');

In Tests:

it('should show maintenance screen', async () => {
  controller.setScenario('maintenance-mode');
  render(<App />);
  await screen.findByText('Under Maintenance');
});

Global Configuration

You can configure global defaults during initialization.

initBrowserMocks({
  handlers,
  scenarios,
  defaultDelay: 500, // Global delay for all handlers (unless overridden)
  storeApi: getCookieStorageApi(), // Use cookies instead of sessionStorage
});

Usage in Tests (Jest/Vitest)

We provide a factory pattern to ensure Test Isolation.

// jest.setup.ts
import { initNodeMocks, RequestSpy } from 'msw-director';
import { handlers, scenarios } from './src/mocks';

export const requestSpy = new RequestSpy();

// Returns a unique controller for this test suite/file
export const { server, controller } = initNodeMocks({
  handlers,
  scenarios, // Don't forget to pass scenarios here!
  loggers: [requestSpy.log],
});

beforeAll(() => server.listen());
afterEach(() => {
  server.resetHandlers();
  requestSpy.clear();
  controller.clearAllHandlers(); // Reset state
  controller.clearAllScenarios(); // Reset active scenarios
});
afterAll(() => server.close());

Writing Tests

import { controller, requestSpy } from './jest.setup';

describe('UserProfile', () => {
  it('should display an error on 500', async () => {
    // Set the handler to return a 500 error
    controller.setHandler('getUser', 500);

    render(<UserProfile />);
    await screen.findByText('Something went wrong');
  });

  it('should handle auth scenario', async () => {
    // Activate a complex scenario
    controller.setScenario('auth-error');
    
    render(<UserProfile />);
    await screen.findByText('Please login');
  });
});

Common Pitfalls (Anti-Patterns)

❌ Don't use window in Tests

Bad: Trying to access window.mswController in Jest/Node environment. Good: Use the controller instance returned by initNodeMocks.

❌ Don't put logic in Resolvers

Bad: Writing complex if/else business logic inside .add(...). Good: Mocks should be dumb. If you need logic, create a separate handler variant.

❌ Don't forget to initialize

Bad: Using HandlerBuilder without calling initBrowserMocks or initNodeMocks. Result: The handler will warn in the console and fallback to passthrough.


API Reference

HandlerBuilder

  • .get(path, id): Create a GET handler.
  • .add(key, resolver): Add a response variant.
  • .withDelay(ms): Set default delay.
  • .skipDefaultError(): Disable auto-4xx generation.

MockController

  • .setHandler(id, key): Override a specific handler.
  • .setScenario(name): Activate a scenario.
  • .resolve(ctx): Internal method for state resolution.

Legal & Security

Data Privacy

This library runs entirely on the client-side (Browser or Node.js process). It does not collect telemetry or send data to external servers.

Trademarks

  • MSW is a trademark of its respective owners. This project is not affiliated with MSW.
  • React, Jest, and Vitest are trademarks of their respective owners.

Disclaimer

The software is provided "AS IS", without warranty of any kind, express or implied. The authors are not liable for any claim, damages, or other liability arising from the use of this software.

License

MIT