emulate 0.1.1 → 0.3.0

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 CHANGED
@@ -49,6 +49,65 @@ emulate list
49
49
 
50
50
  The port can also be set via `EMULATE_PORT` or `PORT` environment variables.
51
51
 
52
+ ## Programmatic API
53
+
54
+ ```bash
55
+ npm install emulate
56
+ ```
57
+
58
+ Each call to `createEmulator` starts a single service:
59
+
60
+ ```typescript
61
+ import { createEmulator } from 'emulate'
62
+
63
+ const github = await createEmulator({ service: 'github', port: 4001 })
64
+ const vercel = await createEmulator({ service: 'vercel', port: 4002 })
65
+
66
+ github.url // 'http://localhost:4001'
67
+ vercel.url // 'http://localhost:4002'
68
+
69
+ await github.close()
70
+ await vercel.close()
71
+ ```
72
+
73
+ ### Vitest / Jest setup
74
+
75
+ ```typescript
76
+ // vitest.setup.ts
77
+ import { createEmulator, type Emulator } from 'emulate'
78
+
79
+ let github: Emulator
80
+ let vercel: Emulator
81
+
82
+ beforeAll(async () => {
83
+ ;[github, vercel] = await Promise.all([
84
+ createEmulator({ service: 'github', port: 4001 }),
85
+ createEmulator({ service: 'vercel', port: 4002 }),
86
+ ])
87
+ process.env.GITHUB_URL = github.url
88
+ process.env.VERCEL_URL = vercel.url
89
+ })
90
+
91
+ afterEach(() => { github.reset(); vercel.reset() })
92
+ afterAll(() => Promise.all([github.close(), vercel.close()]))
93
+ ```
94
+
95
+ ### Options
96
+
97
+ | Option | Default | Description |
98
+ |--------|---------|-------------|
99
+ | `service` | *(required)* | Service to emulate: `'github'`, `'vercel'`, or `'google'` |
100
+ | `port` | `4000` | Port for the HTTP server |
101
+ | `seed` | none | Inline seed data (same shape as YAML config) |
102
+
103
+ ### Instance methods
104
+
105
+ | Method | Description |
106
+ |--------|-------------|
107
+ | `url` | Base URL of the running server |
108
+ | `reset()` | Wipe the store and replay seed data |
109
+ | `close()` | Shut down the HTTP server, returns a Promise |
110
+
52
111
  ## Configuration
53
112
 
54
113
  Configuration is optional. To customize seed data, create `emulate.config.yaml` in your project root (or pass `--seed`):
@@ -95,6 +154,40 @@ google:
95
154
  client_secret: GOCSPX-secret
96
155
  redirect_uris:
97
156
  - http://localhost:3000/api/auth/callback/google
157
+ labels:
158
+ - id: Label_ops
159
+ user_email: testuser@example.com
160
+ name: Ops/Review
161
+ color_background: "#DDEEFF"
162
+ color_text: "#111111"
163
+ messages:
164
+ - id: msg_welcome
165
+ user_email: testuser@example.com
166
+ from: welcome@example.com
167
+ to: testuser@example.com
168
+ subject: Welcome to the Gmail emulator
169
+ body_text: You can now test Gmail, Calendar, and Drive flows locally.
170
+ label_ids: [INBOX, UNREAD, CATEGORY_UPDATES]
171
+ calendars:
172
+ - id: primary
173
+ user_email: testuser@example.com
174
+ summary: testuser@example.com
175
+ primary: true
176
+ selected: true
177
+ time_zone: UTC
178
+ calendar_events:
179
+ - id: evt_kickoff
180
+ user_email: testuser@example.com
181
+ calendar_id: primary
182
+ summary: Project Kickoff
183
+ start_date_time: 2025-01-10T09:00:00.000Z
184
+ end_date_time: 2025-01-10T09:30:00.000Z
185
+ drive_items:
186
+ - id: drv_docs
187
+ user_email: testuser@example.com
188
+ name: Docs
189
+ mime_type: application/vnd.google-apps.folder
190
+ parent_ids: [root]
98
191
  ```
99
192
 
100
193
  ## OAuth & Integrations
@@ -145,6 +238,8 @@ github:
145
238
  contents: read
146
239
  issues: write
147
240
  events: [push, pull_request]
241
+ webhook_url: "http://localhost:3000/webhooks/github"
242
+ webhook_secret: "my-secret"
148
243
  installations:
149
244
  - installation_id: 100
150
245
  account: my-org
@@ -153,6 +248,10 @@ github:
153
248
 
154
249
  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
250
 
251
+ **App webhook delivery**: When events occur on repos where a GitHub App is installed, the emulator mirrors real GitHub behavior:
252
+ - All webhook payloads (including repo and org hooks) include an `installation` field with `{ id, node_id }`.
253
+ - If the app has a `webhook_url`, the emulator delivers the event there with the `installation` field and (if configured) an `X-Hub-Signature-256` header signed with `webhook_secret`.
254
+
156
255
  ## Vercel API
157
256
 
158
257
  Every endpoint below is fully stateful with Vercel-style JSON responses and cursor-based pagination.
@@ -322,26 +421,47 @@ Every endpoint below is fully stateful. Creates, updates, and deletes persist in
322
421
  - `GET /zen` - random zen phrase
323
422
  - `GET /versions` - API versions
324
423
 
325
- ## Google API
424
+ ## Google OAuth + Gmail, Calendar, and Drive APIs
326
425
 
327
- OAuth 2.0 and OpenID Connect emulation.
426
+ OAuth 2.0, OpenID Connect, and mutable Google Workspace-style surfaces for local inbox, calendar, and drive flows.
427
+ This stays under a single `google:` service because the Gmail API is used by both consumer Google accounts and Google Workspace accounts. A separate Workspace-specific service would only make sense once we add admin or tenant-level APIs that do not belong in the basic Google/Gmail emulator.
328
428
 
329
429
  - `GET /o/oauth2/v2/auth` - authorization endpoint
330
- - `POST /oauth2/v4/token` - token exchange
430
+ - `POST /oauth2/token` - token exchange
331
431
  - `GET /oauth2/v2/userinfo` - get user info
332
432
  - `GET /.well-known/openid-configuration` - OIDC discovery document
333
433
  - `GET /oauth2/v3/certs` - JSON Web Key Set (JWKS)
434
+ - `GET /gmail/v1/users/:userId/messages` - list messages with `q`, `labelIds`, `maxResults`, and `pageToken`
435
+ - `GET /gmail/v1/users/:userId/messages/:id` - fetch a Gmail-style message payload in `full`, `metadata`, `minimal`, or `raw` formats
436
+ - `GET /gmail/v1/users/:userId/messages/:messageId/attachments/:id` - fetch attachment bodies
437
+ - `POST /gmail/v1/users/:userId/messages/send` - create sent mail from `raw` MIME or structured fields
438
+ - `POST /gmail/v1/users/:userId/messages/import` - import inbox mail
439
+ - `POST /gmail/v1/users/:userId/messages` - insert a message directly
440
+ - `POST /gmail/v1/users/:userId/messages/:id/modify` - add/remove labels on one message
441
+ - `POST /gmail/v1/users/:userId/messages/batchModify` - add/remove labels across many messages
442
+ - `POST /gmail/v1/users/:userId/messages/:id/trash` and `POST /gmail/v1/users/:userId/messages/:id/untrash`
443
+ - `GET /gmail/v1/users/:userId/drafts`, `POST /gmail/v1/users/:userId/drafts`, `GET /gmail/v1/users/:userId/drafts/:id`, `PUT /gmail/v1/users/:userId/drafts/:id`, `POST /gmail/v1/users/:userId/drafts/:id/send`, `DELETE /gmail/v1/users/:userId/drafts/:id`
444
+ - `POST /gmail/v1/users/:userId/threads/:id/modify` - add/remove labels across a thread
445
+ - `GET /gmail/v1/users/:userId/threads` and `GET /gmail/v1/users/:userId/threads/:id`
446
+ - `GET /gmail/v1/users/:userId/labels`, `POST /gmail/v1/users/:userId/labels`, `PATCH /gmail/v1/users/:userId/labels/:id`, `DELETE /gmail/v1/users/:userId/labels/:id`
447
+ - `GET /gmail/v1/users/:userId/history`, `POST /gmail/v1/users/:userId/watch`, `POST /gmail/v1/users/:userId/stop`
448
+ - `GET /gmail/v1/users/:userId/settings/filters`, `POST /gmail/v1/users/:userId/settings/filters`, `DELETE /gmail/v1/users/:userId/settings/filters/:id`
449
+ - `GET /gmail/v1/users/:userId/settings/forwardingAddresses`, `GET /gmail/v1/users/:userId/settings/sendAs`
450
+ - `GET /calendar/v3/users/:userId/calendarList`, `GET /calendar/v3/calendars/:calendarId/events`, `POST /calendar/v3/calendars/:calendarId/events`, `DELETE /calendar/v3/calendars/:calendarId/events/:eventId`, `POST /calendar/v3/freeBusy`
451
+ - `GET /drive/v3/files`, `GET /drive/v3/files/:fileId`, `POST /drive/v3/files`, `PATCH /drive/v3/files/:fileId`, `PUT /drive/v3/files/:fileId`, `POST /upload/drive/v3/files`
452
+
453
+ The Google plugin still does not cover every Google API edge case, but Gmail, Calendar, and Drive now have enough mutable surface to support realistic local automation flows without stuffing everything into static seed config.
334
454
 
335
455
  ## Architecture
336
456
 
337
457
  ```
338
458
  packages/
339
459
  emulate/ # CLI entry point (commander)
340
- @internal/
460
+ @emulators/
341
461
  core/ # HTTP server, in-memory store, plugin interface, middleware
342
462
  vercel/ # Vercel API service
343
463
  github/ # GitHub API service
344
- google/ # Google OAuth 2.0 / OIDC
464
+ google/ # Google OAuth 2.0 / OIDC + Gmail, Calendar, and Drive APIs
345
465
  apps/
346
466
  web/ # Documentation site (Next.js)
347
467
  ```
package/dist/api.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ declare const SERVICE_NAME_LIST: readonly ["vercel", "github", "google", "slack", "apple", "microsoft", "aws"];
2
+ type ServiceName = (typeof SERVICE_NAME_LIST)[number];
3
+
4
+ interface SeedConfig {
5
+ tokens?: Record<string, {
6
+ login: string;
7
+ scopes?: string[];
8
+ }>;
9
+ [service: string]: unknown;
10
+ }
11
+ interface EmulatorOptions {
12
+ service: ServiceName;
13
+ port?: number;
14
+ seed?: SeedConfig;
15
+ }
16
+ interface Emulator {
17
+ url: string;
18
+ reset(): void;
19
+ close(): Promise<void>;
20
+ }
21
+ declare function createEmulator(options: EmulatorOptions): Promise<Emulator>;
22
+
23
+ export { type Emulator, type EmulatorOptions, type SeedConfig, type ServiceName, createEmulator };