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 +79 -185
- package/dist/api.d.ts +8 -2
- package/dist/api.js +82 -421
- package/dist/api.js.map +1 -1
- package/dist/index.js +288 -459
- package/dist/index.js.map +1 -1
- package/package.json +8 -18
package/dist/api.js
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
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 {
|
|
7
|
+
label: mod.label,
|
|
8
|
+
endpoints: mod.endpoints,
|
|
9
|
+
initConfig: mod.initConfig,
|
|
10
|
+
contract: mod.contract,
|
|
11
|
+
...mod.manifest
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function validatePluginManifest(manifest, pluginName) {
|
|
15
|
+
if (manifest.name && manifest.name !== pluginName) {
|
|
16
|
+
throw new Error(`Plugin manifest name "${manifest.name}" does not match plugin name "${pluginName}"`);
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
...manifest,
|
|
20
|
+
label: manifest.label ?? `${pluginName} (external plugin)`,
|
|
21
|
+
endpoints: manifest.endpoints ?? "",
|
|
22
|
+
initConfig: manifest.initConfig ?? {}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/external-plugin-adapter.ts
|
|
3
27
|
async function loadExternalPluginModule(specifier) {
|
|
4
28
|
const modulePath = specifier.startsWith(".") || isAbsolute(specifier) ? resolve(specifier) : specifier;
|
|
5
29
|
const mod = await import(modulePath);
|
|
@@ -8,10 +32,12 @@ async function loadExternalPluginModule(specifier) {
|
|
|
8
32
|
throw new Error(`Plugin "${specifier}" must export a ServicePlugin (as "plugin" or default export)`);
|
|
9
33
|
}
|
|
10
34
|
const name = plugin.name;
|
|
35
|
+
const manifest = validatePluginManifest(readPluginManifest(mod), name);
|
|
11
36
|
return {
|
|
12
37
|
name,
|
|
13
|
-
label:
|
|
14
|
-
endpoints:
|
|
38
|
+
label: manifest.label,
|
|
39
|
+
endpoints: manifest.endpoints,
|
|
40
|
+
manifest,
|
|
15
41
|
async load() {
|
|
16
42
|
return {
|
|
17
43
|
plugin,
|
|
@@ -19,428 +45,38 @@ async function loadExternalPluginModule(specifier) {
|
|
|
19
45
|
};
|
|
20
46
|
},
|
|
21
47
|
defaultFallback: mod.defaultFallback ?? (() => ({ login: "admin", id: 1, scopes: [] })),
|
|
22
|
-
initConfig:
|
|
48
|
+
initConfig: manifest.initConfig
|
|
23
49
|
};
|
|
24
50
|
}
|
|
25
51
|
|
|
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
|
-
}
|
|
52
|
+
// src/plugin-lock.ts
|
|
53
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
54
|
+
import { resolve as resolve2 } from "path";
|
|
55
|
+
var PLUGIN_LOCK_FILE = "api-emulator.lock";
|
|
56
|
+
function createEmptyPluginLock() {
|
|
57
|
+
return { version: 1, plugins: {} };
|
|
58
|
+
}
|
|
59
|
+
function readPluginLock(cwd = process.cwd()) {
|
|
60
|
+
const path = resolve2(cwd, PLUGIN_LOCK_FILE);
|
|
61
|
+
if (!existsSync(path)) return createEmptyPluginLock();
|
|
62
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
63
|
+
if (parsed.version !== 1 || typeof parsed.plugins !== "object" || parsed.plugins === null) {
|
|
64
|
+
throw new Error(`Invalid ${PLUGIN_LOCK_FILE}`);
|
|
438
65
|
}
|
|
439
|
-
|
|
66
|
+
return parsed;
|
|
67
|
+
}
|
|
68
|
+
function getLockedPluginSpecifiers(cwd = process.cwd()) {
|
|
69
|
+
return Object.values(readPluginLock(cwd).plugins).map((entry) => entry.specifier);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/default-plugin-catalog.ts
|
|
73
|
+
var DEFAULT_PLUGIN_REGISTRY = {};
|
|
440
74
|
|
|
441
75
|
// src/registry.ts
|
|
442
|
-
async function resolvePluginModules(pluginSpecifiers = []) {
|
|
443
|
-
const
|
|
76
|
+
async function resolvePluginModules(pluginSpecifiers = [], options = {}) {
|
|
77
|
+
const installedSpecifiers = options.includeInstalled ? getLockedPluginSpecifiers() : [];
|
|
78
|
+
const allSpecifiers = [...installedSpecifiers, ...pluginSpecifiers];
|
|
79
|
+
const results = await Promise.all(allSpecifiers.map(loadExternalPluginModule));
|
|
444
80
|
const externalEntries = {};
|
|
445
81
|
for (const pluginModule of results) {
|
|
446
82
|
if (pluginModule.name in DEFAULT_PLUGIN_REGISTRY) {
|
|
@@ -474,7 +110,11 @@ function resolveBaseUrl(opts) {
|
|
|
474
110
|
}
|
|
475
111
|
|
|
476
112
|
// src/service-runtime.ts
|
|
477
|
-
import {
|
|
113
|
+
import {
|
|
114
|
+
createServer,
|
|
115
|
+
createStoreFixture,
|
|
116
|
+
fixtureStoreSnapshot
|
|
117
|
+
} from "@api-emulator/core";
|
|
478
118
|
import { serve } from "@hono/node-server";
|
|
479
119
|
function createAuthTokens(seedConfig) {
|
|
480
120
|
const tokens = {};
|
|
@@ -513,15 +153,32 @@ function createServiceRuntime(options) {
|
|
|
513
153
|
service,
|
|
514
154
|
url: baseUrl,
|
|
515
155
|
store,
|
|
156
|
+
snapshot() {
|
|
157
|
+
return store.snapshot();
|
|
158
|
+
},
|
|
159
|
+
restore(fixture) {
|
|
160
|
+
store.restore(fixtureStoreSnapshot(fixture));
|
|
161
|
+
},
|
|
162
|
+
exportFixture(options2 = {}) {
|
|
163
|
+
const interactions = store.getData("api-emulator:interactions");
|
|
164
|
+
return createStoreFixture(service, store.snapshot(), {
|
|
165
|
+
...options2,
|
|
166
|
+
interactions: options2.interactions ?? interactions
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
resetToFixture(fixture) {
|
|
170
|
+
store.reset();
|
|
171
|
+
store.restore(fixtureStoreSnapshot(fixture));
|
|
172
|
+
},
|
|
516
173
|
reset() {
|
|
517
174
|
store.reset();
|
|
518
175
|
seed();
|
|
519
176
|
},
|
|
520
177
|
close() {
|
|
521
|
-
return new Promise((
|
|
178
|
+
return new Promise((resolve3, reject) => {
|
|
522
179
|
httpServer.close((err) => {
|
|
523
180
|
if (err) reject(err);
|
|
524
|
-
else
|
|
181
|
+
else resolve3();
|
|
525
182
|
});
|
|
526
183
|
});
|
|
527
184
|
}
|
|
@@ -551,6 +208,10 @@ async function createEmulator(options) {
|
|
|
551
208
|
});
|
|
552
209
|
return {
|
|
553
210
|
url: running.url,
|
|
211
|
+
snapshot: running.snapshot,
|
|
212
|
+
restore: running.restore,
|
|
213
|
+
exportFixture: running.exportFixture,
|
|
214
|
+
resetToFixture: running.resetToFixture,
|
|
554
215
|
reset: running.reset,
|
|
555
216
|
close: running.close
|
|
556
217
|
};
|