HomeBrowseUpload
← Back to registry
// Skill profile

vercel-labs/emulate

name: vercel-labs-emulate

by adisinghstudent · published 2026-04-01

邮件处理开发工具
Total installs
0
Stars
★ 0
Last updated
2026-04
// Install command
$ claw add gh:adisinghstudent/adisinghstudent-vercel-labs-emulate
View on GitHub
// Full documentation

---

name: vercel-labs-emulate

description: Local drop-in API emulation for Vercel, GitHub, and Google services in CI and no-network sandboxes

triggers:

- emulate vercel api locally

- mock github api for tests

- local api emulation for CI

- no-network sandbox testing

- emulate google oauth locally

- run vercel api without network

- local github api server for vitest

- drop-in api replacement for testing

---

# vercel-labs/emulate

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

`emulate` provides fully stateful, production-fidelity local HTTP servers that replace Vercel, GitHub, and Google APIs. Designed for CI pipelines and no-network sandboxes — not mocks, real in-memory state with proper pagination, OAuth, webhooks, and cascading deletes.

Installation

# CLI (no install needed)
npx emulate

# Or install as a dev dependency
npm install --save-dev emulate

CLI Usage

# Start all services with defaults
npx emulate

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

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

# Start with seed data
npx emulate --seed emulate.config.yaml

# Generate a starter config
npx emulate init

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

# List available services
npx emulate list

Default ports:

  • **Vercel** → `http://localhost:4000`
  • **GitHub** → `http://localhost:4001`
  • **Google** → `http://localhost:4002`
  • Port can also be set via `EMULATE_PORT` or `PORT` environment variables.

    Programmatic API

    import { createEmulator, type Emulator } from 'emulate'
    
    // Start a single service
    const github = await createEmulator({ service: 'github', port: 4001 })
    const vercel = await createEmulator({ service: 'vercel', port: 4002 })
    
    console.log(github.url)  // 'http://localhost:4001'
    console.log(vercel.url)  // 'http://localhost:4002'
    
    // Reset state (replays seed data)
    github.reset()
    
    // Shutdown
    await github.close()
    await vercel.close()

    Options

    | Option | Default | Description |

    |--------|---------|-------------|

    | `service` | *(required)* | `'github'`, `'vercel'`, or `'google'` |

    | `port` | `4000` | Port for the HTTP server |

    | `seed` | none | Inline seed data object (same shape as YAML config) |

    Instance Methods

    | Method | Description |

    |--------|-------------|

    | `url` | Base URL of the running server |

    | `reset()` | Wipe in-memory store and replay seed data |

    | `close()` | Shut down the server (returns Promise) |

    Vitest / Jest Setup

    // vitest.setup.ts
    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_URL = github.url
      process.env.VERCEL_URL = vercel.url
    })
    
    afterEach(() => {
      github.reset()
      vercel.reset()
    })
    
    afterAll(() => Promise.all([github.close(), vercel.close()]))
    // vitest.config.ts
    import { defineConfig } from 'vitest/config'
    
    export default defineConfig({
      test: {
        setupFiles: ['./vitest.setup.ts'],
        environment: 'node',
      },
    })

    Seed Configuration

    Create `emulate.config.yaml` in your project root (auto-detected):

    # Auth tokens
    tokens:
      my_token:
        login: admin
        scopes: [repo, user]
    
    vercel:
      users:
        - username: developer
          name: Developer
          email: dev@example.com
      teams:
        - slug: my-team
          name: My Team
      projects:
        - name: my-app
          team: my-team
          framework: nextjs
    
    github:
      users:
        - login: octocat
          name: The Octocat
          email: octocat@github.com
      orgs:
        - login: my-org
          name: My Organization
      repos:
        - owner: octocat
          name: hello-world
          language: JavaScript
          auto_init: true
    
    google:
      users:
        - email: testuser@example.com
          name: Test User
      oauth_clients:
        - client_id: my-client-id.apps.googleusercontent.com
          client_secret: $GOOGLE_CLIENT_SECRET
          redirect_uris:
            - http://localhost:3000/api/auth/callback/google

    Inline Seed (Programmatic)

    const github = await createEmulator({
      service: 'github',
      port: 4001,
      seed: {
        users: [
          { login: 'testuser', name: 'Test User', email: 'test@example.com' }
        ],
        repos: [
          { owner: 'testuser', name: 'my-repo', language: 'TypeScript', auto_init: true }
        ],
      },
    })

    OAuth Configuration

    GitHub OAuth Apps

    github:
      oauth_apps:
        - client_id: $GITHUB_CLIENT_ID
          client_secret: $GITHUB_CLIENT_SECRET
          name: My Web App
          redirect_uris:
            - http://localhost:3000/api/auth/callback/github

    > Without `oauth_apps` configured, the emulator accepts any `client_id` (backward-compatible). With apps configured, strict validation is enforced.

    GitHub Apps (JWT Auth)

    github:
      apps:
        - app_id: 12345
          slug: my-github-app
          name: My GitHub App
          private_key: |
            -----BEGIN RSA PRIVATE KEY-----
            ...your PEM key...
            -----END RSA PRIVATE KEY-----
          permissions:
            contents: read
            issues: write
          events: [push, pull_request]
          installations:
            - installation_id: 100
              account: my-org
              repository_selection: all

    Sign JWTs with `{ iss: "<app_id>" }` using RS256 — the emulator verifies the signature.

    Vercel Integrations

    vercel:
      integrations:
        - client_id: $VERCEL_CLIENT_ID
          client_secret: $VERCEL_CLIENT_SECRET
          name: My Vercel App
          redirect_uris:
            - http://localhost:3000/api/auth/callback/vercel

    Real-World Test Patterns

    Testing a GitHub API Client

    import { createEmulator } from 'emulate'
    import { Octokit } from '@octokit/rest'
    
    describe('GitHub integration', () => {
      let emulator: Awaited<ReturnType<typeof createEmulator>>
      let octokit: Octokit
    
      beforeAll(async () => {
        emulator = await createEmulator({
          service: 'github',
          port: 4001,
          seed: {
            users: [{ login: 'testuser', name: 'Test User' }],
            repos: [{ owner: 'testuser', name: 'my-repo', auto_init: true }],
          },
        })
    
        octokit = new Octokit({
          baseUrl: emulator.url,
          auth: 'any-token',
        })
      })
    
      afterEach(() => emulator.reset())
      afterAll(() => emulator.close())
    
      it('creates and fetches an issue', async () => {
        const { data: issue } = await octokit.issues.create({
          owner: 'testuser',
          repo: 'my-repo',
          title: 'Test issue',
          body: 'This is a test',
        })
    
        expect(issue.number).toBe(1)
        expect(issue.state).toBe('open')
    
        const { data: fetched } = await octokit.issues.get({
          owner: 'testuser',
          repo: 'my-repo',
          issue_number: issue.number,
        })
    
        expect(fetched.title).toBe('Test issue')
      })
    })

    Testing a Vercel Deployment Workflow

    import { createEmulator } from 'emulate'
    
    describe('Vercel deployment', () => {
      let emulator: Awaited<ReturnType<typeof createEmulator>>
    
      beforeAll(async () => {
        emulator = await createEmulator({
          service: 'vercel',
          port: 4002,
          seed: {
            users: [{ username: 'dev', email: 'dev@example.com' }],
            projects: [{ name: 'my-app', framework: 'nextjs' }],
          },
        })
        process.env.VERCEL_API_URL = emulator.url
      })
    
      afterEach(() => emulator.reset())
      afterAll(() => emulator.close())
    
      it('creates a deployment and transitions to READY', async () => {
        const res = await fetch(`${emulator.url}/v13/deployments`, {
          method: 'POST',
          headers: {
            Authorization: 'Bearer any-token',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({ name: 'my-app', target: 'production' }),
        })
    
        const deployment = await res.json()
        expect(deployment.readyState).toBe('READY')
      })
    })

    Testing Multiple Services Together

    import { createEmulator, type Emulator } from 'emulate'
    
    let github: Emulator
    let vercel: Emulator
    let google: Emulator
    
    beforeAll(async () => {
      ;[github, vercel, google] = await Promise.all([
        createEmulator({ service: 'github', port: 4001 }),
        createEmulator({ service: 'vercel', port: 4002 }),
        createEmulator({ service: 'google', port: 4003 }),
      ])
    
      // Point your app's env vars at local emulators
      process.env.GITHUB_API_URL = github.url
      process.env.VERCEL_API_URL = vercel.url
      process.env.GOOGLE_API_URL = google.url
    })
    
    afterEach(() => {
      github.reset()
      vercel.reset()
      google.reset()
    })
    
    afterAll(() => Promise.all([github.close(), vercel.close(), google.close()]))

    CI Configuration

    GitHub Actions

    # .github/workflows/test.yml
    jobs:
      test:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - uses: actions/setup-node@v4
            with:
              node-version: 20
          - run: npm ci
          - name: Run tests with emulated APIs
            run: npm test
            env:
              # Emulators start in vitest.setup.ts — no extra service needed
              NODE_ENV: test

    Docker / No-Network Sandbox

    FROM node:20-alpine
    WORKDIR /app
    COPY package*.json ./
    RUN npm ci
    COPY . .
    # Tests start emulators programmatically — no outbound network needed
    RUN npm test

    Key API Endpoints Reference

    GitHub Emulator

    GET    /user                                    # authenticated user
    GET    /repos/:owner/:repo                      # get repo
    POST   /user/repos                              # create repo
    POST   /repos/:owner/:repo/issues               # create issue
    PATCH  /repos/:owner/:repo/issues/:number       # update issue
    POST   /repos/:owner/:repo/pulls                # create PR
    PUT    /repos/:owner/:repo/pulls/:number/merge  # merge PR
    GET    /search/repositories                     # search repos
    GET    /search/issues                           # search issues

    Vercel Emulator

    GET    /v2/user                          # authenticated user
    GET    /v2/teams                         # list teams
    POST   /v11/projects                     # create project
    GET    /v10/projects                     # list projects
    POST   /v13/deployments                  # create deployment (auto → READY)
    GET    /v13/deployments/:idOrUrl         # get deployment
    POST   /v10/projects/:id/env             # create env vars
    GET    /v10/projects/:id/env             # list env vars

    Troubleshooting

    **Port already in use**

    # Use a different base port
    npx emulate --port 5000
    # Or set via env
    EMULATE_PORT=5000 npx emulate

    **Tests interfering with each other**

    // Always call reset() in afterEach, not afterAll
    afterEach(() => emulator.reset())

    **OAuth strict validation rejecting requests**

  • If you configure `oauth_apps` or `integrations`, only matching `client_id` values are accepted
  • Remove the `oauth_apps` block to fall back to accept-any mode
  • **Emulator not receiving requests from app code**

    // Make sure your app reads the URL from env at request time, not module load time
    // ✅ Good
    async function fetchUser() {
      return fetch(`${process.env.GITHUB_API_URL}/user`)
    }
    
    // ❌ Bad — captured before emulator starts
    const API_URL = process.env.GITHUB_API_URL

    **GitHub App JWT auth failing**

  • JWT must have `{ iss: "<app_id>" }` as a string or number matching the configured `app_id`
  • Must be signed RS256 with the exact private key from config
  • The emulator verifies the signature — use a real RSA key pair in tests
  • // Comments
    Sign in with GitHub to leave a comment.
    // Related skills

    More tools from the same signal band