api-emulator 0.5.1 → 0.5.2

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
@@ -1,112 +1,130 @@
1
1
  <p align="center">
2
- <img src="./.README/cover.png" alt="api-emulator" width="1024" />
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 runs local, stateful API emulators for development, CI, and no-network sandboxes. The core package is a runtime plus a default plugin catalog; teams can load public, shared, or private provider-shaped plugins without forking the runtime.
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
- ## Install
11
+ ## Why use it?
14
12
 
15
- ```bash
16
- npm install api-emulator
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
- For one-off CLI usage:
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
- No API keys, Docker daemon, or external sandbox accounts are required for the default plugin catalog.
26
-
27
- ## CLI
24
+ Then point your app at the local provider URLs:
28
25
 
29
- ```bash
30
- # Start the default plugin catalog
31
- npx api-emulator
32
-
33
- # Start a subset
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
- # Generate a starter config
43
- npx api-emulator init
32
+ Use trusted local HTTPS names when your app needs browser compatible origins:
44
33
 
45
- # List default and external plugins
46
- npx api-emulator list
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
- GitHub https://github.api-emulator.localhost
53
- Stripe https://stripe.api-emulator.localhost
54
- Resend https://resend.api-emulator.localhost
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
- The CLI reads `API_EMULATOR_PORT`, `API_EMULATOR_BASE_URL`, and `PORTLESS_URL`. It also accepts `EMULATE_PORT`, `EMULATE_BASE_URL`, and `emulate.config.*` names as migration compatibility aliases.
44
+ Create starter config and list available services:
59
45
 
60
- ## Canonical API
46
+ ```bash
47
+ npx -p api-emulator api init
48
+ npx -p api-emulator api list
49
+ ```
61
50
 
62
- Prefer the programmatic runtime when writing tests:
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
- Each emulator instance exposes:
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
- - `url` for the advertised base URL
78
- - `reset()` to wipe state and replay seed data
79
- - `close()` to stop the local server
68
+ github.resetToFixture(fixture)
69
+ ```
80
70
 
81
71
  ## Plugins
82
72
 
83
- api-emulator is designed around a small runtime spine and provider-shaped plugins. The default providers are loaded from `packages/emulate/src/default-plugin-catalog.ts`; external plugin modules are normalized by `packages/emulate/src/external-plugin-adapter.ts`.
73
+ Install more providers from a public or internal plugin shelf:
84
74
 
85
- Public external plugins live at [`jsj/api-emulator-plugins`](https://github.com/jsj/api-emulator-plugins). Private teams can keep the same shape in internal repos.
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 '@emulators/core'
91
+ import type { ServicePlugin } from '@api-emulator/core'
96
92
 
97
93
  export const plugin: ServicePlugin = {
98
94
  name: 'internal-billing',
99
- register(app, store, webhooks, baseUrl) {
95
+ register(app) {
100
96
  app.get('/v1/customers', (c) => c.json({ data: [] }))
101
97
  },
102
98
  }
103
99
  ```
104
100
 
105
- Plugins can also export `label`, `endpoints`, `initConfig`, `seedFromConfig`, and `defaultFallback`. The runtime keeps plugin lifecycle in one service-runtime module, so CLI and programmatic usage share the same seed, reset, token, and close behavior.
101
+ ## Next.js embedded mode
102
+
103
+ ```bash
104
+ npm install @api-emulator/adapter-next @api-emulator/core
105
+ ```
106
+
107
+ ```ts
108
+ import { createEmulateHandler } 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 } = createEmulateHandler({
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
- This gives local routes like:
141
+ The CLI auto-detects `api-emulator.config.yaml`, `.yml`, and `.json`. Older `emulate.config.*` and `service-emulator.config.*` names still work as migration aliases.
187
142
 
188
- ```text
189
- /emulate/github/login/oauth/authorize
190
- /emulate/stripe/v1/checkout/sessions
191
- ```
192
-
193
- ## Production guidance
143
+ ## Examples
194
144
 
195
- - Treat emulators as test infrastructure, not production dependencies.
196
- - Keep real provider credentials out of seed files.
197
- - Use fake tokens, fake webhook secrets, and local-only API keys.
198
- - Prefer provider-shaped resource modules over app-specific happy-path stubs.
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
- Release publishing also uses `bun pm pack` before `npm publish` so workspace dependencies resolve in package tarballs.
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
- Provider concepts, not one app's current happy path. Model resources, state transitions, errors, inspect surfaces, and control hooks so multiple products can test against the same plugin.
270
- </details>
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
- declare const DEFAULT_PLUGIN_NAME_LIST: readonly ["vercel", "github", "google", "slack", "apple", "microsoft", "okta", "aws", "resend", "stripe", "mongoatlas", "clerk"];
2
- type ServiceName = (typeof DEFAULT_PLUGIN_NAME_LIST)[number];
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
  }