emulate 0.2.0 → 0.4.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/LICENSE +202 -0
- package/README.md +386 -9
- package/dist/api.d.ts +3 -13
- package/dist/api.js +707 -17186
- package/dist/api.js.map +1 -1
- package/dist/chunk-D6EKRYGP.js +1615 -0
- package/dist/chunk-D6EKRYGP.js.map +1 -0
- package/dist/chunk-TEPNEZ63.js +2143 -0
- package/dist/chunk-TEPNEZ63.js.map +1 -0
- package/dist/dist-6EW7SSOZ.js +3641 -0
- package/dist/dist-6EW7SSOZ.js.map +1 -0
- package/dist/dist-6JFNJPUU.js +626 -0
- package/dist/dist-6JFNJPUU.js.map +1 -0
- package/dist/dist-B674PYKV.js +961 -0
- package/dist/dist-B674PYKV.js.map +1 -0
- package/dist/dist-G7WQPZ3Y.js +1287 -0
- package/dist/dist-G7WQPZ3Y.js.map +1 -0
- package/dist/dist-H6JYGQM4.js +10586 -0
- package/dist/dist-H6JYGQM4.js.map +1 -0
- package/dist/dist-OTJZRQ3Q.js +1956 -0
- package/dist/dist-OTJZRQ3Q.js.map +1 -0
- package/dist/dist-QMOJM6DV.js +774 -0
- package/dist/dist-QMOJM6DV.js.map +1 -0
- package/dist/dist-RDFBZ5O6.js +2684 -0
- package/dist/dist-RDFBZ5O6.js.map +1 -0
- package/dist/dist-RMK3BS5M.js +843 -0
- package/dist/dist-RMK3BS5M.js.map +1 -0
- package/dist/dist-VVXVP5EZ.js +1199 -0
- package/dist/dist-VVXVP5EZ.js.map +1 -0
- package/dist/dist-YOVM5HEY.js +932 -0
- package/dist/dist-YOVM5HEY.js.map +1 -0
- package/dist/index.js +740 -17353
- package/dist/index.js.map +1 -1
- package/package.json +21 -14
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to the Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by the Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding any notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. Please also get an informed
|
|
186
|
+
understanding of your current status in the "Work made for hire"
|
|
187
|
+
or other employer IP clauses in your jurisdiction and contracts
|
|
188
|
+
before applying.
|
|
189
|
+
|
|
190
|
+
Copyright 2025 Chris Tate
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -13,6 +13,10 @@ All services start with sensible defaults. No config file needed:
|
|
|
13
13
|
- **Vercel** on `http://localhost:4000`
|
|
14
14
|
- **GitHub** on `http://localhost:4001`
|
|
15
15
|
- **Google** on `http://localhost:4002`
|
|
16
|
+
- **Slack** on `http://localhost:4003`
|
|
17
|
+
- **Apple** on `http://localhost:4004`
|
|
18
|
+
- **Microsoft** on `http://localhost:4005`
|
|
19
|
+
- **AWS** on `http://localhost:4006`
|
|
16
20
|
|
|
17
21
|
## CLI
|
|
18
22
|
|
|
@@ -84,8 +88,8 @@ beforeAll(async () => {
|
|
|
84
88
|
createEmulator({ service: 'github', port: 4001 }),
|
|
85
89
|
createEmulator({ service: 'vercel', port: 4002 }),
|
|
86
90
|
])
|
|
87
|
-
process.env.
|
|
88
|
-
process.env.
|
|
91
|
+
process.env.GITHUB_EMULATOR_URL = github.url
|
|
92
|
+
process.env.VERCEL_EMULATOR_URL = vercel.url
|
|
89
93
|
})
|
|
90
94
|
|
|
91
95
|
afterEach(() => { github.reset(); vercel.reset() })
|
|
@@ -96,7 +100,7 @@ afterAll(() => Promise.all([github.close(), vercel.close()]))
|
|
|
96
100
|
|
|
97
101
|
| Option | Default | Description |
|
|
98
102
|
|--------|---------|-------------|
|
|
99
|
-
| `service` | *(required)* | Service
|
|
103
|
+
| `service` | *(required)* | Service name: `'vercel'`, `'github'`, `'google'`, `'slack'`, `'apple'`, `'microsoft'`, or `'aws'` |
|
|
100
104
|
| `port` | `4000` | Port for the HTTP server |
|
|
101
105
|
| `seed` | none | Inline seed data (same shape as YAML config) |
|
|
102
106
|
|
|
@@ -110,7 +114,7 @@ afterAll(() => Promise.all([github.close(), vercel.close()]))
|
|
|
110
114
|
|
|
111
115
|
## Configuration
|
|
112
116
|
|
|
113
|
-
Configuration is optional.
|
|
117
|
+
Configuration is optional. The CLI auto-detects config files in this order: `emulate.config.yaml` / `.yml`, `emulate.config.json`, `service-emulator.config.yaml` / `.yml`, `service-emulator.config.json`. Or pass `--seed <file>` explicitly. Run `emulate init` to generate a starter file.
|
|
114
118
|
|
|
115
119
|
```yaml
|
|
116
120
|
tokens:
|
|
@@ -154,6 +158,102 @@ google:
|
|
|
154
158
|
client_secret: GOCSPX-secret
|
|
155
159
|
redirect_uris:
|
|
156
160
|
- http://localhost:3000/api/auth/callback/google
|
|
161
|
+
labels:
|
|
162
|
+
- id: Label_ops
|
|
163
|
+
user_email: testuser@example.com
|
|
164
|
+
name: Ops/Review
|
|
165
|
+
color_background: "#DDEEFF"
|
|
166
|
+
color_text: "#111111"
|
|
167
|
+
messages:
|
|
168
|
+
- id: msg_welcome
|
|
169
|
+
user_email: testuser@example.com
|
|
170
|
+
from: welcome@example.com
|
|
171
|
+
to: testuser@example.com
|
|
172
|
+
subject: Welcome to the Gmail emulator
|
|
173
|
+
body_text: You can now test Gmail, Calendar, and Drive flows locally.
|
|
174
|
+
label_ids: [INBOX, UNREAD, CATEGORY_UPDATES]
|
|
175
|
+
calendars:
|
|
176
|
+
- id: primary
|
|
177
|
+
user_email: testuser@example.com
|
|
178
|
+
summary: testuser@example.com
|
|
179
|
+
primary: true
|
|
180
|
+
selected: true
|
|
181
|
+
time_zone: UTC
|
|
182
|
+
calendar_events:
|
|
183
|
+
- id: evt_kickoff
|
|
184
|
+
user_email: testuser@example.com
|
|
185
|
+
calendar_id: primary
|
|
186
|
+
summary: Project Kickoff
|
|
187
|
+
start_date_time: 2025-01-10T09:00:00.000Z
|
|
188
|
+
end_date_time: 2025-01-10T09:30:00.000Z
|
|
189
|
+
drive_items:
|
|
190
|
+
- id: drv_docs
|
|
191
|
+
user_email: testuser@example.com
|
|
192
|
+
name: Docs
|
|
193
|
+
mime_type: application/vnd.google-apps.folder
|
|
194
|
+
parent_ids: [root]
|
|
195
|
+
|
|
196
|
+
slack:
|
|
197
|
+
team:
|
|
198
|
+
name: My Workspace
|
|
199
|
+
domain: my-workspace
|
|
200
|
+
users:
|
|
201
|
+
- name: developer
|
|
202
|
+
real_name: Developer
|
|
203
|
+
email: dev@example.com
|
|
204
|
+
channels:
|
|
205
|
+
- name: general
|
|
206
|
+
topic: General discussion
|
|
207
|
+
- name: random
|
|
208
|
+
topic: Random stuff
|
|
209
|
+
bots:
|
|
210
|
+
- name: my-bot
|
|
211
|
+
oauth_apps:
|
|
212
|
+
- client_id: "12345.67890"
|
|
213
|
+
client_secret: example_client_secret
|
|
214
|
+
name: My Slack App
|
|
215
|
+
redirect_uris:
|
|
216
|
+
- http://localhost:3000/api/auth/callback/slack
|
|
217
|
+
|
|
218
|
+
apple:
|
|
219
|
+
users:
|
|
220
|
+
- email: testuser@icloud.com
|
|
221
|
+
name: Test User
|
|
222
|
+
oauth_clients:
|
|
223
|
+
- client_id: com.example.app
|
|
224
|
+
team_id: TEAM001
|
|
225
|
+
name: My Apple App
|
|
226
|
+
redirect_uris:
|
|
227
|
+
- http://localhost:3000/api/auth/callback/apple
|
|
228
|
+
|
|
229
|
+
microsoft:
|
|
230
|
+
users:
|
|
231
|
+
- email: testuser@outlook.com
|
|
232
|
+
name: Test User
|
|
233
|
+
oauth_clients:
|
|
234
|
+
- client_id: example-client-id
|
|
235
|
+
client_secret: example-client-secret
|
|
236
|
+
name: My Microsoft App
|
|
237
|
+
redirect_uris:
|
|
238
|
+
- http://localhost:3000/api/auth/callback/microsoft-entra-id
|
|
239
|
+
|
|
240
|
+
aws:
|
|
241
|
+
region: us-east-1
|
|
242
|
+
s3:
|
|
243
|
+
buckets:
|
|
244
|
+
- name: my-app-bucket
|
|
245
|
+
- name: my-app-uploads
|
|
246
|
+
sqs:
|
|
247
|
+
queues:
|
|
248
|
+
- name: my-app-events
|
|
249
|
+
- name: my-app-dlq
|
|
250
|
+
iam:
|
|
251
|
+
users:
|
|
252
|
+
- user_name: developer
|
|
253
|
+
create_access_key: true
|
|
254
|
+
roles:
|
|
255
|
+
- role_name: lambda-execution-role
|
|
256
|
+
description: Role for Lambda function execution
|
|
157
257
|
```
|
|
158
258
|
|
|
159
259
|
## OAuth & Integrations
|
|
@@ -204,6 +304,8 @@ github:
|
|
|
204
304
|
contents: read
|
|
205
305
|
issues: write
|
|
206
306
|
events: [push, pull_request]
|
|
307
|
+
webhook_url: "http://localhost:3000/webhooks/github"
|
|
308
|
+
webhook_secret: "my-secret"
|
|
207
309
|
installations:
|
|
208
310
|
- installation_id: 100
|
|
209
311
|
account: my-org
|
|
@@ -212,6 +314,46 @@ github:
|
|
|
212
314
|
|
|
213
315
|
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.
|
|
214
316
|
|
|
317
|
+
**App webhook delivery**: When events occur on repos where a GitHub App is installed, the emulator mirrors real GitHub behavior:
|
|
318
|
+
- All webhook payloads (including repo and org hooks) include an `installation` field with `{ id, node_id }`.
|
|
319
|
+
- 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`.
|
|
320
|
+
|
|
321
|
+
### Slack OAuth Apps
|
|
322
|
+
|
|
323
|
+
```yaml
|
|
324
|
+
slack:
|
|
325
|
+
oauth_apps:
|
|
326
|
+
- client_id: "12345.67890"
|
|
327
|
+
client_secret: "example_client_secret"
|
|
328
|
+
name: "My Slack App"
|
|
329
|
+
redirect_uris:
|
|
330
|
+
- "http://localhost:3000/api/auth/callback/slack"
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Apple OAuth Clients
|
|
334
|
+
|
|
335
|
+
```yaml
|
|
336
|
+
apple:
|
|
337
|
+
oauth_clients:
|
|
338
|
+
- client_id: "com.example.app"
|
|
339
|
+
team_id: "TEAM001"
|
|
340
|
+
name: "My Apple App"
|
|
341
|
+
redirect_uris:
|
|
342
|
+
- "http://localhost:3000/api/auth/callback/apple"
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Microsoft OAuth Clients
|
|
346
|
+
|
|
347
|
+
```yaml
|
|
348
|
+
microsoft:
|
|
349
|
+
oauth_clients:
|
|
350
|
+
- client_id: "example-client-id"
|
|
351
|
+
client_secret: "example-client-secret"
|
|
352
|
+
name: "My Microsoft App"
|
|
353
|
+
redirect_uris:
|
|
354
|
+
- "http://localhost:3000/api/auth/callback/microsoft-entra-id"
|
|
355
|
+
```
|
|
356
|
+
|
|
215
357
|
## Vercel API
|
|
216
358
|
|
|
217
359
|
Every endpoint below is fully stateful with Vercel-style JSON responses and cursor-based pagination.
|
|
@@ -381,26 +523,251 @@ Every endpoint below is fully stateful. Creates, updates, and deletes persist in
|
|
|
381
523
|
- `GET /zen` - random zen phrase
|
|
382
524
|
- `GET /versions` - API versions
|
|
383
525
|
|
|
384
|
-
## Google
|
|
526
|
+
## Google OAuth + Gmail, Calendar, and Drive APIs
|
|
385
527
|
|
|
386
|
-
OAuth 2.0
|
|
528
|
+
OAuth 2.0, OpenID Connect, and mutable Google Workspace-style surfaces for local inbox, calendar, and drive flows.
|
|
387
529
|
|
|
388
530
|
- `GET /o/oauth2/v2/auth` - authorization endpoint
|
|
389
|
-
- `POST /oauth2/
|
|
531
|
+
- `POST /oauth2/token` - token exchange
|
|
390
532
|
- `GET /oauth2/v2/userinfo` - get user info
|
|
391
533
|
- `GET /.well-known/openid-configuration` - OIDC discovery document
|
|
392
534
|
- `GET /oauth2/v3/certs` - JSON Web Key Set (JWKS)
|
|
535
|
+
- `GET /gmail/v1/users/:userId/messages` - list messages with `q`, `labelIds`, `maxResults`, and `pageToken`
|
|
536
|
+
- `GET /gmail/v1/users/:userId/messages/:id` - fetch a Gmail-style message payload in `full`, `metadata`, `minimal`, or `raw` formats
|
|
537
|
+
- `GET /gmail/v1/users/:userId/messages/:messageId/attachments/:id` - fetch attachment bodies
|
|
538
|
+
- `POST /gmail/v1/users/:userId/messages/send` - create sent mail from `raw` MIME or structured fields
|
|
539
|
+
- `POST /gmail/v1/users/:userId/messages/import` - import inbox mail
|
|
540
|
+
- `POST /gmail/v1/users/:userId/messages` - insert a message directly
|
|
541
|
+
- `POST /gmail/v1/users/:userId/messages/:id/modify` - add/remove labels on one message
|
|
542
|
+
- `POST /gmail/v1/users/:userId/messages/batchModify` - add/remove labels across many messages
|
|
543
|
+
- `POST /gmail/v1/users/:userId/messages/:id/trash` and `POST /gmail/v1/users/:userId/messages/:id/untrash`
|
|
544
|
+
- `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`
|
|
545
|
+
- `POST /gmail/v1/users/:userId/threads/:id/modify` - add/remove labels across a thread
|
|
546
|
+
- `GET /gmail/v1/users/:userId/threads` and `GET /gmail/v1/users/:userId/threads/:id`
|
|
547
|
+
- `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`
|
|
548
|
+
- `GET /gmail/v1/users/:userId/history`, `POST /gmail/v1/users/:userId/watch`, `POST /gmail/v1/users/:userId/stop`
|
|
549
|
+
- `GET /gmail/v1/users/:userId/settings/filters`, `POST /gmail/v1/users/:userId/settings/filters`, `DELETE /gmail/v1/users/:userId/settings/filters/:id`
|
|
550
|
+
- `GET /gmail/v1/users/:userId/settings/forwardingAddresses`, `GET /gmail/v1/users/:userId/settings/sendAs`
|
|
551
|
+
- `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`
|
|
552
|
+
- `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`
|
|
553
|
+
|
|
554
|
+
## Slack API
|
|
555
|
+
|
|
556
|
+
Fully stateful Slack Web API emulation with channels, messages, threads, reactions, OAuth v2, and incoming webhooks.
|
|
557
|
+
|
|
558
|
+
### Auth & Chat
|
|
559
|
+
- `POST /api/auth.test` - test authentication
|
|
560
|
+
- `POST /api/chat.postMessage` - post message (supports threads via `thread_ts`)
|
|
561
|
+
- `POST /api/chat.update` - update message
|
|
562
|
+
- `POST /api/chat.delete` - delete message
|
|
563
|
+
- `POST /api/chat.meMessage` - /me message
|
|
564
|
+
|
|
565
|
+
### Conversations
|
|
566
|
+
- `POST /api/conversations.list` - list channels (cursor pagination)
|
|
567
|
+
- `POST /api/conversations.info` - get channel info
|
|
568
|
+
- `POST /api/conversations.create` - create channel
|
|
569
|
+
- `POST /api/conversations.history` - channel history
|
|
570
|
+
- `POST /api/conversations.replies` - thread replies
|
|
571
|
+
- `POST /api/conversations.join` / `conversations.leave` - join/leave
|
|
572
|
+
- `POST /api/conversations.members` - list members
|
|
573
|
+
|
|
574
|
+
### Users & Reactions
|
|
575
|
+
- `POST /api/users.list` - list users (cursor pagination)
|
|
576
|
+
- `POST /api/users.info` - get user info
|
|
577
|
+
- `POST /api/users.lookupByEmail` - lookup by email
|
|
578
|
+
- `POST /api/reactions.add` / `reactions.remove` / `reactions.get` - manage reactions
|
|
579
|
+
|
|
580
|
+
### Team, Bots & Webhooks
|
|
581
|
+
- `POST /api/team.info` - workspace info
|
|
582
|
+
- `POST /api/bots.info` - bot info
|
|
583
|
+
- `POST /services/:teamId/:botId/:webhookId` - incoming webhook
|
|
584
|
+
|
|
585
|
+
### OAuth
|
|
586
|
+
- `GET /oauth/v2/authorize` - authorization (shows user picker)
|
|
587
|
+
- `POST /api/oauth.v2.access` - token exchange
|
|
588
|
+
|
|
589
|
+
## Apple Sign In
|
|
590
|
+
|
|
591
|
+
Sign in with Apple emulation with authorization code flow, PKCE support, RS256 ID tokens, and OIDC discovery.
|
|
592
|
+
|
|
593
|
+
- `GET /.well-known/openid-configuration` - OIDC discovery document
|
|
594
|
+
- `GET /auth/keys` - JSON Web Key Set (JWKS)
|
|
595
|
+
- `GET /auth/authorize` - authorization endpoint (shows user picker)
|
|
596
|
+
- `POST /auth/token` - token exchange (authorization code and refresh token grants)
|
|
597
|
+
- `POST /auth/revoke` - token revocation
|
|
598
|
+
|
|
599
|
+
## Microsoft Entra ID
|
|
600
|
+
|
|
601
|
+
Microsoft Entra ID (Azure AD) v2.0 OAuth 2.0 and OpenID Connect emulation with authorization code flow, PKCE, client credentials, RS256 ID tokens, and OIDC discovery.
|
|
602
|
+
|
|
603
|
+
- `GET /.well-known/openid-configuration` - OIDC discovery document
|
|
604
|
+
- `GET /:tenant/v2.0/.well-known/openid-configuration` - tenant-scoped OIDC discovery
|
|
605
|
+
- `GET /discovery/v2.0/keys` - JSON Web Key Set (JWKS)
|
|
606
|
+
- `GET /oauth2/v2.0/authorize` - authorization endpoint (shows user picker)
|
|
607
|
+
- `POST /oauth2/v2.0/token` - token exchange (authorization code, refresh token, client credentials)
|
|
608
|
+
- `GET /oidc/userinfo` - OpenID Connect user info
|
|
609
|
+
- `GET /v1.0/me` - Microsoft Graph user profile
|
|
610
|
+
- `GET /oauth2/v2.0/logout` - end session / logout
|
|
611
|
+
- `POST /oauth2/v2.0/revoke` - token revocation
|
|
612
|
+
|
|
613
|
+
## AWS
|
|
614
|
+
|
|
615
|
+
S3, SQS, IAM, and STS emulation with REST-style S3 paths and query-style SQS/IAM/STS endpoints. All responses use AWS-compatible XML.
|
|
616
|
+
|
|
617
|
+
### S3
|
|
618
|
+
- `GET /s3/` - list all buckets
|
|
619
|
+
- `PUT /s3/:bucket` - create bucket
|
|
620
|
+
- `DELETE /s3/:bucket` - delete bucket
|
|
621
|
+
- `HEAD /s3/:bucket` - check existence
|
|
622
|
+
- `GET /s3/:bucket` - list objects (prefix, delimiter, max-keys)
|
|
623
|
+
- `PUT /s3/:bucket/:key` - put object (supports copy via `x-amz-copy-source`)
|
|
624
|
+
- `GET /s3/:bucket/:key` - get object
|
|
625
|
+
- `HEAD /s3/:bucket/:key` - head object
|
|
626
|
+
- `DELETE /s3/:bucket/:key` - delete object
|
|
627
|
+
|
|
628
|
+
### SQS
|
|
629
|
+
All operations via `POST /sqs/` with `Action` parameter:
|
|
630
|
+
- `CreateQueue`, `ListQueues`, `GetQueueUrl`, `GetQueueAttributes`
|
|
631
|
+
- `SendMessage`, `ReceiveMessage`, `DeleteMessage`
|
|
632
|
+
- `PurgeQueue`, `DeleteQueue`
|
|
633
|
+
|
|
634
|
+
### IAM
|
|
635
|
+
All operations via `POST /iam/` with `Action` parameter:
|
|
636
|
+
- `CreateUser`, `GetUser`, `ListUsers`, `DeleteUser`
|
|
637
|
+
- `CreateAccessKey`, `ListAccessKeys`, `DeleteAccessKey`
|
|
638
|
+
- `CreateRole`, `GetRole`, `ListRoles`, `DeleteRole`
|
|
639
|
+
|
|
640
|
+
### STS
|
|
641
|
+
All operations via `POST /sts/` with `Action` parameter:
|
|
642
|
+
- `GetCallerIdentity`, `AssumeRole`
|
|
643
|
+
|
|
644
|
+
## Next.js Integration
|
|
645
|
+
|
|
646
|
+
Embed emulators directly in your Next.js app so they run on the same origin. This solves the Vercel preview deployment problem where OAuth callback URLs change with every deployment.
|
|
647
|
+
|
|
648
|
+
### Install
|
|
649
|
+
|
|
650
|
+
```bash
|
|
651
|
+
npm install @emulators/adapter-next @emulators/github @emulators/google
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
Only install the emulators you need. Each `@emulators/*` package is published independently.
|
|
655
|
+
|
|
656
|
+
### Route handler
|
|
657
|
+
|
|
658
|
+
Create a catch-all route that serves emulator traffic:
|
|
659
|
+
|
|
660
|
+
```typescript
|
|
661
|
+
// app/emulate/[...path]/route.ts
|
|
662
|
+
import { createEmulateHandler } from '@emulators/adapter-next'
|
|
663
|
+
import * as github from '@emulators/github'
|
|
664
|
+
import * as google from '@emulators/google'
|
|
665
|
+
|
|
666
|
+
export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({
|
|
667
|
+
services: {
|
|
668
|
+
github: {
|
|
669
|
+
emulator: github,
|
|
670
|
+
seed: {
|
|
671
|
+
users: [{ login: 'octocat', name: 'The Octocat' }],
|
|
672
|
+
repos: [{ owner: 'octocat', name: 'hello-world', auto_init: true }],
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
google: {
|
|
676
|
+
emulator: google,
|
|
677
|
+
seed: {
|
|
678
|
+
users: [{ email: 'test@example.com', name: 'Test User' }],
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
},
|
|
682
|
+
})
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
### Auth.js / NextAuth configuration
|
|
686
|
+
|
|
687
|
+
Point your provider at the emulator paths on the same origin:
|
|
688
|
+
|
|
689
|
+
```typescript
|
|
690
|
+
import GitHub from 'next-auth/providers/github'
|
|
691
|
+
|
|
692
|
+
const baseUrl = process.env.VERCEL_URL
|
|
693
|
+
? `https://${process.env.VERCEL_URL}`
|
|
694
|
+
: 'http://localhost:3000'
|
|
695
|
+
|
|
696
|
+
GitHub({
|
|
697
|
+
clientId: 'any-value',
|
|
698
|
+
clientSecret: 'any-value',
|
|
699
|
+
authorization: { url: `${baseUrl}/emulate/github/login/oauth/authorize` },
|
|
700
|
+
token: { url: `${baseUrl}/emulate/github/login/oauth/access_token` },
|
|
701
|
+
userinfo: { url: `${baseUrl}/emulate/github/user` },
|
|
702
|
+
})
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
No `oauth_apps` need to be seeded. When none are configured, the emulator skips `client_id`, `client_secret`, and `redirect_uri` validation.
|
|
706
|
+
|
|
707
|
+
### Font files in serverless
|
|
708
|
+
|
|
709
|
+
Emulator UI pages use bundled fonts. Wrap your Next.js config to include them in the serverless trace:
|
|
710
|
+
|
|
711
|
+
```typescript
|
|
712
|
+
// next.config.mjs
|
|
713
|
+
import { withEmulate } from '@emulators/adapter-next'
|
|
714
|
+
|
|
715
|
+
export default withEmulate({
|
|
716
|
+
// your normal Next.js config
|
|
717
|
+
})
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
If you mount the catch-all at a custom path, pass the matching prefix:
|
|
721
|
+
|
|
722
|
+
```typescript
|
|
723
|
+
export default withEmulate(nextConfig, { routePrefix: '/api/emulate' })
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### Persistence
|
|
727
|
+
|
|
728
|
+
By default, emulator state is in-memory and resets on every cold start. To persist state across restarts, pass a `persistence` adapter:
|
|
729
|
+
|
|
730
|
+
```typescript
|
|
731
|
+
import { createEmulateHandler } from '@emulators/adapter-next'
|
|
732
|
+
import * as github from '@emulators/github'
|
|
733
|
+
|
|
734
|
+
const kvAdapter = {
|
|
735
|
+
async load() { return await kv.get('emulate-state') },
|
|
736
|
+
async save(data: string) { await kv.set('emulate-state', data) },
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({
|
|
740
|
+
services: { github: { emulator: github } },
|
|
741
|
+
persistence: kvAdapter,
|
|
742
|
+
})
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
For local development, `@emulators/core` ships `filePersistence`:
|
|
746
|
+
|
|
747
|
+
```typescript
|
|
748
|
+
import { filePersistence } from '@emulators/core'
|
|
749
|
+
|
|
750
|
+
// ...
|
|
751
|
+
persistence: filePersistence('.emulate/state.json'),
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
The persistence adapter is called on cold start (load) and after every mutating request (save). Saves are serialized via an internal queue to prevent race conditions.
|
|
393
755
|
|
|
394
756
|
## Architecture
|
|
395
757
|
|
|
396
758
|
```
|
|
397
759
|
packages/
|
|
398
760
|
emulate/ # CLI entry point (commander)
|
|
399
|
-
|
|
761
|
+
@emulators/
|
|
400
762
|
core/ # HTTP server, in-memory store, plugin interface, middleware
|
|
763
|
+
adapter-next/ # Next.js App Router integration
|
|
401
764
|
vercel/ # Vercel API service
|
|
402
765
|
github/ # GitHub API service
|
|
403
|
-
google/ # Google OAuth 2.0 / OIDC
|
|
766
|
+
google/ # Google OAuth 2.0 / OIDC + Gmail, Calendar, Drive
|
|
767
|
+
slack/ # Slack Web API, OAuth v2, incoming webhooks
|
|
768
|
+
apple/ # Apple Sign In / OIDC
|
|
769
|
+
microsoft/ # Microsoft Entra ID OAuth 2.0 / OIDC + Graph /me
|
|
770
|
+
aws/ # AWS S3, SQS, IAM, STS
|
|
404
771
|
apps/
|
|
405
772
|
web/ # Documentation site (Next.js)
|
|
406
773
|
```
|
|
@@ -414,3 +781,13 @@ Tokens are configured in the seed config and map to users. Pass them as `Authori
|
|
|
414
781
|
**Vercel**: All endpoints accept `teamId` or `slug` query params for team scoping. Pagination uses cursor-based `limit`/`since`/`until` with `pagination` response objects.
|
|
415
782
|
|
|
416
783
|
**GitHub**: Public repo endpoints work without auth. Private repos and write operations require a valid token. Pagination uses `page`/`per_page` with `Link` headers.
|
|
784
|
+
|
|
785
|
+
**Google**: Standard OAuth 2.0 authorization code flow. Configure clients in the seed config.
|
|
786
|
+
|
|
787
|
+
**Slack**: All Web API endpoints require `Authorization: Bearer <token>`. OAuth v2 flow with user picker UI.
|
|
788
|
+
|
|
789
|
+
**Apple**: OIDC authorization code flow with RS256 ID tokens. On first auth per user/client pair, a `user` JSON blob is included.
|
|
790
|
+
|
|
791
|
+
**Microsoft**: OIDC authorization code flow with PKCE support. Also supports client credentials grants. Microsoft Graph `/v1.0/me` available.
|
|
792
|
+
|
|
793
|
+
**AWS**: Bearer tokens or IAM access key credentials. Default key pair always seeded: `AKIAIOSFODNN7EXAMPLE` / `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`.
|