# Platform & Infrastructure Services — GitHub, Vercel, Slack, AWS, Stripe, Resend, MongoDB Atlas

> The seven emulators covering developer platforms and cloud infrastructure: what REST surface area each one reproduces, how webhooks are dispatched in-process, and what the examples directory shows about wiring them into a Next.js app.

- 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

- `packages/@emulators/github/src`
- `packages/@emulators/vercel/src`
- `packages/@emulators/slack/src`
- `packages/@emulators/aws/src`
- `packages/@emulators/stripe/src`
- `packages/@emulators/resend/src`
- `packages/@emulators/mongoatlas/src`
- `examples/stripe-checkout/README.md`
- `examples/oauth/README.md`

---

> ⚠️ The agent returned an invalid wiki page. This page needs recovery.
>
> First failure: the page included too few precise line citations; include at least 3 citations like Sources: [path/to/file.ts:12-40]() outside the opening source list
> Retry failure: the page did not include the required "# Platform & Infrastructure Services — GitHub, Vercel, Slack, AWS, Stripe, Resend, MongoDB Atlas" heading near the top

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

- [packages/@emulators/github/src/index.ts](packages/@emulators/github/src/index.ts)
- [packages/@emulators/github/src/routes/issues.ts](packages/@emulators/github/src/routes/issues.ts)
- [packages/@emulators/github/src/routes/webhooks.ts](packages/@emulators/github/src/routes/webhooks.ts)
- [packages/@emulators/github/src/routes/actions.ts](packages/@emulators/github/src/routes/actions.ts)
- [packages/@emulators/vercel/src/index.ts](packages/@emulators/vercel/src/index.ts)
- [packages/@emulators/vercel/src/routes/deployments.ts](packages/@emulators/vercel/src/routes/deployments.ts)
- [packages/@emulators/slack/src/index.ts](packages/@emulators/slack/src/index.ts)
- [packages/@emulators/slack/src/routes/chat.ts](packages/@emulators/slack/src/routes/chat.ts)
- [packages/@emulators/slack/src/routes/webhooks.ts](packages/@emulators/slack/src/routes/webhooks.ts)
- [packages/@emulators/aws/src/index.ts](packages/@emulators/aws/src/index.ts)
- [packages/@emulators/aws/src/routes/s3.ts](packages/@emulators/aws/src/routes/s3.ts)
- [packages/@emulators/aws/src/routes/sqs.ts](packages/@emulators/aws/src/routes/sqs.ts)
- [packages/@emulators/stripe/src/index.ts](packages/@emulators/stripe/src/index.ts)
- [packages/@emulators/stripe/src/routes/checkout-sessions.ts](packages/@emulators/stripe/src/routes/checkout-sessions.ts)
- [packages/@emulators/stripe/src/routes/payment-intents.ts](packages/@emulators/stripe/src/routes/payment-intents.ts)
- [packages/@emulators/resend/src/index.ts](packages/@emulators/resend/src/index.ts)
- [packages/@emulators/resend/src/routes/emails.ts](packages/@emulators/resend/src/routes/emails.ts)
- [packages/@emulators/resend/src/routes/inbox.ts](packages/@emulators/resend/src/routes/inbox.ts)
- [packages/@emulators/mongoatlas/src/index.ts](packages/@emulators/mongoatlas/src/index.ts)
- [packages/@emulators/mongoatlas/src/routes/admin.ts](packages/@emulators/mongoatlas/src/routes/admin.ts)
- [packages/@emulators/mongoatlas/src/routes/data-api.ts](packages/@emulators/mongoatlas/src/routes/data-api.ts)
- [packages/@emulators/core/src/webhooks.ts](packages/@emulators/core/src/webhooks.ts)
- [examples/stripe-checkout/README.md](examples/stripe-checkout/README.md)
- [examples/oauth/README.md](examples/oauth/README.md)
- [examples/oauth/src/lib/providers.ts](examples/oauth/src/lib/providers.ts)
- [examples/nextjs-embedded/README.md](examples/nextjs-embedded/README.md)
</details>

# Platform & Infrastructure Services — GitHub, Vercel, Slack, AWS, Stripe, Resend, MongoDB Atlas

This page covers the seven platform and infrastructure emulators that ship with `emulate`: **GitHub**, **Vercel**, **Slack**, **AWS** (S3, SQS, IAM), **Stripe**, **Resend**, and **MongoDB Atlas**. Each one is a self-contained Hono plugin that registers REST routes, seeds a typed in-memory store, and participates in the shared webhook dispatch system — so your test suite (or a Next.js preview deployment) can talk to realistic local counterparts of these services with no real accounts, network calls, or money involved.

Understanding how these emulators fit together matters because they all share the same `WebhookDispatcher`, `Store`, and `RouteContext` primitives from `@emulators/core`. That means a single action — creating a Stripe PaymentIntent, pushing a commit event through GitHub, or sending an email through Resend — can fire an in-process webhook delivery to your app's handler in the same test process, without any external tunnel or queue.

---

## Architecture overview

Every emulator is a `ServicePlugin`:

```ts
// packages/@emulators/core/src/plugin.ts (interface shape)
export interface ServicePlugin {
  name: string;
  register(app, store, webhooks, baseUrl, tokenMap?): void;
  seed(store, baseUrl): void;
}
```

`register` mounts all the HTTP routes; `seed` populates the store with sensible defaults so the emulator is usable immediately. The `webhooks` argument is the shared `WebhookDispatcher` from `@emulators/core`.

```text
┌─────────────────────────────────────────────────────────┐
│                    Hono HTTP server                      │
│  ┌────────────┐  ┌─────────┐  ┌──────┐  ┌───────────┐  │
│  │  github/*  │  │vercel/* │  │slack/│  │  stripe/* │  │
│  └────────────┘  └─────────┘  └──────┘  └───────────┘  │
│  ┌─────────┐  ┌──────────┐  ┌────────────────────────┐  │
│  │  aws/*  │  │ resend/* │  │   mongoatlas/*         │  │
│  └─────────┘  └──────────┘  └────────────────────────┘  │
│                    ↓ shared                              │
│          Store   WebhookDispatcher                       │
└─────────────────────────────────────────────────────────┘
```

---

## GitHub emulator

### REST surface area

The GitHub emulator is the most feature-rich of the seven. It registers route modules for:

| Module | Key endpoints |
|--------|--------------|
| `usersRoutes` | `GET /user`, `GET /users/:login` |
| `reposRoutes` | CRUD for repos, forks, collaborators, git objects |
| `issuesRoutes` | CRUD for issues, state transitions, assignees |
| `pullsRoutes` | PR creation, listing, merging |
| `commentsRoutes` | Issue and PR comments |
| `reviewsRoutes` | PR reviews and review comments |
| `labelsAndMilestonesRoutes` | Labels and milestones |
| `branchesAndGitRoutes` | Branches, refs, commits, trees, blobs |
| `orgsAndTeamsRoutes` | Orgs, teams, memberships |
| `releasesRoutes` | Releases and release assets |
| `searchRoutes` | Code, issues, PR search |
| `actionsRoutes` | Workflow runs, jobs, secrets, artifacts |
| `checksRoutes` | Check runs and check suites |
| `rateLimitRoutes` | `GET /rate_limit` |
| `metaRoutes` | `GET /meta`, `GET /octocat` |
| `oauthRoutes` | OAuth device flow, web flow |
| `appsRoutes` | GitHub Apps, installation tokens |
| `webhooksRoutes` | Repo and org webhook CRUD |

Sources: [packages/@emulators/github/src/index.ts:8-26](packages/@emulators/github/src/index.ts)

### GitHub Apps and webhook enrichment

The GitHub emulator goes further than a plain REST stub: it integrates GitHub App installation semantics into the webhook pipeline. When `githubPlugin.register` runs, it **wraps** the shared `WebhookDispatcher.dispatch` method. Before forwarding an event to registered subscribers, it:

1. Finds all active app installations that match the event's owner/repo scope and subscribe to that event type.
2. Enriches the payload with an `installation` object (`{ id, node_id }`).
3. Calls the original dispatcher (in-process subscribers).
4. Separately POSTs to any `webhook_url` configured on the GitHub App, signing the body with HMAC-SHA256 if a `webhook_secret` is set.

```ts
// packages/@emulators/github/src/index.ts:460-475
webhooks.dispatch = async (event, action, payload, owner, repo) => {
  const installations = findInstallationsForRepo(gh, owner, repo, event);
  const enrichedPayload =
    installations.length > 0 ? enrichPayloadWithInstallation(payload, installations[0]) : payload;

  await originalDispatch(event, action, enrichedPayload, owner, repo);
  await deliverToAppWebhookUrls(gh, event, action, payload, owner, repo);
};
```

Sources: [packages/@emulators/github/src/index.ts:460-475](packages/@emulators/github/src/index.ts), [packages/@emulators/github/src/index.ts:415-453](packages/@emulators/github/src/index.ts)

### Seed config

`GitHubSeedConfig` supports seeding users, orgs, repos (with auto-init commits and branches), OAuth apps, and full GitHub App definitions including multiple installations with per-installation repository selection and event subscriptions.

Sources: [packages/@emulators/github/src/index.ts:30-85](packages/@emulators/github/src/index.ts)

---

## Vercel emulator

### REST surface area

The Vercel emulator covers the core Vercel REST API used by integrations and CI tooling:

| Module | Key endpoints |
|--------|--------------|
| `oauthRoutes` | OAuth authorization, token exchange, userinfo |
| `userRoutes` | `GET /v2/user`, update user |
| `projectsRoutes` | Project CRUD, list by team/user |
| `deploymentsRoutes` | Create, list, get, cancel, delete deployments; deployment events; aliases |
| `domainsRoutes` | Domain registration and lookup |
| `envRoutes` | Environment variable CRUD per project |
| `apiKeysRoutes` | API key creation and revocation |

Sources: [packages/@emulators/vercel/src/index.ts:6-12](packages/@emulators/vercel/src/index.ts)

The deployment module implements cursor-based pagination (Vercel's `until`/`since` cursor style) and synthesizes deployment hostnames from the project name and a UID slice:

```ts
// packages/@emulators/vercel/src/routes/deployments.ts:57-59
function deploymentHostname(name, uid, baseUrl) {
  const slug = `${name}-${uid.slice(4, 12)}`;
  return `${slug}.${primaryHostFromBaseUrl(baseUrl)}`;
}
```

Sources: [packages/@emulators/vercel/src/routes/deployments.ts:57-64](packages/@emulators/vercel/src/routes/deployments.ts)

### Seed config

`VercelSeedConfig` covers users, teams (with automatic team membership for all seeded users), projects (with framework settings, build commands, and env vars), and OAuth app registrations (Vercel integrations).

Sources: [packages/@emulators/vercel/src/index.ts:17-50](packages/@emulators/vercel/src/index.ts)

---

## Slack emulator

### REST surface area

The Slack emulator covers the Web API endpoints that real Slack bots and integrations rely on:

| Module | Key endpoints |
|--------|--------------|
| `authRoutes` | `auth.test`, `auth.revoke` |
| `chatRoutes` | `chat.postMessage`, `chat.update`, `chat.delete`, `chat.scheduleMessage`, `chat.getPermalink` |
| `conversationsRoutes` | `conversations.list`, `.info`, `.create`, `.join`, `.invite`, `.archive`, `.members`, `.history`, `.replies` |
| `usersRoutes` | `users.list`, `.info`, `.profile.get`, `.profile.set`, `.getPresence` |
| `reactionsRoutes` | `reactions.add`, `.remove`, `.list`, `.get` |
| `teamRoutes` | `team.info` |
| `oauthRoutes` | `oauth.v2.access` |
| `webhookRoutes` | Incoming webhooks via `POST /services/:teamId/:botId/:token` |
| `filesRoutes` | File listing (stub) |
| `pinsRoutes` | `pins.add`, `.remove`, `.list` |
| `bookmarksRoutes` | Bookmark CRUD |
| `viewsRoutes` | Modal view push/update/open |
| `inspectorRoutes` | Dev UI for inspecting emulator state |

Sources: [packages/@emulators/slack/src/index.ts:6-18](packages/@emulators/slack/src/index.ts)

### Incoming webhooks

The Slack emulator supports Slack's Incoming Webhook pattern. `POST /services/:teamId/:botId/:token` accepts both `application/json` and `application/x-www-form-urlencoded` (with a `payload` field), resolves the target channel, and stores the message in the in-memory channel history — the same store that `chat.postMessage` writes to. The webhook token is matched against the `incomingWebhooks` table seeded at startup.

Sources: [packages/@emulators/slack/src/routes/webhooks.ts:14-60](packages/@emulators/slack/src/routes/webhooks.ts)

### Token authentication

Slack's token format differs from the Bearer-token convention used by most other emulators. The Slack plugin installs a middleware that reads `Authorization: Bearer xoxb-...` (or `Authorization: token xoxb-...`) before every request, looks up the token in the store, and injects `authToken`, `authScopes`, and `authUser` into the Hono context:

```ts
// packages/@emulators/slack/src/index.ts:402-416
function applySlackTokenAuth(c, store) {
  const token = slackRequestToken(c);
  if (!token) return;
  const record = getSlackStore(store).tokens.findOneBy("token", token);
  if (!record) return;
  c.set("authToken", record.token);
  c.set("authScopes", record.scopes);
  c.set("authUser", { login: record.user_id, ... });
}
```

The `strict_scopes` seed option controls whether missing scopes return a `missing_scope` error or are silently allowed — useful for loosening validation during initial development.

Sources: [packages/@emulators/slack/src/index.ts:361-390](packages/@emulators/slack/src/index.ts), [packages/@emulators/slack/src/index.ts:402-423](packages/@emulators/slack/src/index.ts)

---

## AWS emulator

### REST surface area

The AWS emulator covers three services: **S3**, **SQS**, and **IAM**. All three use the same in-process store.

#### S3

Routes follow the AWS S3 REST API shape (path-style). The module handles:

- `GET /` — list all buckets
- `PUT /:bucket` — create bucket
- `DELETE /:bucket` — delete bucket
- `GET /:bucket` — list objects in bucket
- `PUT /:bucket/:key` — upload object
- `GET /:bucket/:key` — download object
- `DELETE /:bucket/:key` — delete object
- `HEAD /:bucket/:key` — object metadata

Responses are XML, matching AWS's actual wire format. Object content is stored in-memory as `Buffer`.

Sources: [packages/@emulators/aws/src/routes/s3.ts:13-61](packages/@emulators/aws/src/routes/s3.ts)

#### SQS

SQS uses the AWS query-protocol format: `POST /sqs/` with an `Action` parameter. Supported actions:

| Action | Description |
|--------|-------------|
| `CreateQueue` | Create a new queue (FIFO-aware) |
| `DeleteQueue` | Delete queue and its messages |
| `ListQueues` | List all queues, optional prefix filter |
| `GetQueueUrl` | Resolve queue name to URL |
| `GetQueueAttributes` | Return queue metadata |
| `SendMessage` | Enqueue a message, returns MD5 and MessageId |
| `ReceiveMessage` | Dequeue up to N messages with visibility timeout |
| `DeleteMessage` | Acknowledge and remove by receipt handle |
| `PurgeQueue` | Delete all messages |

Sources: [packages/@emulators/aws/src/routes/sqs.ts:21-47](packages/@emulators/aws/src/routes/sqs.ts)

#### IAM

The IAM module emulates user and role management for tests that need to create access keys or assume roles without hitting real AWS:

- User CRUD with embedded access key list
- Role CRUD with `AssumeRolePolicyDocument`

Sources: [packages/@emulators/aws/src/index.ts:12-13](packages/@emulators/aws/src/index.ts)

### Seed config

`AwsSeedConfig` covers region, account ID override, S3 bucket list, SQS queue list (with FIFO support), IAM users (optional `create_access_key`), and IAM roles. The seed defaults insert one bucket (`emulate-default`), one queue (`emulate-default-queue`), and one IAM user (`admin`) with well-known example credentials.

Sources: [packages/@emulators/aws/src/index.ts:13-88](packages/@emulators/aws/src/index.ts)

---

## Stripe emulator

### REST surface area

The Stripe emulator speaks the Stripe API's `application/x-www-form-urlencoded` body format (`parseStripeBody`) and returns JSON shaped exactly like the Stripe SDK expects.

| Module | Key endpoints |
|--------|--------------|
| `customerRoutes` | `POST/GET/POST/DELETE /v1/customers`, `GET /v1/customers/:id` |
| `paymentMethodRoutes` | Create, attach, detach, list payment methods |
| `paymentIntentRoutes` | Create, confirm, capture, cancel payment intents |
| `chargeRoutes` | List and retrieve charges |
| `productRoutes` | Product CRUD |
| `priceRoutes` | Price CRUD (recurring and one-time) |
| `checkoutSessionRoutes` | Create sessions, host checkout page, expire |
| `customerSessionRoutes` | Customer portal sessions |

Sources: [packages/@emulators/stripe/src/index.ts:5-13](packages/@emulators/stripe/src/index.ts)

### Webhooks

The Stripe emulator fires events through `WebhookDispatcher` on mutations. For example, creating a PaymentIntent immediately dispatches a `payment_intent.created` event:

```ts
// packages/@emulators/stripe/src/routes/payment-intents.ts:54-59
await webhooks.dispatch(
  "payment_intent.created",
  undefined,
  { type: "payment_intent.created", data: { object: formatPaymentIntent(pi) } },
  "stripe",
);
```

Similarly, when the emulator's hosted checkout page is submitted, it dispatches `checkout.session.completed` with the full session object.

Webhook endpoints can be registered in `StripeSeedConfig.webhooks` — each entry specifies a `url`, `events` array, and optional `secret`. Registration calls `webhooks.register(...)` on the shared dispatcher.

Sources: [packages/@emulators/stripe/src/routes/payment-intents.ts:54-60](packages/@emulators/stripe/src/routes/payment-intents.ts), [packages/@emulators/stripe/src/index.ts:104-114](packages/@emulators/stripe/src/index.ts)

### Checkout page

`checkoutSessionRoutes` also serves an HTML checkout page at `GET /checkout/:sessionId`. This is a rendered HTML form (using `renderCheckoutPage` from `@emulators/core`) that shows line items and a "Pay and Complete" button — enough to drive end-to-end browser tests without a real Stripe.js integration.

Sources: [packages/@emulators/stripe/src/routes/checkout-sessions.ts:1-25](packages/@emulators/stripe/src/routes/checkout-sessions.ts)

---

## Resend emulator

### REST surface area

The Resend emulator covers the email sending, domain management, audience, and contact surfaces:

| Module | Key endpoints |
|--------|--------------|
| `emailRoutes` | `POST /emails` (single), `POST /emails/batch` (up to 100), `GET /emails/:id`, `PATCH /emails/:id` (reschedule), `POST /emails/:id/cancel` |
| `domainRoutes` | Domain CRUD with pre-populated DNS record stubs |
| `apiKeyRoutes` | API key CRUD |
| `contactRoutes` | Contact CRUD per audience |
| `inboxRoutes` | `GET /inbox`, `GET /inbox/:id` — dev UI to view sent emails |

Sources: [packages/@emulators/resend/src/index.ts:5-9](packages/@emulators/resend/src/index.ts)

### Webhook dispatch

Sending an email fires two events through the shared `WebhookDispatcher`: `email.sent` and `email.delivered`. Scheduled emails (`scheduled_at` parameter) skip these events until they are explicitly triggered. Batch sends validate all emails before inserting any, so a validation error in item 3 of a batch leaves the store untouched.

```ts
// packages/@emulators/resend/src/routes/emails.ts:62-75
if (!scheduledAt) {
  await webhooks.dispatch("email.sent", undefined,
    { type: "email.sent", data: { email_id: uuid, to: toArray, from, subject } }, "resend");
  await webhooks.dispatch("email.delivered", undefined,
    { type: "email.delivered", data: { email_id: uuid, ... } }, "resend");
}
```

Sources: [packages/@emulators/resend/src/routes/emails.ts:62-75](packages/@emulators/resend/src/routes/emails.ts)

### Inbox UI

`GET /inbox` returns an HTML page listing all sent emails with their status (`delivered`, `bounced`, `scheduled`), sender, and recipients. `GET /inbox/:id` shows the full email including HTML body — a quick way to verify that your transactional email template rendered correctly without a real mail server.

Sources: [packages/@emulators/resend/src/routes/inbox.ts:11-47](packages/@emulators/resend/src/routes/inbox.ts)

---

## MongoDB Atlas emulator

### REST surface area

The Atlas emulator splits into two distinct API surfaces:

**Admin API** (`adminRoutes`) — mirrors the Atlas Administration API v2 at `/api/atlas/v2/...`:

| Resource | Operations |
|----------|------------|
| Projects (`/groups`) | CRUD, cascade-deletes clusters on project delete |
| Clusters | CRUD with provider settings, connection string generation |
| Database users | Create, list, delete per project |

**Data API** (`dataApiRoutes`) — mirrors the Atlas Data API v1 at `/app/data-api/v1/action/...`:

| Action | Description |
|--------|-------------|
| `findOne` | Find first document matching a filter |
| `find` | Find documents with filter, sort, skip, limit |
| `insertOne` | Insert a document with auto-generated `_id` |
| `insertMany` | Batch insert |
| `updateOne` / `updateMany` | MongoDB `$set`-style update |
| `deleteOne` / `deleteMany` | Delete by filter |
| `aggregate` | Basic aggregation pipeline (subset of operators) |

Sources: [packages/@emulators/mongoatlas/src/index.ts:1-7](packages/@emulators/mongoatlas/src/index.ts), [packages/@emulators/mongoatlas/src/routes/admin.ts:1-79](packages/@emulators/mongoatlas/src/routes/admin.ts), [packages/@emulators/mongoatlas/src/routes/data-api.ts:10-44](packages/@emulators/mongoatlas/src/routes/data-api.ts)

Documents are stored in a flat in-memory table keyed by `(cluster_id, database, collection)`. The Data API routes parse a `filter` field using an in-process `matchFilter` helper that supports basic equality and `$in`/`$gt`/`$lt` operators.

---

## Shared webhook dispatch

All seven emulators share one `WebhookDispatcher` instance from `@emulators/core`. The dispatcher maintains an in-memory subscription list and a delivery log (capped at 1000 entries):

```ts
// packages/@emulators/core/src/webhooks.ts:27-30
export class WebhookDispatcher {
  private subscriptions: WebhookSubscription[] = [];
  private deliveries: WebhookDelivery[] = [];
  ...
}
```

When an emulator calls `webhooks.dispatch(event, action, payload, owner, repo?)`, the dispatcher:

1. Filters subscriptions by `owner`, `repo`, and `events` list.
2. Signs the JSON body with HMAC-SHA256 if the subscription has a `secret`, adding `X-Hub-Signature-256`.
3. POSTs to the subscription URL using `fetch` with a 10-second `AbortSignal` timeout.
4. Records a `WebhookDelivery` entry (status code, duration, success flag) regardless of outcome — failures are logged, not re-tried.

Sources: [packages/@emulators/core/src/webhooks.ts:73-138](packages/@emulators/core/src/webhooks.ts)

```mermaid
sequenceDiagram
  participant App as Your app code
  participant Emulator as Emulator route
  participant Dispatcher as WebhookDispatcher
  participant Handler as Your webhook handler

  App->>Emulator: POST /v1/payment_intents
  Emulator->>Dispatcher: dispatch("payment_intent.created", payload)
  Dispatcher->>Handler: POST /api/webhooks/stripe (signed body)
  Handler-->>Dispatcher: 200 OK
  Dispatcher-->>Emulator: delivery recorded
  Emulator-->>App: { id: "pi_...", status: "requires_payment_method" }
```

---

## Examples

### stripe-checkout

The `examples/stripe-checkout` Next.js app demonstrates the full Stripe Checkout flow end-to-end:

1. A product catalog page fetches products and prices via the official `stripe` Node SDK, pointed at `localhost` with `protocol: http`.
2. A server action calls `POST /v1/checkout/sessions` to create a session.
3. The browser redirects to the emulator's hosted checkout page (`GET /checkout/:id`).
4. The user clicks "Pay and Complete"; the emulator marks the session as paid and fires `checkout.session.completed` to `POST /api/webhooks/stripe` in the same Next.js app.
5. The webhook handler records the order; the user is redirected to a success page.

A thin proxy route at `/v1/[...path]` rewrites Stripe SDK calls from the default Stripe API domain to the embedded emulator. The emulator is mounted at `/emulate/stripe/` via `@emulators/adapter-next`.

Sources: [examples/stripe-checkout/README.md:1-73](examples/stripe-checkout/README.md)

### oauth example

`examples/oauth` shows multi-provider OAuth sign-in (GitHub, Google, Vercel) against separate emulator processes:

```ts
// examples/oauth/src/lib/providers.ts:19-21
const GITHUB_URL = process.env.GITHUB_EMULATOR_URL ?? "http://localhost:4001";
const VERCEL_URL = process.env.VERCEL_EMULATOR_URL ?? "http://localhost:4000";
const GOOGLE_URL = process.env.GOOGLE_EMULATOR_URL ?? "http://localhost:4002";
```

The app redirects to the emulator's authorize endpoint, which shows a user-picker UI. After selection, the emulator issues a code, and the callback route exchanges it for a token via the emulator's token endpoint — identical to real OAuth from the application's perspective.

Sources: [examples/oauth/src/lib/providers.ts:19-51](examples/oauth/src/lib/providers.ts), [examples/oauth/README.md:26-34](examples/oauth/README.md)

### nextjs-embedded

`examples/nextjs-embedded` solves a specific Vercel preview deployment problem: OAuth callback URLs change with every deployment, so a fixed external emulator URL won't work. The solution embeds the emulators inside the Next.js app at `/emulate/github/**` and `/emulate/google/**`. Because the emulators run on the same origin as the app, callback URLs are always valid regardless of which preview URL Vercel assigned. The trade-off: client credentials can be set to `"any"` since validation is bypassed in embedded mode.

Sources: [examples/nextjs-embedded/README.md:1-58](examples/nextjs-embedded/README.md)

---

## Summary

The seven platform emulators in `emulate` each implement a meaningful slice of a real service's REST API — from GitHub's full issues/PRs/Actions surface to Stripe's checkout flow to MongoDB Atlas's Data API. They all share one `WebhookDispatcher`, so mutations trigger in-process webhook deliveries signed with HMAC-SHA256, matching what a production deployment would receive. The `examples/` directory shows three concrete wiring patterns: a standalone emulator process (oauth), a Next.js embedded adapter (nextjs-embedded), and a full checkout flow with webhook roundtrip (stripe-checkout). Each emulator exposes a typed `SeedConfig` interface, which is the primary entry point for declaring what state the emulator starts with before tests run.

Sources: [packages/@emulators/core/src/webhooks.ts:27-153](packages/@emulators/core/src/webhooks.ts)
