Architecture
How the emulate monorepo is structured — the core plugin system, in-memory store, and shared middleware.
packages/ emulate/ # @pleaseai/emulate — CLI (commander) + programmatic API kakao/ # @pleaseai/emulate-kakao — kauth OAuth + kapi user/talk naver/ # @pleaseai/emulate-naver — Naver Login OAuth + profile toss-payments/ # @pleaseai/emulate-toss-payments — payments + webhooks firebase/ # @pleaseai/emulate-firebase — Auth, Secure Token, FCM v1 supabase/ # @pleaseai/emulate-supabase — GoTrue + PostgREST asana/ # @pleaseai/emulate-asana — REST API v1.0 linear/ # @pleaseai/emulate-linear — GraphQL (read-only) autumn/ # @pleaseai/emulate-autumn — billing gitlab/ # @pleaseai/emulate-gitlab — GraphQL with introspection posthog/ # @pleaseai/emulate-posthog — capture + read API + OAuth spotify/ # @pleaseai/emulate-spotify — token + catalog workos/ # @pleaseai/emulate-workos — User Management + Vault x/ # @pleaseai/emulate-x — X API v2docs/ EMULATOR-CONVENTIONS.md # guide for adding new emulatorswebsite/ # this documentation site (Astro Starlight)Every service emulator follows the @emulators/* patterns from
vercel-labs/emulate and depends on
@emulators/core from npm.
The runtime and package manager is bun; task orchestration
is Turborepo; builds are tsup.
@emulators/core provides a generic Store with typed Collection<T>
instances supporting CRUD, indexing, filtering, and pagination. Each service
plugin registers its routes with the shared internal app (Hono-style
handlers) and uses the store for state.
Plugin system
Section titled “Plugin system”Each service is a ServicePlugin that:
- Defines its entity types and store collections (
entities.ts,store.ts) - Registers HTTP routes with the shared internal app (
routes/*.ts) - Provides a
seed()for minimal defaults and aseedFromConfig()that consumes its section ofemulate.config.yaml - Uses shared middleware for auth, error handling, and webhooks
The CLI’s registry (packages/emulate/src/registry.ts) lazily loads each
plugin, so starting one service does not import the other twelve. Multiple
services run simultaneously, each on its own port (auto-incremented from the
base port).
In-memory state
Section titled “In-memory state”All state is held in memory with no database. This makes the emulators fast,
easy to reset, and ideal for CI pipelines. State is populated from the seed
config on startup and mutated by API calls during a test run; reset()
restores the seeded snapshot.
Webhooks
Section titled “Webhooks”Core ships a WebhookDispatcher. Services dispatch events with
webhooks.dispatch(event, undefined, payload, "<service>"), and seed configs
can register subscriptions declaratively (webhooks: [{ url, events }]).
Fidelity rules
Section titled “Fidelity rules”- Response JSON field names and formats match the real service exactly (snake_case/camelCase per service).
- Error responses follow each real service’s error format.
- Intentional divergences (unsigned JWTs, no Supabase RLS) are documented in Authentication.