api-emulator 0.5.1 → 0.6.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 +79 -185
- package/dist/api.d.ts +8 -2
- package/dist/api.js +77 -422
- package/dist/api.js.map +1 -1
- package/dist/index.js +285 -472
- package/dist/index.js.map +1 -1
- package/package.json +8 -18
package/README.md
CHANGED
|
@@ -1,112 +1,130 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="
|
|
2
|
+
<img src="https://raw.githubusercontent.com/jsj/api-emulator/main/.README/cover.png" alt="api-emulator" width="1024" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">api-emulator</h1>
|
|
6
6
|
|
|
7
|
-
<p align="center">
|
|
8
|
-
Local API emulators you can run, share, and extend with plugins
|
|
9
|
-
</p>
|
|
7
|
+
<p align="center">Fake real APIs locally so your app can test integrations without touching production, sandboxes, or someone else's server.</p>
|
|
10
8
|
|
|
11
|
-
api-emulator
|
|
9
|
+
`api-emulator` is a local app store for fake APIs. Run GitHub, Stripe, Resend, and plugin powered providers on localhost, seed state, inspect behavior, reset data, and test your app in one place.
|
|
12
10
|
|
|
13
|
-
##
|
|
11
|
+
## Why use it?
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
- Test API integrations locally without real provider credentials.
|
|
14
|
+
- Run multiple fake services together, with shared state, auth, webhooks, seed data, and resets.
|
|
15
|
+
- Keep provider behavior in plugins so public, private, and internal APIs can live outside your app.
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
## Quick start
|
|
20
18
|
|
|
21
19
|
```bash
|
|
22
|
-
npx api-emulator
|
|
20
|
+
npx -p api-emulator api
|
|
21
|
+
npx -p api-emulator api --service github,stripe,resend
|
|
23
22
|
```
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
## CLI
|
|
24
|
+
Then point your app at the local provider URLs:
|
|
28
25
|
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
npx api-emulator --service github,stripe,resend
|
|
35
|
-
|
|
36
|
-
# Start on trusted local HTTPS names
|
|
37
|
-
npx api-emulator --portless
|
|
38
|
-
|
|
39
|
-
# Load a seed file
|
|
40
|
-
npx api-emulator --seed api-emulator.config.yaml
|
|
26
|
+
```text
|
|
27
|
+
http://localhost:4000/github
|
|
28
|
+
http://localhost:4000/stripe
|
|
29
|
+
http://localhost:4000/resend
|
|
30
|
+
```
|
|
41
31
|
|
|
42
|
-
|
|
43
|
-
npx api-emulator init
|
|
32
|
+
Use trusted local HTTPS names when your app needs browser compatible origins:
|
|
44
33
|
|
|
45
|
-
|
|
46
|
-
npx api-emulator
|
|
34
|
+
```bash
|
|
35
|
+
npx -p api-emulator api --service github,stripe,resend --portless
|
|
47
36
|
```
|
|
48
37
|
|
|
49
|
-
With `--portless`, each service gets a named local HTTPS URL:
|
|
50
|
-
|
|
51
38
|
```text
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
AWS https://aws.api-emulator.localhost
|
|
39
|
+
https://github.api-emulator.localhost
|
|
40
|
+
https://stripe.api-emulator.localhost
|
|
41
|
+
https://resend.api-emulator.localhost
|
|
56
42
|
```
|
|
57
43
|
|
|
58
|
-
|
|
44
|
+
Create starter config and list available services:
|
|
59
45
|
|
|
60
|
-
|
|
46
|
+
```bash
|
|
47
|
+
npx -p api-emulator api init
|
|
48
|
+
npx -p api-emulator api list
|
|
49
|
+
```
|
|
61
50
|
|
|
62
|
-
|
|
51
|
+
## Use in tests
|
|
63
52
|
|
|
64
53
|
```ts
|
|
65
54
|
import { createEmulator } from 'api-emulator'
|
|
66
55
|
|
|
67
56
|
const github = await createEmulator({ service: 'github', port: 4001 })
|
|
68
|
-
|
|
69
57
|
process.env.GITHUB_API_BASE = github.url
|
|
70
58
|
|
|
71
59
|
afterEach(() => github.reset())
|
|
72
60
|
afterAll(() => github.close())
|
|
73
61
|
```
|
|
74
62
|
|
|
75
|
-
|
|
63
|
+
Capture and replay a stable fixture after a stochastic or stateful run:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
const fixture = github.exportFixture({ metadata: { name: 'pull-request-flow' } })
|
|
76
67
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
- `close()` to stop the local server
|
|
68
|
+
github.resetToFixture(fixture)
|
|
69
|
+
```
|
|
80
70
|
|
|
81
71
|
## Plugins
|
|
82
72
|
|
|
83
|
-
|
|
73
|
+
Install more providers from a public or internal plugin shelf:
|
|
84
74
|
|
|
85
|
-
|
|
75
|
+
```bash
|
|
76
|
+
npx -p api-emulator api install posthog
|
|
77
|
+
npx -p api-emulator api install pepper --no-package-manager
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Or load a plugin file directly:
|
|
86
81
|
|
|
87
82
|
```bash
|
|
88
|
-
npx api-emulator --plugin ./api-emulator-plugins/@posthog/api-emulator.mjs --service posthog
|
|
89
|
-
npx api-emulator --plugin ./api-emulator-plugins/@github/api-emulator.mjs,./api-emulator-plugins/@apple/api-emulator.mjs
|
|
83
|
+
npx -p api-emulator api --plugin ./api-emulator-plugins/@posthog/api-emulator.mjs --service posthog
|
|
90
84
|
```
|
|
91
85
|
|
|
86
|
+
The installer auto discovers sibling `api-emulator-plugins` and `api-emulator-internal` checkouts. Set `API_EMULATOR_PLUGIN_CATALOGS=/path/to/shelf,/path/to/internal` to add more shelves.
|
|
87
|
+
|
|
92
88
|
A plugin exports a `ServicePlugin`:
|
|
93
89
|
|
|
94
90
|
```ts
|
|
95
|
-
import type { ServicePlugin } from '@
|
|
91
|
+
import type { ServicePlugin } from '@api-emulator/core'
|
|
96
92
|
|
|
97
93
|
export const plugin: ServicePlugin = {
|
|
98
94
|
name: 'internal-billing',
|
|
99
|
-
register(app
|
|
95
|
+
register(app) {
|
|
100
96
|
app.get('/v1/customers', (c) => c.json({ data: [] }))
|
|
101
97
|
},
|
|
102
98
|
}
|
|
103
99
|
```
|
|
104
100
|
|
|
105
|
-
|
|
101
|
+
## Next.js embedded mode
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install @api-emulator/adapter-next @api-emulator/core
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { createApiEmulatorHandler } from '@api-emulator/adapter-next'
|
|
109
|
+
import type { ServicePlugin } from '@api-emulator/core'
|
|
110
|
+
|
|
111
|
+
const internalPlugin: ServicePlugin = {
|
|
112
|
+
name: 'internal',
|
|
113
|
+
register(app) {
|
|
114
|
+
app.get('/health', (c) => c.json({ ok: true }))
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const { GET, POST, PUT, PATCH, DELETE } = createApiEmulatorHandler({
|
|
119
|
+
services: {
|
|
120
|
+
internal: { emulator: { plugin: internalPlugin } },
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
```
|
|
106
124
|
|
|
107
125
|
## Configuration
|
|
108
126
|
|
|
109
|
-
`api-emulator init` creates `api-emulator.config.yaml`.
|
|
127
|
+
`npx -p api-emulator api init` creates `api-emulator.config.yaml`.
|
|
110
128
|
|
|
111
129
|
```yaml
|
|
112
130
|
tokens:
|
|
@@ -118,92 +136,19 @@ github:
|
|
|
118
136
|
users:
|
|
119
137
|
- login: octocat
|
|
120
138
|
name: The Octocat
|
|
121
|
-
email: octocat@github.com
|
|
122
|
-
repos:
|
|
123
|
-
- owner: octocat
|
|
124
|
-
name: hello-world
|
|
125
|
-
auto_init: true
|
|
126
|
-
|
|
127
|
-
stripe:
|
|
128
|
-
products:
|
|
129
|
-
- id: prod_tshirt
|
|
130
|
-
name: T-shirt
|
|
131
|
-
prices:
|
|
132
|
-
- id: price_tshirt
|
|
133
|
-
product: prod_tshirt
|
|
134
|
-
unit_amount: 2500
|
|
135
|
-
currency: usd
|
|
136
|
-
|
|
137
|
-
resend:
|
|
138
|
-
apiKeys:
|
|
139
|
-
- re_test_key
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
The CLI auto-detects:
|
|
143
|
-
|
|
144
|
-
- `api-emulator.config.yaml`
|
|
145
|
-
- `api-emulator.config.yml`
|
|
146
|
-
- `api-emulator.config.json`
|
|
147
|
-
- `emulate.config.yaml`
|
|
148
|
-
- `emulate.config.yml`
|
|
149
|
-
- `emulate.config.json`
|
|
150
|
-
- `service-emulator.config.yaml`
|
|
151
|
-
- `service-emulator.config.yml`
|
|
152
|
-
- `service-emulator.config.json`
|
|
153
|
-
|
|
154
|
-
## Default plugin catalog
|
|
155
|
-
|
|
156
|
-
api-emulator ships with a default plugin catalog so first run is useful, but these providers are catalog entries rather than hard-coded runtime behavior:
|
|
157
|
-
|
|
158
|
-
- Vercel API
|
|
159
|
-
- GitHub REST, OAuth, Apps, Actions, checks, statuses, and webhooks
|
|
160
|
-
- Google OAuth, OIDC, Gmail, Calendar, and Drive
|
|
161
|
-
- Slack Web API, OAuth, channels, messages, users, and webhooks
|
|
162
|
-
- Apple Sign in with Apple and OIDC
|
|
163
|
-
- Microsoft Entra ID OAuth and OIDC
|
|
164
|
-
- Okta OAuth and OIDC
|
|
165
|
-
- Clerk auth surfaces
|
|
166
|
-
- AWS S3, SQS, IAM, and STS
|
|
167
|
-
- MongoDB Atlas Admin and Data API
|
|
168
|
-
- Resend email API with local inbox
|
|
169
|
-
- Stripe Checkout, customers, products, prices, payment intents, and webhooks
|
|
170
|
-
|
|
171
|
-
## Next.js embedded mode
|
|
172
|
-
|
|
173
|
-
Use `@emulators/adapter-next` to mount emulators inside a Next.js app on the same origin.
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
// app/emulate/[...path]/route.ts
|
|
177
|
-
import { createEmulateHandler } from '@emulators/adapter-next'
|
|
178
|
-
import { githubPlugin } from '@emulators/github'
|
|
179
|
-
import { stripePlugin } from '@emulators/stripe'
|
|
180
|
-
|
|
181
|
-
export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({
|
|
182
|
-
plugins: [githubPlugin, stripePlugin],
|
|
183
|
-
})
|
|
184
139
|
```
|
|
185
140
|
|
|
186
|
-
|
|
141
|
+
The CLI auto-detects `api-emulator.config.yaml`, `.yml`, and `.json`.
|
|
187
142
|
|
|
188
|
-
|
|
189
|
-
/emulate/github/login/oauth/authorize
|
|
190
|
-
/emulate/stripe/v1/checkout/sessions
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
## Production guidance
|
|
143
|
+
## Examples
|
|
194
144
|
|
|
195
|
-
-
|
|
196
|
-
-
|
|
197
|
-
-
|
|
198
|
-
-
|
|
199
|
-
- Keep route handlers thin. Put depth in reusable Modules, Interfaces, and adapters.
|
|
200
|
-
- Keep private team plugins outside the public runtime until the interface proves stable.
|
|
201
|
-
- Use inspect and control endpoints for deterministic tests instead of sleeping or polling real systems.
|
|
145
|
+
- [`examples/oauth`](./examples/oauth)
|
|
146
|
+
- [`examples/nextjs-embedded`](./examples/nextjs-embedded)
|
|
147
|
+
- [`examples/resend-magic-link`](./examples/resend-magic-link)
|
|
148
|
+
- [`examples/stripe-checkout`](./examples/stripe-checkout)
|
|
202
149
|
|
|
203
150
|
## Development
|
|
204
151
|
|
|
205
|
-
This monorepo uses Bun.
|
|
206
|
-
|
|
207
152
|
```bash
|
|
208
153
|
bun install
|
|
209
154
|
bun run build
|
|
@@ -213,61 +158,10 @@ bun run lint
|
|
|
213
158
|
bun run test
|
|
214
159
|
```
|
|
215
160
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
## Examples
|
|
219
|
-
These examples use the default plugin catalog. For examples of external provider plugins you can load with `--plugin`, see [jsj/api-emulator-plugins](https://github.com/jsj/api-emulator-plugins).
|
|
220
|
-
|
|
221
|
-
See:
|
|
222
|
-
|
|
223
|
-
- [`examples/oauth`](./examples/oauth)
|
|
224
|
-
- [`examples/nextjs-embedded`](./examples/nextjs-embedded)
|
|
225
|
-
- [`examples/resend-magic-link`](./examples/resend-magic-link)
|
|
226
|
-
- [`examples/stripe-checkout`](./examples/stripe-checkout)
|
|
227
|
-
|
|
228
|
-
## Features
|
|
229
|
-
|
|
230
|
-
- Stateful local provider APIs for development and CI
|
|
231
|
-
- One CLI that can start many providers at predictable ports
|
|
232
|
-
- Trusted local HTTPS names through portless
|
|
233
|
-
- External plugin loading from files or package names through a dedicated adapter seam
|
|
234
|
-
- YAML and JSON seed data
|
|
235
|
-
- Programmatic test API with reset and close hooks
|
|
236
|
-
- Shared core store, auth, persistence, UI, webhooks, and inspect pages
|
|
237
|
-
- Next.js embedded mode for same-origin OAuth and SDK flows
|
|
238
|
-
- Public, shared, default-catalog, and private plugin workflows
|
|
239
|
-
|
|
240
|
-
## FAQ
|
|
241
|
-
|
|
242
|
-
<details>
|
|
243
|
-
<summary>Is this a fork of emulate?</summary>
|
|
244
|
-
|
|
245
|
-
Yes. api-emulator is a hard fork with a new identity and a stronger focus on the plugin-spine model: a small runtime plus many provider-shaped plugins.
|
|
246
|
-
</details>
|
|
247
|
-
|
|
248
|
-
<details>
|
|
249
|
-
<summary>Why not just mock fetch calls?</summary>
|
|
250
|
-
|
|
251
|
-
SDKs, OAuth redirects, webhooks, pagination, retries, XML wire formats, and provider state usually live below your app code. api-emulator runs those protocol surfaces locally so tests exercise the integration seam instead of a shallow stub.
|
|
252
|
-
</details>
|
|
253
|
-
|
|
254
|
-
<details>
|
|
255
|
-
<summary>Can teams keep private emulators?</summary>
|
|
256
|
-
|
|
257
|
-
Yes. Private and internal plugins are a first-class workflow. Load them with `--plugin`, keep them in a separate repo, and share only the runtime spine publicly.
|
|
258
|
-
</details>
|
|
259
|
-
|
|
260
|
-
<details>
|
|
261
|
-
<summary>Does api-emulator still support old emulate config names?</summary>
|
|
262
|
-
|
|
263
|
-
Yes. `emulate.config.*`, `EMULATE_PORT`, and `EMULATE_BASE_URL` still work as migration compatibility aliases.
|
|
264
|
-
</details>
|
|
265
|
-
|
|
266
|
-
<details>
|
|
267
|
-
<summary>What should a good plugin emulate?</summary>
|
|
161
|
+
## Links
|
|
268
162
|
|
|
269
|
-
|
|
270
|
-
|
|
163
|
+
- [`jsj/api-emulator-plugins`](https://github.com/jsj/api-emulator-plugins)
|
|
164
|
+
- [`api-emulator` on npm](https://www.npmjs.com/package/api-emulator)
|
|
271
165
|
|
|
272
166
|
## License
|
|
273
167
|
|
package/dist/api.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { StoreSnapshot, FixtureSource, StoreFixtureOptions, StoreFixture } from '@api-emulator/core';
|
|
2
|
+
export { FixtureInteraction, FixtureSource, StoreFixture, StoreFixtureOptions, StoreSnapshot } from '@api-emulator/core';
|
|
3
|
+
|
|
4
|
+
type ServiceName = string;
|
|
3
5
|
|
|
4
6
|
interface SeedConfig {
|
|
5
7
|
tokens?: Record<string, {
|
|
@@ -18,6 +20,10 @@ interface EmulatorOptions {
|
|
|
18
20
|
}
|
|
19
21
|
interface Emulator {
|
|
20
22
|
url: string;
|
|
23
|
+
snapshot(): StoreSnapshot;
|
|
24
|
+
restore(fixture: FixtureSource): void;
|
|
25
|
+
exportFixture(options?: StoreFixtureOptions): StoreFixture;
|
|
26
|
+
resetToFixture(fixture: FixtureSource): void;
|
|
21
27
|
reset(): void;
|
|
22
28
|
close(): Promise<void>;
|
|
23
29
|
}
|