# Explain It Simply — What Emulate Does

> Plain-language explanation of the problem emulate solves, the one analogy to hold in your head, and the three ideas you must not forget before reading anything else.

- Repository: vercel-labs/emulate
- GitHub: https://github.com/vercel-labs/emulate
- Human wiki: https://grok-wiki.com/public/wiki/vercel-labs-emulate-ddc6091d171d
- Complete Markdown: https://grok-wiki.com/public/wiki/vercel-labs-emulate-ddc6091d171d/llms-full.txt

## Source Files

- `README.md`
- `packages/emulate/src/commands/start.ts`
- `packages/emulate/package.json`
- `emulate.config.example.yaml`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:

- [README.md](README.md)
- [packages/emulate/src/commands/start.ts](packages/emulate/src/commands/start.ts)
- [packages/emulate/src/registry.ts](packages/emulate/src/registry.ts)
- [packages/emulate/package.json](packages/emulate/package.json)
- [emulate.config.example.yaml](emulate.config.example.yaml)
- [packages/@emulators/core/src/plugin.ts](packages/@emulators/core/src/plugin.ts)
- [packages/@emulators/core/src/store.ts](packages/@emulators/core/src/store.ts)
</details>

# Explain It Simply — What Emulate Does

This page gives you the plain-language mental model you need before diving into the codebase or the detailed API docs. It answers three questions: what problem does emulate exist to solve, what is the one analogy that unlocks how it works, and what are the three ideas you must carry with you before reading anything else.

If you have ever tried to write a test that calls GitHub, hit a rate limit, got the wrong user back, or discovered your CI pipeline has no network access at all — you have already felt the pain emulate was built to remove.

---

## The Problem It Solves

Your app calls real external APIs: GitHub to manage repositories, Google for OAuth and Gmail, Slack to post messages, AWS to read from S3, Stripe to process payments. Testing those flows is painful for three reasons.

1. **Real APIs are slow and flaky.** Network calls add seconds to every test run. Rate limits kick in. The remote service has downtime. Credentials expire.
2. **Real APIs are stateless from your test's point of view.** You cannot control which users exist, which repos are pre-created, or what emails are in the inbox. You have to set up and tear down data on the real service, which is fragile and expensive.
3. **CI environments and preview deployments often have no network access at all.** OAuth redirect URLs change with every Vercel preview deployment. You cannot register a GitHub OAuth app that points at a URL that does not exist yet.

Existing solutions fall into two categories: *mocks* (you write fake response objects by hand) and *record-replay* (you record real HTTP traffic and replay it). Both lie to your test. Mocks are frozen snapshots of what you believed the API returned. Replays become stale the moment the API changes shape.

Emulate takes a third path: **production-fidelity stateful emulation**. It runs a real HTTP server that speaks the same wire protocol as the real service — same endpoints, same request/response shapes, same auth token flow — but the state is entirely under your control.

Sources: [README.md:1-3](), [packages/emulate/package.json:4]()

---

## The One Analogy

Think of emulate as a **flight simulator** for external APIs.

A flight simulator is not a plane. It does not take you anywhere real. But from the perspective of the pilot's instruments — the throttle, the altimeter, the radio — it behaves exactly like a real plane. You can stall it, land it, crash it, and do it again a second later with a fresh runway. None of that is possible with a real aircraft in real airspace.

Emulate does the same thing for your application code. Your app points its HTTP client at `http://localhost:4001` instead of `https://api.github.com`. The emulator responds with GitHub-shaped JSON, issues real OAuth tokens, delivers webhook payloads to your app, and keeps everything in memory. Your app never knows it is not talking to the real GitHub.

The simulator analogy also explains what emulate is *not*: it is not a mock (a static recorded response), and it is not a proxy (a passthrough to the real service). It is an independent implementation of the same protocol.

---

## Three Ideas You Must Not Forget

### 1. Stateful, Not Frozen

Every emulated service maintains a live in-memory `Store` of typed `Collection<T>` instances. When your test creates a GitHub repo via `POST /user/repos`, that repo exists for all subsequent calls in the same session. When you merge a pull request, the branch protection rules are enforced, the PR state changes to `merged`, and linked issues may be closed — exactly as the real GitHub would behave. Nothing is a canned response.

The store can be reset between test runs by calling `emulator.reset()`, which wipes the collections and replays the original seed data. This gives you a clean slate for each test without restarting the process.

Sources: [packages/@emulators/core/src/store.ts:1-37](), [README.md:136-137]()

### 2. Seed-First Design

The starting state of every emulated service is defined by a *seed*: a YAML or JSON file (or inline TypeScript object) that describes the users, repos, OAuth clients, tokens, and other entities that should exist before your first request arrives. Emulate's CLI auto-detects `emulate.config.yaml` in the current directory, or you can pass `--seed config.yaml` explicitly.

```yaml
# emulate.config.example.yaml (lines 9-35)
github:
  users:
    - login: octocat
      name: The Octocat
  repos:
    - owner: octocat
      name: hello-world
      auto_init: true
tokens:
  gho_test_token_admin:
    login: admin
    scopes: [repo, user, admin:org, admin:repo_hook]
```

This design means tests are deterministic. The same seed produces the same starting state every time. You do not have to create test data programmatically in `beforeEach` hooks — declare it once and reset.

Sources: [emulate.config.example.yaml:1-35](), [packages/emulate/src/commands/start.ts:31-72]()

### 3. One Plugin Per Service, One Core

Each external service is its own package (`@emulators/github`, `@emulators/google`, etc.) that exports a `ServicePlugin`. A plugin has exactly one job: register HTTP routes on a shared Hono app and read/write state through the shared `Store`. The `@emulators/core` package owns the HTTP server, auth middleware, store implementation, webhook dispatcher, and persistence layer. Individual service packages contain only the route logic and seed helpers — they know nothing about the network.

```typescript
// packages/@emulators/core/src/plugin.ts
export interface ServicePlugin {
  name: string;
  register(app: Hono<AppEnv>, store: Store, webhooks: WebhookDispatcher, baseUrl: string, tokenMap?: TokenMap): void;
  seed?(store: Store, baseUrl: string): void;
}
```

This architecture means you can embed a single emulator inside a Next.js route handler, run several in parallel from the CLI, or instantiate them programmatically in a Vitest setup file. The same plugin code runs in all three contexts.

Sources: [packages/@emulators/core/src/plugin.ts:1-18](), [packages/emulate/src/registry.ts:3-7](), [README.md:929-946]()

---

## How the Parts Fit Together

```text
┌──────────────────────────────────────────────────────────────────┐
│  Your app / tests                                                │
│  (points HTTP client at http://localhost:4001, 4002, etc.)       │
└────────────┬─────────────────────────────────────────────────────┘
             │  HTTP  (same wire protocol as real service)
             ▼
┌──────────────────────────────────────────────────────────────────┐
│  emulate CLI  /  createEmulator()  /  Next.js adapter           │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │  @emulators/core                                          │   │
│  │  ┌──────────┐  ┌───────────────┐  ┌───────────────────┐ │   │
│  │  │  Store   │  │  Auth middle  │  │  Webhook dispatch │ │   │
│  │  │  (typed  │  │  (tokens +    │  │  (HTTP POST to    │ │   │
│  │  │  CRUD)   │  │   OAuth)      │  │   your server)    │ │   │
│  │  └──────────┘  └───────────────┘  └───────────────────┘ │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                  │
│  Service plugins (each is an independent package):              │
│  @emulators/github  @emulators/google  @emulators/slack         │
│  @emulators/vercel  @emulators/aws     @emulators/stripe  ...   │
└──────────────────────────────────────────────────────────────────┘
             │  optional
             ▼
┌────────────────────────────┐
│  portless (HTTPS proxy)    │
│  github.emulate.localhost  │
│  google.emulate.localhost  │
└────────────────────────────┘
```

Sources: [README.md:929-946](), [packages/emulate/src/commands/start.ts:162-192]()

---

## What Services Are Supported

The full list of services registered in the CLI is defined in `packages/emulate/src/registry.ts`:

| Service | Coverage |
|---|---|
| `github` | Users, repos, issues, PRs, reviews, branches, git data, orgs, webhooks, actions, checks, search |
| `google` | OAuth 2.0 / OIDC, Gmail, Calendar, Drive |
| `slack` | Web API, OAuth v2, channels, messages, files, pins, bookmarks, views, incoming webhooks |
| `vercel` | Projects, deployments, domains, env vars, teams, integrations |
| `apple` | Sign In with Apple (OIDC, RS256 ID tokens) |
| `microsoft` | Entra ID / Azure AD (OIDC, PKCE, Graph `/me`) |
| `okta` | OIDC / OAuth 2.0, management API, users, groups, authorization servers |
| `aws` | S3, SQS, IAM, STS |
| `stripe` | Customers, payment intents, checkout sessions, products, prices, webhooks |
| `resend` | Emails, domains, contacts, API keys |
| `mongoatlas` | Atlas Admin API, Atlas Data API |
| `clerk` | OIDC, users, organizations, sessions, invitations |

Sources: [packages/emulate/src/registry.ts:17-30]()

---

## How to Start (30-Second Version)

```bash
# Zero config: starts all services on ports 4000–4011
npx emulate

# Only what you need
npx emulate --service github,google

# With seed data
npx emulate --seed emulate.config.yaml
```

For tests, use the programmatic API so emulators start and stop with your test suite:

```typescript
import { createEmulator } from 'emulate'

const github = await createEmulator({ service: 'github', port: 4001 })
process.env.GITHUB_API_URL = github.url   // point your app here

// between tests:
github.reset()   // wipe state, replay seed

// when done:
await github.close()
```

Sources: [README.md:99-138](), [packages/emulate/src/commands/start.ts:79-99]()

---

## Closing Summary

Emulate is a local, fully stateful, in-process replacement for the external APIs your application depends on. Its plugin architecture separates the generic concerns (HTTP server, authentication, store, webhooks) in `@emulators/core` from the service-specific route logic in the individual `@emulators/*` packages. The seed-first design gives tests deterministic starting state, and `reset()` gives them a clean slate between runs — no mocks, no recorded traffic, no network required.

Sources: [packages/@emulators/core/src/plugin.ts:14-18](), [README.md:1-3]()
