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/dist/api.js
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
// src/external-plugin-adapter.ts
|
|
2
2
|
import { isAbsolute, resolve } from "path";
|
|
3
|
+
|
|
4
|
+
// src/plugin-manifest.ts
|
|
5
|
+
function readPluginManifest(mod) {
|
|
6
|
+
return mod.manifest ?? {};
|
|
7
|
+
}
|
|
8
|
+
function validatePluginManifest(manifest, pluginName) {
|
|
9
|
+
if (manifest.name && manifest.name !== pluginName) {
|
|
10
|
+
throw new Error(`Plugin manifest name "${manifest.name}" does not match plugin name "${pluginName}"`);
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
...manifest,
|
|
14
|
+
label: manifest.label ?? `${pluginName} (external plugin)`,
|
|
15
|
+
endpoints: manifest.endpoints ?? "",
|
|
16
|
+
initConfig: manifest.initConfig ?? {}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/external-plugin-adapter.ts
|
|
3
21
|
async function loadExternalPluginModule(specifier) {
|
|
4
22
|
const modulePath = specifier.startsWith(".") || isAbsolute(specifier) ? resolve(specifier) : specifier;
|
|
5
23
|
const mod = await import(modulePath);
|
|
@@ -8,10 +26,12 @@ async function loadExternalPluginModule(specifier) {
|
|
|
8
26
|
throw new Error(`Plugin "${specifier}" must export a ServicePlugin (as "plugin" or default export)`);
|
|
9
27
|
}
|
|
10
28
|
const name = plugin.name;
|
|
29
|
+
const manifest = validatePluginManifest(readPluginManifest(mod), name);
|
|
11
30
|
return {
|
|
12
31
|
name,
|
|
13
|
-
label:
|
|
14
|
-
endpoints:
|
|
32
|
+
label: manifest.label,
|
|
33
|
+
endpoints: manifest.endpoints,
|
|
34
|
+
manifest,
|
|
15
35
|
async load() {
|
|
16
36
|
return {
|
|
17
37
|
plugin,
|
|
@@ -19,428 +39,38 @@ async function loadExternalPluginModule(specifier) {
|
|
|
19
39
|
};
|
|
20
40
|
},
|
|
21
41
|
defaultFallback: mod.defaultFallback ?? (() => ({ login: "admin", id: 1, scopes: [] })),
|
|
22
|
-
initConfig:
|
|
42
|
+
initConfig: manifest.initConfig
|
|
23
43
|
};
|
|
24
44
|
}
|
|
25
45
|
|
|
26
|
-
// src/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
},
|
|
40
|
-
initConfig: {
|
|
41
|
-
vercel: {
|
|
42
|
-
users: [{ username: "developer", name: "Developer", email: "dev@example.com" }],
|
|
43
|
-
teams: [{ slug: "my-team", name: "My Team" }],
|
|
44
|
-
projects: [{ name: "my-app", team: "my-team", framework: "nextjs" }],
|
|
45
|
-
integrations: [
|
|
46
|
-
{
|
|
47
|
-
client_id: "oac_example_client_id",
|
|
48
|
-
client_secret: "example_client_secret",
|
|
49
|
-
name: "My Vercel App",
|
|
50
|
-
redirect_uris: ["http://localhost:3000/api/auth/callback/vercel"]
|
|
51
|
-
}
|
|
52
|
-
]
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
github: {
|
|
57
|
-
name: "github",
|
|
58
|
-
label: "GitHub REST API emulator",
|
|
59
|
-
endpoints: "users, repos, issues, PRs, comments, reviews, labels, milestones, branches, git data, orgs, teams, releases, webhooks, search, actions, checks, rate limit",
|
|
60
|
-
async load() {
|
|
61
|
-
const mod = await import("@emulators/github");
|
|
62
|
-
return {
|
|
63
|
-
plugin: mod.githubPlugin,
|
|
64
|
-
seedFromConfig: mod.seedFromConfig,
|
|
65
|
-
createAppKeyResolver(store) {
|
|
66
|
-
return (appId) => {
|
|
67
|
-
try {
|
|
68
|
-
const gh = mod.getGitHubStore(store);
|
|
69
|
-
const ghApp = gh.apps.all().find((a) => a.app_id === appId);
|
|
70
|
-
if (!ghApp) return null;
|
|
71
|
-
return { privateKey: ghApp.private_key, slug: ghApp.slug, name: ghApp.name };
|
|
72
|
-
} catch {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
},
|
|
79
|
-
defaultFallback(cfg) {
|
|
80
|
-
const firstLogin = cfg?.users?.[0]?.login ?? "admin";
|
|
81
|
-
return { login: firstLogin, id: 1, scopes: ["repo", "user", "admin:org", "admin:repo_hook"] };
|
|
82
|
-
},
|
|
83
|
-
initConfig: {
|
|
84
|
-
github: {
|
|
85
|
-
users: [
|
|
86
|
-
{
|
|
87
|
-
login: "octocat",
|
|
88
|
-
name: "The Octocat",
|
|
89
|
-
email: "octocat@github.com",
|
|
90
|
-
bio: "I am the Octocat",
|
|
91
|
-
company: "GitHub",
|
|
92
|
-
location: "San Francisco"
|
|
93
|
-
}
|
|
94
|
-
],
|
|
95
|
-
orgs: [{ login: "my-org", name: "My Organization", description: "A test organization" }],
|
|
96
|
-
repos: [
|
|
97
|
-
{
|
|
98
|
-
owner: "octocat",
|
|
99
|
-
name: "hello-world",
|
|
100
|
-
description: "My first repository",
|
|
101
|
-
language: "JavaScript",
|
|
102
|
-
topics: ["hello", "world"],
|
|
103
|
-
auto_init: true
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
owner: "my-org",
|
|
107
|
-
name: "org-repo",
|
|
108
|
-
description: "An organization repository",
|
|
109
|
-
language: "TypeScript",
|
|
110
|
-
auto_init: true
|
|
111
|
-
}
|
|
112
|
-
],
|
|
113
|
-
oauth_apps: [
|
|
114
|
-
{
|
|
115
|
-
client_id: "Iv1.example_client_id",
|
|
116
|
-
client_secret: "example_client_secret",
|
|
117
|
-
name: "My App",
|
|
118
|
-
redirect_uris: ["http://localhost:3000/api/auth/callback/github"]
|
|
119
|
-
}
|
|
120
|
-
]
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
google: {
|
|
125
|
-
name: "google",
|
|
126
|
-
label: "Google OAuth 2.0 / OpenID Connect + Gmail, Calendar, and Drive emulator",
|
|
127
|
-
endpoints: "OAuth authorize, token exchange, userinfo, OIDC discovery, token revocation, Gmail messages/drafts/threads/labels/history/settings, Calendar lists/events/freebusy, Drive files/uploads",
|
|
128
|
-
async load() {
|
|
129
|
-
const mod = await import("@emulators/google");
|
|
130
|
-
return { plugin: mod.googlePlugin, seedFromConfig: mod.seedFromConfig };
|
|
131
|
-
},
|
|
132
|
-
defaultFallback(cfg) {
|
|
133
|
-
const firstEmail = cfg?.users?.[0]?.email ?? "testuser@gmail.com";
|
|
134
|
-
return { login: firstEmail, id: 1, scopes: ["openid", "email", "profile"] };
|
|
135
|
-
},
|
|
136
|
-
initConfig: {
|
|
137
|
-
google: {
|
|
138
|
-
users: [
|
|
139
|
-
{
|
|
140
|
-
email: "testuser@example.com",
|
|
141
|
-
name: "Test User",
|
|
142
|
-
picture: "https://lh3.googleusercontent.com/a/default-user",
|
|
143
|
-
email_verified: true
|
|
144
|
-
}
|
|
145
|
-
],
|
|
146
|
-
oauth_clients: [
|
|
147
|
-
{
|
|
148
|
-
client_id: "example-client-id.apps.googleusercontent.com",
|
|
149
|
-
client_secret: "GOCSPX-example_secret",
|
|
150
|
-
name: "Code App (Google)",
|
|
151
|
-
redirect_uris: ["http://localhost:3000/api/auth/callback/google"]
|
|
152
|
-
}
|
|
153
|
-
],
|
|
154
|
-
labels: [
|
|
155
|
-
{
|
|
156
|
-
id: "Label_ops",
|
|
157
|
-
user_email: "testuser@example.com",
|
|
158
|
-
name: "Ops/Review",
|
|
159
|
-
color_background: "#DDEEFF",
|
|
160
|
-
color_text: "#111111"
|
|
161
|
-
}
|
|
162
|
-
],
|
|
163
|
-
messages: [
|
|
164
|
-
{
|
|
165
|
-
id: "msg_welcome",
|
|
166
|
-
user_email: "testuser@example.com",
|
|
167
|
-
from: "welcome@example.com",
|
|
168
|
-
to: "testuser@example.com",
|
|
169
|
-
subject: "Welcome to the Gmail emulator",
|
|
170
|
-
body_text: "You can now test Gmail, Calendar, and Drive flows locally.",
|
|
171
|
-
label_ids: ["INBOX", "UNREAD", "CATEGORY_UPDATES"],
|
|
172
|
-
date: "2025-01-04T10:00:00.000Z"
|
|
173
|
-
}
|
|
174
|
-
],
|
|
175
|
-
calendars: [
|
|
176
|
-
{
|
|
177
|
-
id: "primary",
|
|
178
|
-
user_email: "testuser@example.com",
|
|
179
|
-
summary: "testuser@example.com",
|
|
180
|
-
primary: true,
|
|
181
|
-
selected: true,
|
|
182
|
-
time_zone: "UTC"
|
|
183
|
-
}
|
|
184
|
-
],
|
|
185
|
-
calendar_events: [
|
|
186
|
-
{
|
|
187
|
-
id: "evt_kickoff",
|
|
188
|
-
user_email: "testuser@example.com",
|
|
189
|
-
calendar_id: "primary",
|
|
190
|
-
summary: "Project Kickoff",
|
|
191
|
-
start_date_time: "2025-01-10T09:00:00.000Z",
|
|
192
|
-
end_date_time: "2025-01-10T09:30:00.000Z"
|
|
193
|
-
}
|
|
194
|
-
],
|
|
195
|
-
drive_items: [
|
|
196
|
-
{
|
|
197
|
-
id: "drv_docs",
|
|
198
|
-
user_email: "testuser@example.com",
|
|
199
|
-
name: "Docs",
|
|
200
|
-
mime_type: "application/vnd.google-apps.folder",
|
|
201
|
-
parent_ids: ["root"]
|
|
202
|
-
}
|
|
203
|
-
]
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
},
|
|
207
|
-
slack: {
|
|
208
|
-
name: "slack",
|
|
209
|
-
label: "Slack API emulator",
|
|
210
|
-
endpoints: "auth, chat, conversations, users, reactions, team, OAuth, incoming webhooks",
|
|
211
|
-
async load() {
|
|
212
|
-
const mod = await import("@emulators/slack");
|
|
213
|
-
return { plugin: mod.slackPlugin, seedFromConfig: mod.seedFromConfig };
|
|
214
|
-
},
|
|
215
|
-
defaultFallback() {
|
|
216
|
-
return { login: "U000000001", id: 1, scopes: ["chat:write", "channels:read", "users:read", "reactions:write"] };
|
|
217
|
-
},
|
|
218
|
-
initConfig: {
|
|
219
|
-
slack: {
|
|
220
|
-
team: { name: "My Workspace", domain: "my-workspace" },
|
|
221
|
-
users: [{ name: "developer", real_name: "Developer", email: "dev@example.com" }],
|
|
222
|
-
channels: [
|
|
223
|
-
{ name: "general", topic: "General discussion" },
|
|
224
|
-
{ name: "random", topic: "Random stuff" }
|
|
225
|
-
],
|
|
226
|
-
bots: [{ name: "my-bot" }],
|
|
227
|
-
oauth_apps: [
|
|
228
|
-
{
|
|
229
|
-
client_id: "12345.67890",
|
|
230
|
-
client_secret: "example_client_secret",
|
|
231
|
-
name: "My Slack App",
|
|
232
|
-
redirect_uris: ["http://localhost:3000/api/auth/callback/slack"]
|
|
233
|
-
}
|
|
234
|
-
]
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
},
|
|
238
|
-
apple: {
|
|
239
|
-
name: "apple",
|
|
240
|
-
label: "Apple Sign In / OAuth emulator",
|
|
241
|
-
endpoints: "OAuth authorize, token exchange, JWKS",
|
|
242
|
-
async load() {
|
|
243
|
-
const mod = await import("@emulators/apple");
|
|
244
|
-
return { plugin: mod.applePlugin, seedFromConfig: mod.seedFromConfig };
|
|
245
|
-
},
|
|
246
|
-
defaultFallback(cfg) {
|
|
247
|
-
const firstEmail = cfg?.users?.[0]?.email ?? "testuser@icloud.com";
|
|
248
|
-
return { login: firstEmail, id: 1, scopes: ["openid", "email", "name"] };
|
|
249
|
-
},
|
|
250
|
-
initConfig: {
|
|
251
|
-
apple: {
|
|
252
|
-
users: [{ email: "testuser@icloud.com", name: "Test User" }],
|
|
253
|
-
oauth_clients: [
|
|
254
|
-
{
|
|
255
|
-
client_id: "com.example.app",
|
|
256
|
-
team_id: "TEAM001",
|
|
257
|
-
name: "My Apple App",
|
|
258
|
-
redirect_uris: ["http://localhost:3000/api/auth/callback/apple"]
|
|
259
|
-
}
|
|
260
|
-
]
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
},
|
|
264
|
-
microsoft: {
|
|
265
|
-
name: "microsoft",
|
|
266
|
-
label: "Microsoft Entra ID OAuth 2.0 / OpenID Connect emulator",
|
|
267
|
-
endpoints: "OAuth authorize, token exchange, userinfo, OIDC discovery, Graph /me, logout, token revocation",
|
|
268
|
-
async load() {
|
|
269
|
-
const mod = await import("@emulators/microsoft");
|
|
270
|
-
return { plugin: mod.microsoftPlugin, seedFromConfig: mod.seedFromConfig };
|
|
271
|
-
},
|
|
272
|
-
defaultFallback(cfg) {
|
|
273
|
-
const firstEmail = cfg?.users?.[0]?.email ?? "testuser@outlook.com";
|
|
274
|
-
return { login: firstEmail, id: 1, scopes: ["openid", "email", "profile", "User.Read"] };
|
|
275
|
-
},
|
|
276
|
-
initConfig: {
|
|
277
|
-
microsoft: {
|
|
278
|
-
users: [{ email: "testuser@outlook.com", name: "Test User" }],
|
|
279
|
-
oauth_clients: [
|
|
280
|
-
{
|
|
281
|
-
client_id: "example-client-id",
|
|
282
|
-
client_secret: "example-client-secret",
|
|
283
|
-
name: "My Microsoft App",
|
|
284
|
-
redirect_uris: ["http://localhost:3000/api/auth/callback/microsoft-entra-id"]
|
|
285
|
-
}
|
|
286
|
-
]
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
},
|
|
290
|
-
okta: {
|
|
291
|
-
name: "okta",
|
|
292
|
-
label: "Okta OAuth 2.0 / OpenID Connect + management API emulator",
|
|
293
|
-
endpoints: "OIDC discovery, JWKS, OAuth authorize/token/userinfo/introspect/revoke/logout, users, groups, apps, authorization servers",
|
|
294
|
-
async load() {
|
|
295
|
-
const mod = await import("@emulators/okta");
|
|
296
|
-
return { plugin: mod.oktaPlugin, seedFromConfig: mod.seedFromConfig };
|
|
297
|
-
},
|
|
298
|
-
defaultFallback(cfg) {
|
|
299
|
-
const firstLogin = cfg?.users?.[0]?.login ?? cfg?.users?.[0]?.email ?? "testuser@okta.local";
|
|
300
|
-
return { login: firstLogin, id: 1, scopes: ["openid", "profile", "email", "groups"] };
|
|
301
|
-
},
|
|
302
|
-
initConfig: {
|
|
303
|
-
okta: {
|
|
304
|
-
users: [{ login: "testuser@okta.local", email: "testuser@okta.local", first_name: "Test", last_name: "User" }],
|
|
305
|
-
groups: [{ name: "Everyone", description: "All users", type: "BUILT_IN", okta_id: "00g_everyone" }],
|
|
306
|
-
authorization_servers: [{ id: "default", name: "default", audiences: ["api://default"] }],
|
|
307
|
-
oauth_clients: [
|
|
308
|
-
{
|
|
309
|
-
client_id: "okta-test-client",
|
|
310
|
-
client_secret: "okta-test-secret",
|
|
311
|
-
name: "Sample OIDC Client",
|
|
312
|
-
redirect_uris: ["http://localhost:3000/callback"],
|
|
313
|
-
auth_server_id: "default"
|
|
314
|
-
}
|
|
315
|
-
]
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
},
|
|
319
|
-
aws: {
|
|
320
|
-
name: "aws",
|
|
321
|
-
label: "AWS cloud service emulator",
|
|
322
|
-
endpoints: "S3 (buckets, objects), SQS (queues, messages), IAM (users, roles, access keys), STS (assume role, caller identity)",
|
|
323
|
-
async load() {
|
|
324
|
-
const mod = await import("@emulators/aws");
|
|
325
|
-
return { plugin: mod.awsPlugin, seedFromConfig: mod.seedFromConfig };
|
|
326
|
-
},
|
|
327
|
-
defaultFallback() {
|
|
328
|
-
return { login: "admin", id: 1, scopes: ["s3:*", "sqs:*", "iam:*", "sts:*"] };
|
|
329
|
-
},
|
|
330
|
-
initConfig: {
|
|
331
|
-
aws: {
|
|
332
|
-
region: "us-east-1",
|
|
333
|
-
s3: { buckets: [{ name: "my-app-bucket" }, { name: "my-app-uploads" }] },
|
|
334
|
-
sqs: { queues: [{ name: "my-app-events" }, { name: "my-app-dlq" }] },
|
|
335
|
-
iam: {
|
|
336
|
-
users: [{ user_name: "developer", create_access_key: true }],
|
|
337
|
-
roles: [{ role_name: "lambda-execution-role", description: "Role for Lambda function execution" }]
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
},
|
|
342
|
-
resend: {
|
|
343
|
-
name: "resend",
|
|
344
|
-
label: "Resend email API emulator",
|
|
345
|
-
endpoints: "emails, domains, contacts, API keys, inbox UI",
|
|
346
|
-
async load() {
|
|
347
|
-
const mod = await import("@emulators/resend");
|
|
348
|
-
return { plugin: mod.resendPlugin, seedFromConfig: mod.seedFromConfig };
|
|
349
|
-
},
|
|
350
|
-
defaultFallback() {
|
|
351
|
-
return { login: "re_test_admin", id: 1, scopes: [] };
|
|
352
|
-
},
|
|
353
|
-
initConfig: {
|
|
354
|
-
resend: {
|
|
355
|
-
domains: [{ name: "example.com", region: "us-east-1" }],
|
|
356
|
-
contacts: [{ email: "test@example.com", first_name: "Test", last_name: "User" }]
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
},
|
|
360
|
-
stripe: {
|
|
361
|
-
name: "stripe",
|
|
362
|
-
label: "Stripe payments emulator",
|
|
363
|
-
endpoints: "customers, payment methods, customer sessions, payment intents, charges, products, prices, checkout sessions, webhooks",
|
|
364
|
-
async load() {
|
|
365
|
-
const mod = await import("@emulators/stripe");
|
|
366
|
-
return { plugin: mod.stripePlugin, seedFromConfig: mod.seedFromConfig };
|
|
367
|
-
},
|
|
368
|
-
defaultFallback() {
|
|
369
|
-
return { login: "sk_test_admin", id: 1, scopes: [] };
|
|
370
|
-
},
|
|
371
|
-
initConfig: {
|
|
372
|
-
stripe: {
|
|
373
|
-
customers: [{ email: "test@example.com", name: "Test Customer" }],
|
|
374
|
-
products: [{ name: "Pro Plan", description: "Monthly pro subscription" }],
|
|
375
|
-
prices: [{ product_name: "Pro Plan", currency: "usd", unit_amount: 2e3 }]
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
|
-
mongoatlas: {
|
|
380
|
-
name: "mongoatlas",
|
|
381
|
-
label: "MongoDB Atlas service emulator",
|
|
382
|
-
endpoints: "Atlas Admin API v2 (projects, clusters, database users, databases, collections), Atlas Data API v1 (findOne, find, insertOne, insertMany, updateOne, updateMany, deleteOne, deleteMany, aggregate)",
|
|
383
|
-
async load() {
|
|
384
|
-
const mod = await import("@emulators/mongoatlas");
|
|
385
|
-
return { plugin: mod.mongoatlasPlugin, seedFromConfig: mod.seedFromConfig };
|
|
386
|
-
},
|
|
387
|
-
defaultFallback() {
|
|
388
|
-
return { login: "admin", id: 1, scopes: [] };
|
|
389
|
-
},
|
|
390
|
-
initConfig: {
|
|
391
|
-
mongoatlas: {
|
|
392
|
-
projects: [{ name: "Project0" }],
|
|
393
|
-
clusters: [{ name: "Cluster0", project: "Project0" }],
|
|
394
|
-
database_users: [{ username: "admin", project: "Project0" }],
|
|
395
|
-
databases: [{ cluster: "Cluster0", name: "test", collections: ["items"] }]
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
clerk: {
|
|
400
|
-
name: "clerk",
|
|
401
|
-
label: "Clerk authentication and user management emulator",
|
|
402
|
-
endpoints: "OIDC discovery, JWKS, OAuth authorize/token/userinfo, users, email addresses, organizations, memberships, invitations, sessions",
|
|
403
|
-
async load() {
|
|
404
|
-
const mod = await import("@emulators/clerk");
|
|
405
|
-
return { plugin: mod.clerkPlugin, seedFromConfig: mod.seedFromConfig };
|
|
406
|
-
},
|
|
407
|
-
defaultFallback(cfg) {
|
|
408
|
-
const firstEmail = cfg?.users?.[0]?.email_addresses?.[0] ?? "test@example.com";
|
|
409
|
-
return { login: firstEmail, id: 1, scopes: [] };
|
|
410
|
-
},
|
|
411
|
-
initConfig: {
|
|
412
|
-
clerk: {
|
|
413
|
-
users: [
|
|
414
|
-
{
|
|
415
|
-
first_name: "Test",
|
|
416
|
-
last_name: "User",
|
|
417
|
-
email_addresses: ["test@example.com"],
|
|
418
|
-
password: "clerk_test_password"
|
|
419
|
-
}
|
|
420
|
-
],
|
|
421
|
-
organizations: [
|
|
422
|
-
{
|
|
423
|
-
name: "My Company",
|
|
424
|
-
slug: "my-company",
|
|
425
|
-
members: [{ email: "test@example.com", role: "admin" }]
|
|
426
|
-
}
|
|
427
|
-
],
|
|
428
|
-
oauth_applications: [
|
|
429
|
-
{
|
|
430
|
-
client_id: "clerk_emulate_client",
|
|
431
|
-
client_secret: "clerk_emulate_secret",
|
|
432
|
-
name: "api-emulator App",
|
|
433
|
-
redirect_uris: ["http://localhost:3000/api/auth/callback/clerk"]
|
|
434
|
-
}
|
|
435
|
-
]
|
|
436
|
-
}
|
|
437
|
-
}
|
|
46
|
+
// src/plugin-lock.ts
|
|
47
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
48
|
+
import { resolve as resolve2 } from "path";
|
|
49
|
+
var PLUGIN_LOCK_FILE = "api-emulator.lock";
|
|
50
|
+
function createEmptyPluginLock() {
|
|
51
|
+
return { version: 1, plugins: {} };
|
|
52
|
+
}
|
|
53
|
+
function readPluginLock(cwd = process.cwd()) {
|
|
54
|
+
const path = resolve2(cwd, PLUGIN_LOCK_FILE);
|
|
55
|
+
if (!existsSync(path)) return createEmptyPluginLock();
|
|
56
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
57
|
+
if (parsed.version !== 1 || typeof parsed.plugins !== "object" || parsed.plugins === null) {
|
|
58
|
+
throw new Error(`Invalid ${PLUGIN_LOCK_FILE}`);
|
|
438
59
|
}
|
|
439
|
-
|
|
60
|
+
return parsed;
|
|
61
|
+
}
|
|
62
|
+
function getLockedPluginSpecifiers(cwd = process.cwd()) {
|
|
63
|
+
return Object.values(readPluginLock(cwd).plugins).map((entry) => entry.specifier);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/default-plugin-catalog.ts
|
|
67
|
+
var DEFAULT_PLUGIN_REGISTRY = {};
|
|
440
68
|
|
|
441
69
|
// src/registry.ts
|
|
442
|
-
async function resolvePluginModules(pluginSpecifiers = []) {
|
|
443
|
-
const
|
|
70
|
+
async function resolvePluginModules(pluginSpecifiers = [], options = {}) {
|
|
71
|
+
const installedSpecifiers = options.includeInstalled ? getLockedPluginSpecifiers() : [];
|
|
72
|
+
const allSpecifiers = [...installedSpecifiers, ...pluginSpecifiers];
|
|
73
|
+
const results = await Promise.all(allSpecifiers.map(loadExternalPluginModule));
|
|
444
74
|
const externalEntries = {};
|
|
445
75
|
for (const pluginModule of results) {
|
|
446
76
|
if (pluginModule.name in DEFAULT_PLUGIN_REGISTRY) {
|
|
@@ -462,7 +92,7 @@ function resolveBaseUrl(opts) {
|
|
|
462
92
|
if (opts.baseUrl) {
|
|
463
93
|
return opts.baseUrl.replace(/\{service\}/g, opts.service);
|
|
464
94
|
}
|
|
465
|
-
const envBaseUrl = process.env.API_EMULATOR_BASE_URL
|
|
95
|
+
const envBaseUrl = process.env.API_EMULATOR_BASE_URL;
|
|
466
96
|
if (envBaseUrl) {
|
|
467
97
|
return envBaseUrl.replace(/\{service\}/g, opts.service);
|
|
468
98
|
}
|
|
@@ -474,7 +104,11 @@ function resolveBaseUrl(opts) {
|
|
|
474
104
|
}
|
|
475
105
|
|
|
476
106
|
// src/service-runtime.ts
|
|
477
|
-
import {
|
|
107
|
+
import {
|
|
108
|
+
createServer,
|
|
109
|
+
createStoreFixture,
|
|
110
|
+
fixtureStoreSnapshot
|
|
111
|
+
} from "@api-emulator/core";
|
|
478
112
|
import { serve } from "@hono/node-server";
|
|
479
113
|
function createAuthTokens(seedConfig) {
|
|
480
114
|
const tokens = {};
|
|
@@ -513,15 +147,32 @@ function createServiceRuntime(options) {
|
|
|
513
147
|
service,
|
|
514
148
|
url: baseUrl,
|
|
515
149
|
store,
|
|
150
|
+
snapshot() {
|
|
151
|
+
return store.snapshot();
|
|
152
|
+
},
|
|
153
|
+
restore(fixture) {
|
|
154
|
+
store.restore(fixtureStoreSnapshot(fixture));
|
|
155
|
+
},
|
|
156
|
+
exportFixture(options2 = {}) {
|
|
157
|
+
const interactions = store.getData("api-emulator:interactions");
|
|
158
|
+
return createStoreFixture(service, store.snapshot(), {
|
|
159
|
+
...options2,
|
|
160
|
+
interactions: options2.interactions ?? interactions
|
|
161
|
+
});
|
|
162
|
+
},
|
|
163
|
+
resetToFixture(fixture) {
|
|
164
|
+
store.reset();
|
|
165
|
+
store.restore(fixtureStoreSnapshot(fixture));
|
|
166
|
+
},
|
|
516
167
|
reset() {
|
|
517
168
|
store.reset();
|
|
518
169
|
seed();
|
|
519
170
|
},
|
|
520
171
|
close() {
|
|
521
|
-
return new Promise((
|
|
172
|
+
return new Promise((resolve3, reject) => {
|
|
522
173
|
httpServer.close((err) => {
|
|
523
174
|
if (err) reject(err);
|
|
524
|
-
else
|
|
175
|
+
else resolve3();
|
|
525
176
|
});
|
|
526
177
|
});
|
|
527
178
|
}
|
|
@@ -551,6 +202,10 @@ async function createEmulator(options) {
|
|
|
551
202
|
});
|
|
552
203
|
return {
|
|
553
204
|
url: running.url,
|
|
205
|
+
snapshot: running.snapshot,
|
|
206
|
+
restore: running.restore,
|
|
207
|
+
exportFixture: running.exportFixture,
|
|
208
|
+
resetToFixture: running.resetToFixture,
|
|
554
209
|
reset: running.reset,
|
|
555
210
|
close: running.close
|
|
556
211
|
};
|