emulate 0.3.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/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.GITHUB_URL = github.url
88
- process.env.VERCEL_URL = vercel.url
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 to emulate: `'github'`, `'vercel'`, or `'google'` |
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. To customize seed data, create `emulate.config.yaml` in your project root (or pass `--seed`):
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:
@@ -188,6 +192,68 @@ google:
188
192
  name: Docs
189
193
  mime_type: application/vnd.google-apps.folder
190
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
191
257
  ```
192
258
 
193
259
  ## OAuth & Integrations
@@ -252,6 +318,42 @@ JWT authentication: sign a JWT with `{ iss: "<app_id>" }` using the app's privat
252
318
  - All webhook payloads (including repo and org hooks) include an `installation` field with `{ id, node_id }`.
253
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`.
254
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
+
255
357
  ## Vercel API
256
358
 
257
359
  Every endpoint below is fully stateful with Vercel-style JSON responses and cursor-based pagination.
@@ -424,7 +526,6 @@ Every endpoint below is fully stateful. Creates, updates, and deletes persist in
424
526
  ## Google OAuth + Gmail, Calendar, and Drive APIs
425
527
 
426
528
  OAuth 2.0, OpenID Connect, and mutable Google Workspace-style surfaces for local inbox, calendar, and drive flows.
427
- This stays under a single `google:` service because the Gmail API is used by both consumer Google accounts and Google Workspace accounts. A separate Workspace-specific service would only make sense once we add admin or tenant-level APIs that do not belong in the basic Google/Gmail emulator.
428
529
 
429
530
  - `GET /o/oauth2/v2/auth` - authorization endpoint
430
531
  - `POST /oauth2/token` - token exchange
@@ -450,18 +551,223 @@ This stays under a single `google:` service because the Gmail API is used by bot
450
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`
451
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`
452
553
 
453
- The Google plugin still does not cover every Google API edge case, but Gmail, Calendar, and Drive now have enough mutable surface to support realistic local automation flows without stuffing everything into static seed config.
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.
454
755
 
455
756
  ## Architecture
456
757
 
457
758
  ```
458
759
  packages/
459
760
  emulate/ # CLI entry point (commander)
460
- @emulators/
761
+ @emulators/
461
762
  core/ # HTTP server, in-memory store, plugin interface, middleware
763
+ adapter-next/ # Next.js App Router integration
462
764
  vercel/ # Vercel API service
463
765
  github/ # GitHub API service
464
- google/ # Google OAuth 2.0 / OIDC + Gmail, Calendar, and Drive APIs
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
465
771
  apps/
466
772
  web/ # Documentation site (Next.js)
467
773
  ```
@@ -475,3 +781,13 @@ Tokens are configured in the seed config and map to users. Pass them as `Authori
475
781
  **Vercel**: All endpoints accept `teamId` or `slug` query params for team scoping. Pagination uses cursor-based `limit`/`since`/`until` with `pagination` response objects.
476
782
 
477
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`.
package/dist/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- declare const SERVICE_NAME_LIST: readonly ["vercel", "github", "google", "slack", "apple", "microsoft", "aws"];
1
+ declare const SERVICE_NAME_LIST: readonly ["vercel", "github", "google", "slack", "apple", "microsoft", "okta", "aws", "resend", "stripe", "mongoatlas"];
2
2
  type ServiceName = (typeof SERVICE_NAME_LIST)[number];
3
3
 
4
4
  interface SeedConfig {
package/dist/api.js CHANGED
@@ -12,6 +12,28 @@ import { createHmac } from "crypto";
12
12
  import { readFileSync } from "fs";
13
13
  import { fileURLToPath } from "url";
14
14
  import { dirname, join } from "path";
15
+ function serializeValue(value) {
16
+ if (value instanceof Map) {
17
+ return { __type: "Map", entries: [...value.entries()].map(([k, v]) => [k, serializeValue(v)]) };
18
+ }
19
+ if (value instanceof Set) {
20
+ return { __type: "Set", values: [...value.values()] };
21
+ }
22
+ return value;
23
+ }
24
+ function deserializeValue(value) {
25
+ if (value !== null && typeof value === "object" && "__type" in value) {
26
+ const tagged = value;
27
+ if (tagged.__type === "Map") {
28
+ const entries = tagged.entries;
29
+ return new Map(entries.map(([k, v]) => [k, deserializeValue(v)]));
30
+ }
31
+ if (tagged.__type === "Set") {
32
+ return new Set(tagged.values);
33
+ }
34
+ }
35
+ return value;
36
+ }
15
37
  var Collection = class {
16
38
  constructor(indexFields = []) {
17
39
  this.indexFields = indexFields;
@@ -132,6 +154,21 @@ var Collection = class {
132
154
  }
133
155
  this.autoId = 1;
134
156
  }
157
+ snapshot() {
158
+ return {
159
+ items: this.all(),
160
+ autoId: this.autoId,
161
+ indexFields: this.fieldNames
162
+ };
163
+ }
164
+ restore(snap) {
165
+ this.clear();
166
+ this.autoId = snap.autoId;
167
+ for (const item of snap.items) {
168
+ this.items.set(item.id, item);
169
+ this.addToIndex(item);
170
+ }
171
+ }
135
172
  };
136
173
  var Store = class {
137
174
  collections = /* @__PURE__ */ new Map();
@@ -165,6 +202,34 @@ var Store = class {
165
202
  }
166
203
  this._data.clear();
167
204
  }
205
+ snapshot() {
206
+ const collections = {};
207
+ for (const [name, col] of this.collections) {
208
+ collections[name] = col.snapshot();
209
+ }
210
+ const data = {};
211
+ for (const [key, value] of this._data) {
212
+ data[key] = serializeValue(value);
213
+ }
214
+ return { collections, data };
215
+ }
216
+ restore(snap) {
217
+ const snapshotNames = new Set(Object.keys(snap.collections));
218
+ for (const name of this.collections.keys()) {
219
+ if (!snapshotNames.has(name)) {
220
+ this.collections.delete(name);
221
+ }
222
+ }
223
+ for (const [name, colSnap] of Object.entries(snap.collections)) {
224
+ const indexFields = colSnap.indexFields;
225
+ const col = this.collection(name, indexFields);
226
+ col.restore(colSnap);
227
+ }
228
+ this._data.clear();
229
+ for (const [key, value] of Object.entries(snap.data)) {
230
+ this._data.set(key, deserializeValue(value));
231
+ }
232
+ }
168
233
  };
169
234
  var MAX_DELIVERIES = 1e3;
170
235
  var WebhookDispatcher = class {
@@ -448,7 +513,7 @@ var SERVICE_REGISTRY = {
448
513
  label: "Vercel REST API emulator",
449
514
  endpoints: "projects, deployments, domains, env vars, users, teams, file uploads, protection bypass",
450
515
  async load() {
451
- const mod = await import("./dist-JYDZIVC6.js");
516
+ const mod = await import("./dist-RDFBZ5O6.js");
452
517
  return { plugin: mod.vercelPlugin, seedFromConfig: mod.seedFromConfig };
453
518
  },
454
519
  defaultFallback(cfg) {
@@ -473,7 +538,7 @@ var SERVICE_REGISTRY = {
473
538
  label: "GitHub REST API emulator",
474
539
  endpoints: "users, repos, issues, PRs, comments, reviews, labels, milestones, branches, git data, orgs, teams, releases, webhooks, search, actions, checks, rate limit",
475
540
  async load() {
476
- const mod = await import("./dist-OCDKIMRJ.js");
541
+ const mod = await import("./dist-H6JYGQM4.js");
477
542
  return {
478
543
  plugin: mod.githubPlugin,
479
544
  seedFromConfig: mod.seedFromConfig,
@@ -523,7 +588,7 @@ var SERVICE_REGISTRY = {
523
588
  label: "Google OAuth 2.0 / OpenID Connect + Gmail, Calendar, and Drive emulator",
524
589
  endpoints: "OAuth authorize, token exchange, userinfo, OIDC discovery, token revocation, Gmail messages/drafts/threads/labels/history/settings, Calendar lists/events/freebusy, Drive files/uploads",
525
590
  async load() {
526
- const mod = await import("./dist-BKXG6HVH.js");
591
+ const mod = await import("./dist-6EW7SSOZ.js");
527
592
  return { plugin: mod.googlePlugin, seedFromConfig: mod.seedFromConfig };
528
593
  },
529
594
  defaultFallback(cfg) {
@@ -567,7 +632,7 @@ var SERVICE_REGISTRY = {
567
632
  label: "Slack API emulator",
568
633
  endpoints: "auth, chat, conversations, users, reactions, team, OAuth, incoming webhooks",
569
634
  async load() {
570
- const mod = await import("./dist-UZSUUE3Y.js");
635
+ const mod = await import("./dist-G7WQPZ3Y.js");
571
636
  return { plugin: mod.slackPlugin, seedFromConfig: mod.seedFromConfig };
572
637
  },
573
638
  defaultFallback() {
@@ -592,7 +657,7 @@ var SERVICE_REGISTRY = {
592
657
  label: "Apple Sign In / OAuth emulator",
593
658
  endpoints: "OAuth authorize, token exchange, JWKS",
594
659
  async load() {
595
- const mod = await import("./dist-O4KFIBVU.js");
660
+ const mod = await import("./dist-6JFNJPUU.js");
596
661
  return { plugin: mod.applePlugin, seedFromConfig: mod.seedFromConfig };
597
662
  },
598
663
  defaultFallback(cfg) {
@@ -615,7 +680,7 @@ var SERVICE_REGISTRY = {
615
680
  label: "Microsoft Entra ID OAuth 2.0 / OpenID Connect emulator",
616
681
  endpoints: "OAuth authorize, token exchange, userinfo, OIDC discovery, Graph /me, logout, token revocation",
617
682
  async load() {
618
- const mod = await import("./dist-DSSB3LYT.js");
683
+ const mod = await import("./dist-RMK3BS5M.js");
619
684
  return { plugin: mod.microsoftPlugin, seedFromConfig: mod.seedFromConfig };
620
685
  },
621
686
  defaultFallback(cfg) {
@@ -634,6 +699,32 @@ var SERVICE_REGISTRY = {
634
699
  }
635
700
  }
636
701
  },
702
+ okta: {
703
+ label: "Okta OAuth 2.0 / OpenID Connect + management API emulator",
704
+ endpoints: "OIDC discovery, JWKS, OAuth authorize/token/userinfo/introspect/revoke/logout, users, groups, apps, authorization servers",
705
+ async load() {
706
+ const mod = await import("./dist-OTJZRQ3Q.js");
707
+ return { plugin: mod.oktaPlugin, seedFromConfig: mod.seedFromConfig };
708
+ },
709
+ defaultFallback(cfg) {
710
+ const firstLogin = cfg?.users?.[0]?.login ?? cfg?.users?.[0]?.email ?? "testuser@okta.local";
711
+ return { login: firstLogin, id: 1, scopes: ["openid", "profile", "email", "groups"] };
712
+ },
713
+ initConfig: {
714
+ okta: {
715
+ users: [{ login: "testuser@okta.local", email: "testuser@okta.local", first_name: "Test", last_name: "User" }],
716
+ groups: [{ name: "Everyone", description: "All users", type: "BUILT_IN", okta_id: "00g_everyone" }],
717
+ authorization_servers: [{ id: "default", name: "default", audiences: ["api://default"] }],
718
+ oauth_clients: [{
719
+ client_id: "okta-test-client",
720
+ client_secret: "okta-test-secret",
721
+ name: "Sample OIDC Client",
722
+ redirect_uris: ["http://localhost:3000/callback"],
723
+ auth_server_id: "default"
724
+ }]
725
+ }
726
+ }
727
+ },
637
728
  aws: {
638
729
  label: "AWS cloud service emulator",
639
730
  endpoints: "S3 (buckets, objects), SQS (queues, messages), IAM (users, roles, access keys), STS (assume role, caller identity)",
@@ -655,6 +746,60 @@ var SERVICE_REGISTRY = {
655
746
  }
656
747
  }
657
748
  }
749
+ },
750
+ resend: {
751
+ label: "Resend email API emulator",
752
+ endpoints: "emails, domains, contacts, API keys, inbox UI",
753
+ async load() {
754
+ const mod = await import("./dist-QMOJM6DV.js");
755
+ return { plugin: mod.resendPlugin, seedFromConfig: mod.seedFromConfig };
756
+ },
757
+ defaultFallback() {
758
+ return { login: "re_test_admin", id: 1, scopes: [] };
759
+ },
760
+ initConfig: {
761
+ resend: {
762
+ domains: [{ name: "example.com", region: "us-east-1" }],
763
+ contacts: [{ email: "test@example.com", first_name: "Test", last_name: "User" }]
764
+ }
765
+ }
766
+ },
767
+ stripe: {
768
+ label: "Stripe payments emulator",
769
+ endpoints: "customers, payment intents, charges, products, prices, checkout sessions, webhooks",
770
+ async load() {
771
+ const mod = await import("./dist-YOVM5HEY.js");
772
+ return { plugin: mod.stripePlugin, seedFromConfig: mod.seedFromConfig };
773
+ },
774
+ defaultFallback() {
775
+ return { login: "sk_test_admin", id: 1, scopes: [] };
776
+ },
777
+ initConfig: {
778
+ stripe: {
779
+ customers: [{ email: "test@example.com", name: "Test Customer" }],
780
+ products: [{ name: "Pro Plan", description: "Monthly pro subscription" }],
781
+ prices: [{ product_name: "Pro Plan", currency: "usd", unit_amount: 2e3 }]
782
+ }
783
+ }
784
+ },
785
+ mongoatlas: {
786
+ label: "MongoDB Atlas service emulator",
787
+ endpoints: "Atlas Admin API v2 (projects, clusters, database users, databases, collections), Atlas Data API v1 (findOne, find, insertOne, insertMany, updateOne, updateMany, deleteOne, deleteMany, aggregate)",
788
+ async load() {
789
+ const mod = await import("./dist-B674PYKV.js");
790
+ return { plugin: mod.mongoatlasPlugin, seedFromConfig: mod.seedFromConfig };
791
+ },
792
+ defaultFallback() {
793
+ return { login: "admin", id: 1, scopes: [] };
794
+ },
795
+ initConfig: {
796
+ mongoatlas: {
797
+ projects: [{ name: "Project0" }],
798
+ clusters: [{ name: "Cluster0", project: "Project0" }],
799
+ database_users: [{ username: "admin", project: "Project0" }],
800
+ databases: [{ cluster: "Cluster0", name: "test", collections: ["items"] }]
801
+ }
802
+ }
658
803
  }
659
804
  };
660
805