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.
- package/README.md +357 -0
- 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.
|
|
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",
|