mcpField
E
emulate

emulate

github.com/vercel-labsテスト・品質2.0k542 回インストール6 日前に更新
リポジトリ

emulate について

emulate は、CI やネットワークのないサンドボックス向けに、複数の API をローカルで状態付きエミュレートします。CLI またはプログラム API から起動でき、シードデータでテスト環境を構成できます。

主な機能

  1. 01複数のローカル API エミュレーターを起動
  2. 02CLI でサービスを選択・設定
  3. 03プログラム API で個別サービスを作成
  4. 04シードデータから状態を初期化
  5. 05ファイルまたはカスタム永続化アダプターを使用
  6. 06カスタムエミュレーターをひな形から作成

活用シーン

  1. 01CI で外部 API に依存するアプリをテスト
  2. 02ネットワークなしで統合テストを実行
  3. 03OAuth と API テスト用のローカルデータを準備
  4. 04サードパーティ API エミュレーターを開発・テスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/vercel-labs/emulate --skill emulate

インストール後、Claude Code で/emulateを入力して読み込まれたか確認します。

詳細

emulate によるサービスエミュレーション

CI やネットワークに接続できないサンドボックスで利用できる、ローカル環境向けのドロップイン置換サービスです。モックではなく、本番環境に忠実な、完全な状態管理に対応する API エミュレーションです。

クイックスタート

npx emulate

すべてのサービスは、デフォルトで IPv4 ループバック(127.0.0.1)でリッスンします。

サービスデフォルトポート
Vercel4000
GitHub4001
Google4002
Slack4003
Apple4004
Microsoft4005
Okta4006
AWS4007
Resend4008
Stripe4009
MongoDB Atlas4010
Clerk4011
Linear4012
Twilio4013

CLI

# Start all services (zero-config)
npx emulate

# Start specific services
npx emulate --service vercel,github

# Custom base port (auto-increments per service)
npx emulate --port 3000

# Use a seed config file
npx emulate --seed config.yaml

# Generate omitted service secrets into a private file
npx emulate start --seed config.yaml --generated-secrets-file .emulate-secrets.json

# Generate a starter config
npx emulate init

# Generate config for a specific service
npx emulate init --service vercel

# List available services
npx emulate list
オプション
フラグデフォルト説明
-p, --port4000ベースポート(サービスごとに自動でインクリメントされます)
--host127.0.0.1リッスンアドレス。ネットワークアクセスを許可するには 0.0.0.0 を指定します
-s, --serviceall有効にするサービスをカンマ区切りで指定します
--seedauto-detectシード設定ファイルのパス(YAML または JSON)
--base-urlnone公開するベース URL を上書きします({service} テンプレートに対応)
--portlessoffportless 経由で HTTPS を提供します(エイリアスを自動登録します)
--generated-secrets-filenone省略されたサービスシークレットを生成し、新しい所有者のみがアクセスできる JSON ファイルに書き込みます

ポートは、環境変数 EMULATE_PORT または PORT でも設定できます。

コンテナーや別のマシンからアクセスするには、npx emulate start --host 0.0.0.0 を使用します。OAuth リダイレクトやその他の公開 URL を使用する場合は、クライアントから到達できる URL を --base-url に設定してください。

生成されるシークレットの保存先は、存在していてはなりません。emulate は継承された ACL を削除し、実効アクセス権が所有者のみに制限されていることを検証したうえで、リスナーの起動や portless の設定を行う前に完全な JSON を書き込みます。処理済みの起動エラーが発生した場合は、その実行で作成されたファイルを削除します。強制終了した場合は完全なファイルが残ることがあります。その場合は、いずれの実行も使用していないことを確認してから手動で削除してください。ファイルにはサービスによって生成された値のみが含まれます。Linux では、acl パッケージの setfacl と getfacl が必要です。アクセス制御を検証できない場合、このフラグは安全のため処理を中止し、Windows ではサポートされていません。

公開ベース URL(OAuth リダイレクト、Webhook URL などで使用)は、--base-url、環境変数 EMULATE_BASE_URL({service} テンプレートに対応)、またはシード設定内のサービスごとの baseUrl で上書きできます。portless の実行時には、環境変数 PORTLESS_URL も自動的に検出されます。

プログラムからの API 利用

npm install emulate

createEmulator を呼び出すたびに、単一のサービスが起動します。

import { createEmulator } from 'emulate'

const github = await createEmulator({ service: 'github', port: 4001 })
const vercel = await createEmulator({ service: 'vercel', port: 4002 })

github.url   // 'http://localhost:4001'
vercel.url   // 'http://localhost:4002'

await github.close()
await vercel.close()

GitHub App のテストでは、GET /_emulate/installation-tokens で、シークレットを含まない発行済みインストールトークンのメタデータを確認できます。

オプション
オプションデフォルト説明
service(必須)'vercel'、'github'、'google'、'slack'、'apple'、'microsoft'、'okta'、'aws'、'resend'、'stripe'、'mongoatlas'、'clerk'、'linear'、または 'twilio'
port4000HTTP サーバーのポート
hostname127.0.0.1組み込みエミュレーターおよびカスタムエミュレーターのリッスンアドレス
seednoneインラインのシードデータ(YAML 設定と同じ形式)
baseUrlnone公開ベース URL を上書きします。優先順位は、シード設定内のサービスごとの baseUrl、このオプション、環境変数 EMULATE_BASE_URL({service} に対応)、PORTLESS_URL({service} に対応し、portless CLI ラッパーによって自動設定)、http://localhost:<port> の順です。

コンテナーや別のマシンからの接続を許可するには hostname: '0.0.0.0' を使用し、それらのクライアントに公開する URL を指定するには baseUrl を使用します。

インスタンスメソッド
メソッド説明
url実行中サーバーのベース URL
reset()ストアを消去し、シードデータを再適用します
close()HTTP サーバーを停止し、Promise を返します

Vitest / Jest の設定

import { createEmulator, type Emulator } from 'emulate'

let github: Emulator
let vercel: Emulator

beforeAll(async () => {
  ;[github, vercel] = await Promise.all([
    createEmulator({ service: 'github', port: 4001 }),
    createEmulator({ service: 'vercel', port: 4002 }),
  ])
  process.env.GITHUB_EMULATOR_URL = github.url
  process.env.VERCEL_EMULATOR_URL = vercel.url
})

afterEach(() => { github.reset(); vercel.reset() })
afterAll(() => Promise.all([github.close(), vercel.close()]))

設定

設定は任意です。CLI は次の順序で設定ファイルを自動検出します。

  1. emulate.config.yaml / .yml
  2. emulate.config.json
  3. service-emulator.config.yaml / .yml
  4. service-emulator.config.json

または、--seed <file> を明示的に渡してください。npx emulate init を実行すると、ひな形ファイルが生成されます。

設定の構造
tokens:
  my_token:
    login: admin
    scopes: [repo, user]

vercel:
  users:
    - username: developer
      name: Developer
      email: [email protected]
  teams:
    - slug: my-team
      name: My Team
  projects:
    - name: my-app
      team: my-team
      framework: nextjs
  integrations:
    - client_id: oac_abc123
      client_secret: secret_abc123
      name: My Vercel App
      redirect_uris:
        - http://localhost:3000/api/auth/callback/vercel

github:
  users:
    - login: octocat
      name: The Octocat
      email: [email protected]
  orgs:
    - login: my-org
      name: My Organization
      members:
        - login: octocat
          role: admin
  repos:
    - owner: octocat
      name: hello-world
      language: JavaScript
      auto_init: true
  oauth_apps:
    - client_id: Iv1.abc123
      client_secret: secret_abc123
      name: My Web App
      redirect_uris:
        - http://localhost:3000/api/auth/callback/github

google:
  users:
    - email: [email protected]
      name: Test User
  oauth_clients:
    - client_id: my-client-id.apps.googleusercontent.com
      client_secret: GOCSPX-secret
      redirect_uris:
        - http://localhost:3000/api/auth/callback/google

slack:
  team:
    name: My Workspace
    domain: my-workspace
  users:
    - name: developer
      real_name: Developer
      email: [email protected]
  channels:
    - name: general
      topic: General discussion
  bots:
    - name: my-bot
  oauth_apps:
    - client_id: "12345.67890"
      client_secret: example_client_secret
      name: My Slack App
      redirect_uris:
        - http://localhost:3000/api/auth/callback/slack
  signing_secret: my_signing_secret

linear:
  organization:
    name: Acme
    url_key: acme
  users:
    - email: [email protected]
      name: Admin User
      admin: true
    - email: [email protected]
      name: Developer
  teams:
    - key: ENG
      name: Engineering
      states:
        - name: Backlog
          type: backlog
        - name: Todo
          type: unstarted
        - name: In Progress
          type: started
        - name: Done
          type: completed
  labels:
    - name: Bug
      color: "#d92d20"
      team: ENG
    - name: Feature
      color: "#2563eb"
      team: ENG
  issues:
    - team: ENG
      title: Fix local checkout test
      description: Reproduce and fix the checkout failure.
      state: Todo
      assignee: [email protected]
      labels: [Bug]
  oauth_apps:
    - client_id: lin_example_client_id
      client_secret: example_client_secret
      name: My Linear App
      redirect_uris:
        - http://localhost:3000/api/auth/callback/linear
      scopes: [read, write, issues:create, comments:create]
      actor: user
  tokens:
    - token: lin_test_admin
      user: [email protected]
      scopes: [read, write, issues:create, comments:create, admin]
  strict_scopes: false

apple:
  users:
    - email: [email protected]
      name: Test User
  oauth_clients:
    - client_id: com.example.app
      team_id: TEAM001
      name: My Apple App
      redirect_uris:
        - http://localhost:3000/api/auth/callback/apple

microsoft:
  users:
    - email: [email protected]
      name: Test User
  oauth_clients:
    - client_id: example-client-id
      client_secret: example-client-secret
      name: My Microsoft App
      redirect_uris:
        - http://localhost:3000/api/auth/callback/microsoft-entra-id

aws:
  region: us-east-1
  s3:
    buckets:
      - name: my-app-bucket
  sqs:
    queues:
      - name: my-app-events
  iam:
    users:
      - user_name: developer
        create_access_key: true
    roles:
      - role_name: lambda-execution-role
        description: Role for Lambda function execution

okta:
  users:
    - login: [email protected]
      email: [email protected]
      first_name: Test
      last_name: User
  groups:
    - name: Everyone
      description: All users
      type: BUILT_IN
      okta_id: 00g_everyone
  authorization_servers:
    - id: default
      name: default
      audiences: [api://default]
  oauth_clients:
    - client_id: okta-test-client
      client_secret: okta-test-secret
      name: Sample OIDC Client
      redirect_uris:
        - http://localhost:3000/callback
      auth_server_id: default

resend:
  domains:
    - name: example.com
      region: us-east-1
  contacts:
    - email: [email protected]
      first_name: Test
      last_name: User

stripe:
  customers:
    - email: [email protected]
      name: Test Customer
  products:
    - name: Pro Plan
      description: Monthly pro subscription
  prices:
    - product_name: Pro Plan
      currency: usd
      unit_amount: 2000

mongoatlas:
  projects:
    - name: Project0
  clusters:
    - name: Cluster0
      project: Project0
  database_users:
    - username: admin
      project: Project0
  databases:
    - cluster: Cluster0
      name: test
      collections: [items]

clerk:
  users:
    - first_name: Test
      last_name: User
      email_addresses: [[email protected]]
      password: clerk_test_password
  organizations:
    - name: My Company
      slug: my-company
      members:
        - email: [email protected]
          role: admin
  oauth_applications:
    - client_id: clerk_emulate_client
      client_secret: clerk_emulate_secret
      name: Emulate App
      redirect_uris:
        - http://localhost:3000/api/auth/callback/clerk

twilio:
  account:
    sid: AC00000000000000000000000000000000
    auth_token: twilio_test_auth_token
    friendly_name: Local Twilio Account
  api_keys:
    - sid: SK00000000000000000000000000000000
      secret: twilio_test_api_secret
      friendly_name: Local API Key
  phone_numbers:
    - phone_number: "+15551234567"
      friendly_name: Local SMS and Voice Number
      sms_url: http://localhost:3000/api/twilio/sms
      voice_url: http://localhost:3000/api/twilio/voice
  messaging_services:
    - friendly_name: Local Messaging Service
      phone_numbers: ["+15551234567"]
  verify_services:
    - friendly_name: Local Verify Service
      code: "123456"
      default_channel: sms
  conversations:
    services:
      - friendly_name: Local Conversations

slack.signing_secret は、すべての送信イベントサブスクリプションコールバックに署名します。署名付きコールバックには X-Slack-Request-Timestamp と X-Slack-Signature が含まれ、正確にシリアライズされたコールバック本文に対して v0=<HMAC-SHA256(secret, "v0:<timestamp>:<raw-body>")> として計算されます。受信側には同じシークレットを設定し、解析前のリクエスト本文を検証してください。シークレットが未設定または空の場合、コールバックには署名が付与されません。

GitHub App の private_key の値は、意図的にひな形の設定から省略されています。プログラムから createEmulator を呼び出すと、RSA キーが生成され、generatedSecrets を通じて公開されます。CLI の起動時に省略されたキーが生成されるのは、--generated-secrets-file <path> が指定されている場合のみです。それ以外の場合、シードには有効な秘密鍵を明示的に含める必要があります。プレースホルダーの PEM 値は決して使用しないでください。

GitHub 組織の members は任意です。エントリでは、login によってシード済みユーザーを参照します。role の既定値は member で、admin を指定すると組織の管理者になります。不明なユーザーは無視されます。

認証

トークンはユーザーに対応付けられます。Authorization: Bearer <token> または Authorization: token <token> として渡してください。トークンが設定されていない場合は、admin ユーザー用に既定の test_token_admin が作成されます。

各サービスにはフォールバックユーザーもあります。トークンが指定されていない場合、リクエストは最初にシードされたユーザーとして認証されます。

portless を使った HTTPS

portless を使うと、自動生成された証明書を用いて、エミュレーターに信頼済みの HTTPS URL を割り当てられます。--portless フラグを使用すると、各サービスが portless エイリアスとして自動登録されます。

npx emulate start --portless
# github  https://github.emulate.localhost
# google  https://google.emulate.localhost
# ...

これには portless プロキシが起動している必要があります(portless proxy start)。portless がインストールされていない場合、emulate からインストールを促されます。

--portless フラグを指定すると、*.emulate に一致する既存の portless エイリアスは上書きされます。エイリアスは emulate の終了時に自動的に削除されます。

portless の背後で単一のサービスを実行する場合:

portless github.emulate emulate start --service github

portless を使わずにカスタムのベース URL を設定する場合(任意のリバースプロキシ):

npx emulate start --base-url "https://{service}.myproxy.test"
# or
EMULATE_BASE_URL="https://{service}.myproxy.test" npx emulate start

portless CLI ラッパーを通じてコマンドを実行すると(例:portless github.emulate emulate start)、PORTLESS_URL 環境変数が自動的に設定されます。通常、値は https://{service}.emulate.localhost のようになります。--base-url および EMULATE_BASE_URL と同様に、{service} の埋め込みに対応しています。明示的な baseUrl が指定されていない場合、フォールバックとして使用されます。

シード設定でのサービスごとのオーバーライド(ほかのすべてのベース URL の指定元よりも優先されます):

github:
  baseUrl: https://github.emulate.localhost
google:
  baseUrl: https://google.emulate.localhost

アプリからエミュレーターを参照する

環境変数を設定して、実際のサービス URL を上書きします。

VERCEL_EMULATOR_URL=http://localhost:4000
GITHUB_EMULATOR_URL=http://localhost:4001
GOOGLE_EMULATOR_URL=http://localhost:4002
SLACK_EMULATOR_URL=http://localhost:4003
APPLE_EMULATOR_URL=http://localhost:4004
MICROSOFT_EMULATOR_URL=http://localhost:4005
AWS_EMULATOR_URL=http://localhost:4007
LINEAR_EMULATOR_URL=http://localhost:4012

これらをアプリで使用して、API URL と OAuth URL を構築してください。SDK 固有のオーバーライド手順については、各サービスのスキルを参照してください。

フレームワークとの統合(埋め込みモード)

@emulators/adapter-next パッケージを使うと、同じオリジン上でエミュレーターを Next.js アプリに直接埋め込めます。詳しい設定、Auth.js の構成、永続化、フォントのトレースについては、next スキル(skills/next/SKILL.md)を参照してください。

@emulators/adapter-nuxt パッケージを使うと、同じオリジン上でエミュレーターを Nuxt アプリに直接埋め込めます。サーバールート、Nuxt の設定、OAuth の設定、永続化の設定については、nuxt スキル(skills/nuxt/SKILL.md)を参照してください。

永続化

既定では、すべてのエミュレーター状態はメモリ内に保存されます。プロセスの再起動やサーバーレスのコールドスタートをまたいで状態を永続化するには、PersistenceAdapter を使用してください。

組み込みのファイル永続化
import { filePersistence } from '@emulators/core'

// CLI or local dev: persists to a JSON file
const adapter = filePersistence('.emulate/state.json')
カスタムアダプター
import type { PersistenceAdapter } from '@emulators/core'

const kvAdapter: PersistenceAdapter = {
  async load() { return await kv.get('emulate-state') },
  async save(data) { await kv.set('emulate-state', data) },
}

状態はコールドスタート時にロードされ、変更を伴うリクエスト(POST、PUT、PATCH、DELETE)のたびに保存されます。保存は競合状態を防ぐために直列化されます。生成された GitHub App アイデンティティでは、initialize が初期値をアトミックに作成するか、別のインスタンスが先に作成した値を返す必要があります。

アーキテクチャ

packages/
  emulate/           # CLI entry point + programmatic API
  @emulators/
    core/            # HTTP server, Store, plugin interface, middleware
    adapter-next/    # Next.js App Router integration
    adapter-nuxt/    # Nuxt server route integration
    vercel/          # Vercel API service plugin
    github/          # GitHub API service plugin
    google/          # Google OAuth 2.0 / OIDC plugin
    slack/           # Slack Web API, OAuth, incoming webhooks plugin
    linear/          # Linear GraphQL API, OAuth, webhooks plugin
    twilio/          # Twilio Messaging, Verify, Voice, webhooks plugin
    apple/           # Sign in with Apple / OIDC plugin
    microsoft/       # Microsoft Entra ID OAuth 2.0 / OIDC plugin
    aws/             # AWS S3, SQS, IAM, STS plugin

コアは、CRUD、インデックス作成、フィルタリング、ページネーションをサポートする型付き Collection<T> インスタンスを備えた汎用の Store を提供します。各サービスプラグインは、共有の内部アプリにルートを登録し、状態のためにストアを使用します。

ビルトインと併用するカスタムエミュレーター

npx emulate init --custom inventory を使用して、サードパーティ API エミュレーターをスキャフォールドし、テストします。このコマンドは、検出された YAML、JSON、TypeScript、または JavaScript の設定にそれを登録します。既存のサービスを含む設定も対象です。通常とは異なる実行可能な設定については、手動での登録手順が表示されます。npx emulate start --watch を実行してインポートを再読み込みします。ポートは設定に依存するため、リクエストには起動バナーに表示されるサービス URL と Inspector リンクを使用してください。設定ディレクトリ外に存在しないローカルインポートを作成すると、失敗した再読み込みも再試行されます。再読み込みに成功すると、実行はシードにリセットされます。既存のフラットなシード設定も引き続き機能します。--config は明示的なファイルを選択します。Node 26 は消去可能な TypeScript をロードします。enum とパラメータープロパティは先に JavaScript へコンパイルしてください。Node 24 もネイティブの TypeScript トランスフォームをサポートしています。サードパーティ API エミュレーターの作成とテストについては、https://emulate.dev/docs/custom-emulators を参照してください。

コメント