emulate 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +357 -0
  2. package/package.json +7 -7
package/README.md ADDED
@@ -0,0 +1,357 @@
1
+ # emulate
2
+
3
+ Local drop-in replacement services for CI and no-network sandboxes. Fully stateful, production-fidelity API emulation. Not mocks.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx emulate
9
+ ```
10
+
11
+ All services start with sensible defaults. No config file needed:
12
+
13
+ - **Vercel** on `http://localhost:4000`
14
+ - **GitHub** on `http://localhost:4001`
15
+ - **Google** on `http://localhost:4002`
16
+
17
+ ## CLI
18
+
19
+ ```bash
20
+ # Start all services (zero-config)
21
+ emulate
22
+
23
+ # Start specific services
24
+ emulate --service vercel,github
25
+
26
+ # Custom port
27
+ emulate --port 3000
28
+
29
+ # Use a seed config file
30
+ emulate --seed config.yaml
31
+
32
+ # Generate a starter config
33
+ emulate init
34
+
35
+ # Generate config for a specific service
36
+ emulate init --service vercel
37
+
38
+ # List available services
39
+ emulate list
40
+ ```
41
+
42
+ ### Options
43
+
44
+ | Flag | Default | Description |
45
+ |------|---------|-------------|
46
+ | `-p, --port` | `4000` | Base port (auto-increments per service) |
47
+ | `-s, --service` | all | Comma-separated services to enable |
48
+ | `--seed` | auto-detect | Path to seed config (YAML or JSON) |
49
+
50
+ The port can also be set via `EMULATE_PORT` or `PORT` environment variables.
51
+
52
+ ## Configuration
53
+
54
+ Configuration is optional. To customize seed data, create `emulate.config.yaml` in your project root (or pass `--seed`):
55
+
56
+ ```yaml
57
+ tokens:
58
+ my_token:
59
+ login: admin
60
+ scopes: [repo, user]
61
+
62
+ vercel:
63
+ users:
64
+ - username: developer
65
+ name: Developer
66
+ email: dev@example.com
67
+ teams:
68
+ - slug: my-team
69
+ name: My Team
70
+ projects:
71
+ - name: my-app
72
+ team: my-team
73
+ framework: nextjs
74
+
75
+ github:
76
+ users:
77
+ - login: octocat
78
+ name: The Octocat
79
+ email: octocat@github.com
80
+ orgs:
81
+ - login: my-org
82
+ name: My Organization
83
+ repos:
84
+ - owner: octocat
85
+ name: hello-world
86
+ language: JavaScript
87
+ auto_init: true
88
+
89
+ google:
90
+ users:
91
+ - email: testuser@example.com
92
+ name: Test User
93
+ oauth_clients:
94
+ - client_id: my-client-id.apps.googleusercontent.com
95
+ client_secret: GOCSPX-secret
96
+ redirect_uris:
97
+ - http://localhost:3000/api/auth/callback/google
98
+ ```
99
+
100
+ ## OAuth & Integrations
101
+
102
+ The emulator supports configurable OAuth apps and integrations with strict client validation.
103
+
104
+ ### Vercel Integrations
105
+
106
+ ```yaml
107
+ vercel:
108
+ integrations:
109
+ - client_id: "oac_abc123"
110
+ client_secret: "secret_abc123"
111
+ name: "My Vercel App"
112
+ redirect_uris:
113
+ - "http://localhost:3000/api/auth/callback/vercel"
114
+ ```
115
+
116
+ ### GitHub OAuth Apps
117
+
118
+ ```yaml
119
+ github:
120
+ oauth_apps:
121
+ - client_id: "Iv1.abc123"
122
+ client_secret: "secret_abc123"
123
+ name: "My Web App"
124
+ redirect_uris:
125
+ - "http://localhost:3000/api/auth/callback/github"
126
+ ```
127
+
128
+ If no `oauth_apps` are configured, the emulator accepts any `client_id` (backward-compatible). With apps configured, strict validation is enforced.
129
+
130
+ ### GitHub Apps
131
+
132
+ Full GitHub App support with JWT authentication and installation access tokens:
133
+
134
+ ```yaml
135
+ github:
136
+ apps:
137
+ - app_id: 12345
138
+ slug: "my-github-app"
139
+ name: "My GitHub App"
140
+ private_key: |
141
+ -----BEGIN RSA PRIVATE KEY-----
142
+ ...your PEM key...
143
+ -----END RSA PRIVATE KEY-----
144
+ permissions:
145
+ contents: read
146
+ issues: write
147
+ events: [push, pull_request]
148
+ installations:
149
+ - installation_id: 100
150
+ account: my-org
151
+ repository_selection: all
152
+ ```
153
+
154
+ JWT authentication: sign a JWT with `{ iss: "<app_id>" }` using the app's private key (RS256). The emulator verifies the signature and resolves the app.
155
+
156
+ ## Vercel API
157
+
158
+ Every endpoint below is fully stateful with Vercel-style JSON responses and cursor-based pagination.
159
+
160
+ ### User & Teams
161
+ - `GET /v2/user` - authenticated user
162
+ - `PATCH /v2/user` - update user
163
+ - `GET /v2/teams` - list teams (cursor paginated)
164
+ - `GET /v2/teams/:teamId` - get team (by ID or slug)
165
+ - `POST /v2/teams` - create team
166
+ - `PATCH /v2/teams/:teamId` - update team
167
+ - `GET /v2/teams/:teamId/members` - list members
168
+ - `POST /v2/teams/:teamId/members` - add member
169
+
170
+ ### Projects
171
+ - `POST /v11/projects` - create project (with optional env vars and git integration)
172
+ - `GET /v10/projects` - list projects (search, cursor pagination)
173
+ - `GET /v9/projects/:idOrName` - get project (includes env vars)
174
+ - `PATCH /v9/projects/:idOrName` - update project
175
+ - `DELETE /v9/projects/:idOrName` - delete project (cascades)
176
+ - `GET /v1/projects/:projectId/promote/aliases` - promote aliases status
177
+ - `PATCH /v1/projects/:idOrName/protection-bypass` - manage bypass secrets
178
+
179
+ ### Deployments
180
+ - `POST /v13/deployments` - create deployment (auto-transitions to READY)
181
+ - `GET /v13/deployments/:idOrUrl` - get deployment (by ID or URL)
182
+ - `GET /v6/deployments` - list deployments (filter by project, target, state)
183
+ - `DELETE /v13/deployments/:id` - delete deployment (cascades)
184
+ - `PATCH /v12/deployments/:id/cancel` - cancel building deployment
185
+ - `GET /v2/deployments/:id/aliases` - list deployment aliases
186
+ - `GET /v3/deployments/:idOrUrl/events` - get build events/logs
187
+ - `GET /v6/deployments/:id/files` - list deployment files
188
+ - `POST /v2/files` - upload file (by SHA digest)
189
+
190
+ ### Domains
191
+ - `POST /v10/projects/:idOrName/domains` - add domain (with verification challenge)
192
+ - `GET /v9/projects/:idOrName/domains` - list domains
193
+ - `GET /v9/projects/:idOrName/domains/:domain` - get domain
194
+ - `PATCH /v9/projects/:idOrName/domains/:domain` - update domain
195
+ - `DELETE /v9/projects/:idOrName/domains/:domain` - remove domain
196
+ - `POST /v9/projects/:idOrName/domains/:domain/verify` - verify domain
197
+
198
+ ### Environment Variables
199
+ - `GET /v10/projects/:idOrName/env` - list env vars (with decrypt option)
200
+ - `POST /v10/projects/:idOrName/env` - create env vars (single, batch, upsert)
201
+ - `GET /v10/projects/:idOrName/env/:id` - get env var
202
+ - `PATCH /v9/projects/:idOrName/env/:id` - update env var
203
+ - `DELETE /v9/projects/:idOrName/env/:id` - delete env var
204
+
205
+ ## GitHub API
206
+
207
+ Every endpoint below is fully stateful. Creates, updates, and deletes persist in memory and affect related entities.
208
+
209
+ ### Users
210
+ - `GET /user` - authenticated user
211
+ - `PATCH /user` - update profile
212
+ - `GET /users/:username` - get user
213
+ - `GET /users` - list users
214
+ - `GET /users/:username/repos` - list user repos
215
+ - `GET /users/:username/orgs` - list user orgs
216
+ - `GET /users/:username/followers` - list followers
217
+ - `GET /users/:username/following` - list following
218
+
219
+ ### Repositories
220
+ - `GET /repos/:owner/:repo` - get repo
221
+ - `POST /user/repos` - create user repo
222
+ - `POST /orgs/:org/repos` - create org repo
223
+ - `PATCH /repos/:owner/:repo` - update repo
224
+ - `DELETE /repos/:owner/:repo` - delete repo (cascades)
225
+ - `GET/PUT /repos/:owner/:repo/topics` - get/replace topics
226
+ - `GET /repos/:owner/:repo/languages` - languages
227
+ - `GET /repos/:owner/:repo/contributors` - contributors
228
+ - `GET /repos/:owner/:repo/forks` - list forks
229
+ - `POST /repos/:owner/:repo/forks` - create fork
230
+ - `GET/PUT/DELETE /repos/:owner/:repo/collaborators/:username` - collaborators
231
+ - `GET /repos/:owner/:repo/collaborators/:username/permission`
232
+ - `POST /repos/:owner/:repo/transfer` - transfer repo
233
+ - `GET /repos/:owner/:repo/tags` - list tags
234
+
235
+ ### Issues
236
+ - `GET /repos/:owner/:repo/issues` - list (filter by state, labels, assignee, milestone, creator, since)
237
+ - `POST /repos/:owner/:repo/issues` - create
238
+ - `GET /repos/:owner/:repo/issues/:number` - get
239
+ - `PATCH /repos/:owner/:repo/issues/:number` - update (state transitions, events)
240
+ - `PUT/DELETE /repos/:owner/:repo/issues/:number/lock` - lock/unlock
241
+ - `GET /repos/:owner/:repo/issues/:number/timeline` - timeline events
242
+ - `GET /repos/:owner/:repo/issues/:number/events` - events
243
+ - `POST/DELETE /repos/:owner/:repo/issues/:number/assignees` - manage assignees
244
+
245
+ ### Pull Requests
246
+ - `GET /repos/:owner/:repo/pulls` - list (filter by state, head, base)
247
+ - `POST /repos/:owner/:repo/pulls` - create
248
+ - `GET /repos/:owner/:repo/pulls/:number` - get
249
+ - `PATCH /repos/:owner/:repo/pulls/:number` - update
250
+ - `PUT /repos/:owner/:repo/pulls/:number/merge` - merge (with branch protection enforcement)
251
+ - `GET /repos/:owner/:repo/pulls/:number/commits` - list commits
252
+ - `GET /repos/:owner/:repo/pulls/:number/files` - list files
253
+ - `POST/DELETE /repos/:owner/:repo/pulls/:number/requested_reviewers` - manage reviewers
254
+ - `PUT /repos/:owner/:repo/pulls/:number/update-branch` - update branch
255
+
256
+ ### Comments
257
+ - Issue comments: full CRUD on `/repos/:owner/:repo/issues/:number/comments`
258
+ - Review comments: full CRUD on `/repos/:owner/:repo/pulls/:number/comments`
259
+ - Commit comments: full CRUD on `/repos/:owner/:repo/commits/:sha/comments`
260
+ - Repo-wide listings for each type
261
+
262
+ ### Reviews
263
+ - `GET /repos/:owner/:repo/pulls/:number/reviews` - list
264
+ - `POST /repos/:owner/:repo/pulls/:number/reviews` - create (with inline comments)
265
+ - `GET/PUT /repos/:owner/:repo/pulls/:number/reviews/:id` - get/update
266
+ - `POST /repos/:owner/:repo/pulls/:number/reviews/:id/events` - submit
267
+ - `PUT /repos/:owner/:repo/pulls/:number/reviews/:id/dismissals` - dismiss
268
+
269
+ ### Labels & Milestones
270
+ - Labels: full CRUD, add/remove from issues, replace all
271
+ - Milestones: full CRUD, state transitions, issue counts
272
+
273
+ ### Branches & Git Data
274
+ - Branches: list, get, protection CRUD (status checks, PR reviews, enforce admins)
275
+ - Refs: get, match, create, update, delete
276
+ - Commits: get, create
277
+ - Trees: get (with recursive), create (with inline content)
278
+ - Blobs: get, create
279
+ - Tags: get, create
280
+
281
+ ### Organizations & Teams
282
+ - Orgs: get, update, list
283
+ - Org members: list, check, remove, get/set membership
284
+ - Teams: full CRUD, members, repos
285
+
286
+ ### Releases
287
+ - Releases: full CRUD, latest, by tag
288
+ - Release assets: full CRUD, upload
289
+ - Generate release notes
290
+
291
+ ### Webhooks
292
+ - Repo webhooks: full CRUD, ping, test, deliveries
293
+ - Org webhooks: full CRUD, ping
294
+ - Real HTTP delivery to registered URLs on all state changes
295
+
296
+ ### Search
297
+ - `GET /search/repositories` - full query syntax (user, org, language, topic, stars, forks, etc.)
298
+ - `GET /search/issues` - issues + PRs (repo, is, author, label, milestone, state, etc.)
299
+ - `GET /search/users` - users + orgs
300
+ - `GET /search/code` - blob content search
301
+ - `GET /search/commits` - commit message search
302
+ - `GET /search/topics` - topic search
303
+ - `GET /search/labels` - label search
304
+
305
+ ### Actions
306
+ - Workflows: list, get, enable/disable, dispatch
307
+ - Workflow runs: list, get, cancel, rerun, delete, logs
308
+ - Jobs: list, get, logs
309
+ - Artifacts: list, get, delete
310
+ - Secrets: repo + org CRUD
311
+
312
+ ### Checks
313
+ - Check runs: create, update, get, annotations, rerequest, list by ref/suite
314
+ - Check suites: create, get, preferences, rerequest, list by ref
315
+ - Automatic suite status rollup from check run results
316
+
317
+ ### Misc
318
+ - `GET /rate_limit` - rate limit status
319
+ - `GET /meta` - server metadata
320
+ - `GET /octocat` - ASCII art
321
+ - `GET /emojis` - emoji URLs
322
+ - `GET /zen` - random zen phrase
323
+ - `GET /versions` - API versions
324
+
325
+ ## Google API
326
+
327
+ OAuth 2.0 and OpenID Connect emulation.
328
+
329
+ - `GET /o/oauth2/v2/auth` - authorization endpoint
330
+ - `POST /oauth2/v4/token` - token exchange
331
+ - `GET /oauth2/v2/userinfo` - get user info
332
+ - `GET /.well-known/openid-configuration` - OIDC discovery document
333
+ - `GET /oauth2/v3/certs` - JSON Web Key Set (JWKS)
334
+
335
+ ## Architecture
336
+
337
+ ```
338
+ packages/
339
+ emulate/ # CLI entry point (commander)
340
+ @internal/
341
+ core/ # HTTP server, in-memory store, plugin interface, middleware
342
+ vercel/ # Vercel API service
343
+ github/ # GitHub API service
344
+ google/ # Google OAuth 2.0 / OIDC
345
+ apps/
346
+ web/ # Documentation site (Next.js)
347
+ ```
348
+
349
+ The core provides a generic `Store` with typed `Collection<T>` instances supporting CRUD, indexing, filtering, and pagination. Each service plugin registers its routes on the shared Hono app and uses the store for state.
350
+
351
+ ## Auth
352
+
353
+ Tokens are configured in the seed config and map to users. Pass them as `Authorization: Bearer <token>` or `Authorization: token <token>`.
354
+
355
+ **Vercel**: All endpoints accept `teamId` or `slug` query params for team scoping. Pagination uses cursor-based `limit`/`since`/`until` with `pagination` response objects.
356
+
357
+ **GitHub**: Public repo endpoints work without auth. Private repos and write operations require a valid token. Pagination uses `page`/`per_page` with `Link` headers.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emulate",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Local drop-in replacement services for CI and no-network sandboxes",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -35,15 +35,15 @@
35
35
  "@hono/node-server": "^1",
36
36
  "commander": "^14",
37
37
  "picocolors": "^1.1.1",
38
- "yaml": "^2",
39
- "@internal/github": "0.1.0",
40
- "@internal/google": "0.1.0",
41
- "@internal/core": "0.1.0",
42
- "@internal/vercel": "0.1.0"
38
+ "yaml": "^2"
43
39
  },
44
40
  "devDependencies": {
45
41
  "tsup": "^8",
46
- "typescript": "^5.7"
42
+ "typescript": "^5.7",
43
+ "@internal/google": "0.1.0",
44
+ "@internal/github": "0.1.0",
45
+ "@internal/vercel": "0.1.0",
46
+ "@internal/core": "0.1.0"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "tsup",