emulate 0.4.0 → 0.5.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 +59 -10
- package/dist/api.d.ts +2 -1
- package/dist/api.js +232 -96
- package/dist/api.js.map +1 -1
- package/dist/{chunk-TEPNEZ63.js → chunk-AQ2CLRU3.js} +26 -23
- package/dist/chunk-AQ2CLRU3.js.map +1 -0
- package/dist/chunk-WVQMFHQM.js +83 -0
- package/dist/chunk-WVQMFHQM.js.map +1 -0
- package/dist/{dist-RDFBZ5O6.js → dist-4X2KPMAJ.js} +212 -47
- package/dist/dist-4X2KPMAJ.js.map +1 -0
- package/dist/{dist-OTJZRQ3Q.js → dist-5JVGPOL3.js} +217 -75
- package/dist/dist-5JVGPOL3.js.map +1 -0
- package/dist/{dist-G7WQPZ3Y.js → dist-CE6BUCWQ.js} +211 -60
- package/dist/dist-CE6BUCWQ.js.map +1 -0
- package/dist/{dist-6JFNJPUU.js → dist-CFST4X4K.js} +172 -22
- package/dist/dist-CFST4X4K.js.map +1 -0
- package/dist/{dist-YOVM5HEY.js → dist-ENKE2S7V.js} +521 -60
- package/dist/dist-ENKE2S7V.js.map +1 -0
- package/dist/{dist-RMK3BS5M.js → dist-ETHHYBGF.js} +197 -33
- package/dist/dist-ETHHYBGF.js.map +1 -0
- package/dist/{dist-QMOJM6DV.js → dist-IBXD3O6A.js} +239 -54
- package/dist/dist-IBXD3O6A.js.map +1 -0
- package/dist/dist-J6LHUR52.js +1899 -0
- package/dist/dist-J6LHUR52.js.map +1 -0
- package/dist/{dist-6EW7SSOZ.js → dist-KKTYBE5S.js} +391 -222
- package/dist/dist-KKTYBE5S.js.map +1 -0
- package/dist/{dist-VVXVP5EZ.js → dist-LDUHEJAN.js} +553 -91
- package/dist/dist-LDUHEJAN.js.map +1 -0
- package/dist/{dist-B674PYKV.js → dist-PWGOAQC6.js} +22 -43
- package/dist/dist-PWGOAQC6.js.map +1 -0
- package/dist/{dist-H6JYGQM4.js → dist-REDHDZ3V.js} +272 -157
- package/dist/dist-REDHDZ3V.js.map +1 -0
- package/dist/fonts/favicon.ico +0 -0
- package/dist/helpers-LXLP3DFE-LBOTATT5.js +17 -0
- package/dist/helpers-LXLP3DFE-LBOTATT5.js.map +1 -0
- package/dist/index.js +365 -108
- package/dist/index.js.map +1 -1
- package/package.json +17 -14
- package/dist/chunk-TEPNEZ63.js.map +0 -1
- package/dist/dist-6EW7SSOZ.js.map +0 -1
- package/dist/dist-6JFNJPUU.js.map +0 -1
- package/dist/dist-B674PYKV.js.map +0 -1
- package/dist/dist-G7WQPZ3Y.js.map +0 -1
- package/dist/dist-H6JYGQM4.js.map +0 -1
- package/dist/dist-OTJZRQ3Q.js.map +0 -1
- package/dist/dist-QMOJM6DV.js.map +0 -1
- package/dist/dist-RDFBZ5O6.js.map +0 -1
- package/dist/dist-RMK3BS5M.js.map +0 -1
- package/dist/dist-VVXVP5EZ.js.map +0 -1
- package/dist/dist-YOVM5HEY.js.map +0 -1
package/README.md
CHANGED
|
@@ -50,9 +50,50 @@ emulate list
|
|
|
50
50
|
| `-p, --port` | `4000` | Base port (auto-increments per service) |
|
|
51
51
|
| `-s, --service` | all | Comma-separated services to enable |
|
|
52
52
|
| `--seed` | auto-detect | Path to seed config (YAML or JSON) |
|
|
53
|
+
| `--base-url` | none | Override advertised base URL (supports `{service}` template) |
|
|
54
|
+
| `--portless` | off | Serve over HTTPS via portless (auto-registers aliases) |
|
|
53
55
|
|
|
54
56
|
The port can also be set via `EMULATE_PORT` or `PORT` environment variables.
|
|
55
57
|
|
|
58
|
+
## HTTPS with portless
|
|
59
|
+
|
|
60
|
+
[portless](https://github.com/vercel-labs/portless) gives emulators trusted HTTPS URLs with auto-generated certs and no browser warnings.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Start the portless proxy (first time only)
|
|
64
|
+
portless proxy start
|
|
65
|
+
|
|
66
|
+
# Start emulate with portless integration
|
|
67
|
+
emulate start --portless
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Each service registers as a portless alias and gets a named HTTPS URL:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
github https://github.emulate.localhost
|
|
74
|
+
google https://google.emulate.localhost
|
|
75
|
+
slack https://slack.emulate.localhost
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
If portless is not installed, emulate will prompt to install it (`npm i -g portless`).
|
|
79
|
+
|
|
80
|
+
The `--portless` flag overwrites any existing portless aliases matching `*.emulate`. Aliases are removed automatically when emulate shuts down.
|
|
81
|
+
|
|
82
|
+
For a custom base URL without portless (any reverse proxy), use `--base-url` or the `EMULATE_BASE_URL` env var:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
emulate start --base-url "https://{service}.myproxy.test"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The `PORTLESS_URL` env var is automatically set by the `portless` CLI wrapper when running a command through it (e.g. `portless github.emulate emulate start`), typically to a value like `https://{service}.emulate.localhost`. It supports `{service}` interpolation, just like `--base-url` and `EMULATE_BASE_URL`. When no explicit `baseUrl` is provided, it is used as a fallback.
|
|
89
|
+
|
|
90
|
+
Per-service overrides are also supported in the seed config (these take highest priority over all other base URL sources):
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
github:
|
|
94
|
+
baseUrl: https://github.emulate.localhost
|
|
95
|
+
```
|
|
96
|
+
|
|
56
97
|
## Programmatic API
|
|
57
98
|
|
|
58
99
|
```bash
|
|
@@ -103,6 +144,7 @@ afterAll(() => Promise.all([github.close(), vercel.close()]))
|
|
|
103
144
|
| `service` | *(required)* | Service name: `'vercel'`, `'github'`, `'google'`, `'slack'`, `'apple'`, `'microsoft'`, or `'aws'` |
|
|
104
145
|
| `port` | `4000` | Port for the HTTP server |
|
|
105
146
|
| `seed` | none | Inline seed data (same shape as YAML config) |
|
|
147
|
+
| `baseUrl` | none | Override advertised base URL. Per-service `baseUrl` in seed config takes highest priority, then this option, then `EMULATE_BASE_URL` env var (supports `{service}`), then `PORTLESS_URL` (supports `{service}`, automatically set by the `portless` CLI wrapper), then `http://localhost:<port>`. |
|
|
106
148
|
|
|
107
149
|
### Instance methods
|
|
108
150
|
|
|
@@ -153,6 +195,9 @@ google:
|
|
|
153
195
|
users:
|
|
154
196
|
- email: testuser@example.com
|
|
155
197
|
name: Test User
|
|
198
|
+
- email: admin@acme.com
|
|
199
|
+
name: Admin
|
|
200
|
+
hd: acme.com
|
|
156
201
|
oauth_clients:
|
|
157
202
|
- client_id: my-client-id.apps.googleusercontent.com
|
|
158
203
|
client_secret: GOCSPX-secret
|
|
@@ -612,18 +657,22 @@ Microsoft Entra ID (Azure AD) v2.0 OAuth 2.0 and OpenID Connect emulation with a
|
|
|
612
657
|
|
|
613
658
|
## AWS
|
|
614
659
|
|
|
615
|
-
S3, SQS, IAM, and STS emulation with
|
|
660
|
+
S3, SQS, IAM, and STS emulation with AWS SDK-compatible S3 paths and query-style SQS/IAM/STS endpoints. All responses use AWS-compatible XML.
|
|
616
661
|
|
|
617
662
|
### S3
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
- `
|
|
622
|
-
- `
|
|
623
|
-
- `
|
|
624
|
-
- `
|
|
625
|
-
- `
|
|
626
|
-
- `
|
|
663
|
+
|
|
664
|
+
S3 routes use root paths matching the real AWS S3 wire format, so the official AWS SDK works out of the box with `forcePathStyle: true`. Legacy `/s3/` prefixed paths are also supported for backward compatibility.
|
|
665
|
+
|
|
666
|
+
- `GET /` - list all buckets
|
|
667
|
+
- `PUT /:bucket` - create bucket
|
|
668
|
+
- `DELETE /:bucket` - delete bucket
|
|
669
|
+
- `HEAD /:bucket` - check existence
|
|
670
|
+
- `GET /:bucket` - list objects (prefix, delimiter, max-keys, continuation-token, start-after)
|
|
671
|
+
- `POST /:bucket` - presigned POST upload (browser-style multipart form with policy validation)
|
|
672
|
+
- `PUT /:bucket/:key` - put object (supports copy via `x-amz-copy-source`)
|
|
673
|
+
- `GET /:bucket/:key` - get object
|
|
674
|
+
- `HEAD /:bucket/:key` - head object
|
|
675
|
+
- `DELETE /:bucket/:key` - delete object
|
|
627
676
|
|
|
628
677
|
### SQS
|
|
629
678
|
All operations via `POST /sqs/` with `Action` parameter:
|
package/dist/api.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const SERVICE_NAME_LIST: readonly ["vercel", "github", "google", "slack", "apple", "microsoft", "okta", "aws", "resend", "stripe", "mongoatlas"];
|
|
1
|
+
declare const SERVICE_NAME_LIST: readonly ["vercel", "github", "google", "slack", "apple", "microsoft", "okta", "aws", "resend", "stripe", "mongoatlas", "clerk"];
|
|
2
2
|
type ServiceName = (typeof SERVICE_NAME_LIST)[number];
|
|
3
3
|
|
|
4
4
|
interface SeedConfig {
|
|
@@ -12,6 +12,7 @@ interface EmulatorOptions {
|
|
|
12
12
|
service: ServiceName;
|
|
13
13
|
port?: number;
|
|
14
14
|
seed?: SeedConfig;
|
|
15
|
+
baseUrl?: string;
|
|
15
16
|
}
|
|
16
17
|
interface Emulator {
|
|
17
18
|
url: string;
|
package/dist/api.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
Hono,
|
|
7
7
|
cors
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-AQ2CLRU3.js";
|
|
9
9
|
|
|
10
10
|
// ../@emulators/core/dist/index.js
|
|
11
11
|
import { createHmac } from "crypto";
|
|
@@ -387,9 +387,7 @@ function authMiddleware(tokens, appKeyResolver, fallbackUser) {
|
|
|
387
387
|
if (token.startsWith("eyJ") && appKeyResolver) {
|
|
388
388
|
try {
|
|
389
389
|
const [, payloadB64] = token.split(".");
|
|
390
|
-
const payload = JSON.parse(
|
|
391
|
-
Buffer.from(payloadB64, "base64url").toString()
|
|
392
|
-
);
|
|
390
|
+
const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
|
|
393
391
|
const appId = typeof payload.iss === "string" ? parseInt(payload.iss, 10) : payload.iss;
|
|
394
392
|
if (typeof appId === "number" && !isNaN(appId)) {
|
|
395
393
|
const appInfo = appKeyResolver(appId);
|
|
@@ -426,6 +424,7 @@ var FONTS = {
|
|
|
426
424
|
"geist-sans.woff2": readFileSync(join(__dirname, "fonts", "geist-sans.woff2")),
|
|
427
425
|
"GeistPixel-Square.woff2": readFileSync(join(__dirname, "fonts", "GeistPixel-Square.woff2"))
|
|
428
426
|
};
|
|
427
|
+
var FAVICON = readFileSync(join(__dirname, "fonts", "favicon.ico"));
|
|
429
428
|
function registerFontRoutes(app) {
|
|
430
429
|
app.get("/_emulate/fonts/:name", (c) => {
|
|
431
430
|
const name = c.req.param("name");
|
|
@@ -439,6 +438,14 @@ function registerFontRoutes(app) {
|
|
|
439
438
|
}
|
|
440
439
|
});
|
|
441
440
|
});
|
|
441
|
+
app.get("/_emulate/favicon.ico", (c) => {
|
|
442
|
+
return new Response(FAVICON, {
|
|
443
|
+
headers: {
|
|
444
|
+
"Content-Type": "image/x-icon",
|
|
445
|
+
"Cache-Control": "public, max-age=31536000, immutable"
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
});
|
|
442
449
|
}
|
|
443
450
|
function createServer(plugin, options = {}) {
|
|
444
451
|
const port = options.port ?? 4e3;
|
|
@@ -513,7 +520,7 @@ var SERVICE_REGISTRY = {
|
|
|
513
520
|
label: "Vercel REST API emulator",
|
|
514
521
|
endpoints: "projects, deployments, domains, env vars, users, teams, file uploads, protection bypass",
|
|
515
522
|
async load() {
|
|
516
|
-
const mod = await import("./dist-
|
|
523
|
+
const mod = await import("./dist-4X2KPMAJ.js");
|
|
517
524
|
return { plugin: mod.vercelPlugin, seedFromConfig: mod.seedFromConfig };
|
|
518
525
|
},
|
|
519
526
|
defaultFallback(cfg) {
|
|
@@ -525,12 +532,14 @@ var SERVICE_REGISTRY = {
|
|
|
525
532
|
users: [{ username: "developer", name: "Developer", email: "dev@example.com" }],
|
|
526
533
|
teams: [{ slug: "my-team", name: "My Team" }],
|
|
527
534
|
projects: [{ name: "my-app", team: "my-team", framework: "nextjs" }],
|
|
528
|
-
integrations: [
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
535
|
+
integrations: [
|
|
536
|
+
{
|
|
537
|
+
client_id: "oac_example_client_id",
|
|
538
|
+
client_secret: "example_client_secret",
|
|
539
|
+
name: "My Vercel App",
|
|
540
|
+
redirect_uris: ["http://localhost:3000/api/auth/callback/vercel"]
|
|
541
|
+
}
|
|
542
|
+
]
|
|
534
543
|
}
|
|
535
544
|
}
|
|
536
545
|
},
|
|
@@ -538,7 +547,7 @@ var SERVICE_REGISTRY = {
|
|
|
538
547
|
label: "GitHub REST API emulator",
|
|
539
548
|
endpoints: "users, repos, issues, PRs, comments, reviews, labels, milestones, branches, git data, orgs, teams, releases, webhooks, search, actions, checks, rate limit",
|
|
540
549
|
async load() {
|
|
541
|
-
const mod = await import("./dist-
|
|
550
|
+
const mod = await import("./dist-REDHDZ3V.js");
|
|
542
551
|
return {
|
|
543
552
|
plugin: mod.githubPlugin,
|
|
544
553
|
seedFromConfig: mod.seedFromConfig,
|
|
@@ -562,25 +571,42 @@ var SERVICE_REGISTRY = {
|
|
|
562
571
|
},
|
|
563
572
|
initConfig: {
|
|
564
573
|
github: {
|
|
565
|
-
users: [
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
574
|
+
users: [
|
|
575
|
+
{
|
|
576
|
+
login: "octocat",
|
|
577
|
+
name: "The Octocat",
|
|
578
|
+
email: "octocat@github.com",
|
|
579
|
+
bio: "I am the Octocat",
|
|
580
|
+
company: "GitHub",
|
|
581
|
+
location: "San Francisco"
|
|
582
|
+
}
|
|
583
|
+
],
|
|
573
584
|
orgs: [{ login: "my-org", name: "My Organization", description: "A test organization" }],
|
|
574
585
|
repos: [
|
|
575
|
-
{
|
|
576
|
-
|
|
586
|
+
{
|
|
587
|
+
owner: "octocat",
|
|
588
|
+
name: "hello-world",
|
|
589
|
+
description: "My first repository",
|
|
590
|
+
language: "JavaScript",
|
|
591
|
+
topics: ["hello", "world"],
|
|
592
|
+
auto_init: true
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
owner: "my-org",
|
|
596
|
+
name: "org-repo",
|
|
597
|
+
description: "An organization repository",
|
|
598
|
+
language: "TypeScript",
|
|
599
|
+
auto_init: true
|
|
600
|
+
}
|
|
577
601
|
],
|
|
578
|
-
oauth_apps: [
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
602
|
+
oauth_apps: [
|
|
603
|
+
{
|
|
604
|
+
client_id: "Iv1.example_client_id",
|
|
605
|
+
client_secret: "example_client_secret",
|
|
606
|
+
name: "My App",
|
|
607
|
+
redirect_uris: ["http://localhost:3000/api/auth/callback/github"]
|
|
608
|
+
}
|
|
609
|
+
]
|
|
584
610
|
}
|
|
585
611
|
}
|
|
586
612
|
},
|
|
@@ -588,7 +614,7 @@ var SERVICE_REGISTRY = {
|
|
|
588
614
|
label: "Google OAuth 2.0 / OpenID Connect + Gmail, Calendar, and Drive emulator",
|
|
589
615
|
endpoints: "OAuth authorize, token exchange, userinfo, OIDC discovery, token revocation, Gmail messages/drafts/threads/labels/history/settings, Calendar lists/events/freebusy, Drive files/uploads",
|
|
590
616
|
async load() {
|
|
591
|
-
const mod = await import("./dist-
|
|
617
|
+
const mod = await import("./dist-KKTYBE5S.js");
|
|
592
618
|
return { plugin: mod.googlePlugin, seedFromConfig: mod.seedFromConfig };
|
|
593
619
|
},
|
|
594
620
|
defaultFallback(cfg) {
|
|
@@ -597,34 +623,72 @@ var SERVICE_REGISTRY = {
|
|
|
597
623
|
},
|
|
598
624
|
initConfig: {
|
|
599
625
|
google: {
|
|
600
|
-
users: [
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
626
|
+
users: [
|
|
627
|
+
{
|
|
628
|
+
email: "testuser@example.com",
|
|
629
|
+
name: "Test User",
|
|
630
|
+
picture: "https://lh3.googleusercontent.com/a/default-user",
|
|
631
|
+
email_verified: true
|
|
632
|
+
}
|
|
633
|
+
],
|
|
634
|
+
oauth_clients: [
|
|
635
|
+
{
|
|
636
|
+
client_id: "example-client-id.apps.googleusercontent.com",
|
|
637
|
+
client_secret: "GOCSPX-example_secret",
|
|
638
|
+
name: "Code App (Google)",
|
|
639
|
+
redirect_uris: ["http://localhost:3000/api/auth/callback/google"]
|
|
640
|
+
}
|
|
641
|
+
],
|
|
642
|
+
labels: [
|
|
643
|
+
{
|
|
644
|
+
id: "Label_ops",
|
|
645
|
+
user_email: "testuser@example.com",
|
|
646
|
+
name: "Ops/Review",
|
|
647
|
+
color_background: "#DDEEFF",
|
|
648
|
+
color_text: "#111111"
|
|
649
|
+
}
|
|
650
|
+
],
|
|
651
|
+
messages: [
|
|
652
|
+
{
|
|
653
|
+
id: "msg_welcome",
|
|
654
|
+
user_email: "testuser@example.com",
|
|
655
|
+
from: "welcome@example.com",
|
|
656
|
+
to: "testuser@example.com",
|
|
657
|
+
subject: "Welcome to the Gmail emulator",
|
|
658
|
+
body_text: "You can now test Gmail, Calendar, and Drive flows locally.",
|
|
659
|
+
label_ids: ["INBOX", "UNREAD", "CATEGORY_UPDATES"],
|
|
660
|
+
date: "2025-01-04T10:00:00.000Z"
|
|
661
|
+
}
|
|
662
|
+
],
|
|
663
|
+
calendars: [
|
|
664
|
+
{
|
|
665
|
+
id: "primary",
|
|
666
|
+
user_email: "testuser@example.com",
|
|
667
|
+
summary: "testuser@example.com",
|
|
668
|
+
primary: true,
|
|
669
|
+
selected: true,
|
|
670
|
+
time_zone: "UTC"
|
|
671
|
+
}
|
|
672
|
+
],
|
|
673
|
+
calendar_events: [
|
|
674
|
+
{
|
|
675
|
+
id: "evt_kickoff",
|
|
676
|
+
user_email: "testuser@example.com",
|
|
677
|
+
calendar_id: "primary",
|
|
678
|
+
summary: "Project Kickoff",
|
|
679
|
+
start_date_time: "2025-01-10T09:00:00.000Z",
|
|
680
|
+
end_date_time: "2025-01-10T09:30:00.000Z"
|
|
681
|
+
}
|
|
682
|
+
],
|
|
683
|
+
drive_items: [
|
|
684
|
+
{
|
|
685
|
+
id: "drv_docs",
|
|
686
|
+
user_email: "testuser@example.com",
|
|
687
|
+
name: "Docs",
|
|
688
|
+
mime_type: "application/vnd.google-apps.folder",
|
|
689
|
+
parent_ids: ["root"]
|
|
690
|
+
}
|
|
691
|
+
]
|
|
628
692
|
}
|
|
629
693
|
}
|
|
630
694
|
},
|
|
@@ -632,7 +696,7 @@ var SERVICE_REGISTRY = {
|
|
|
632
696
|
label: "Slack API emulator",
|
|
633
697
|
endpoints: "auth, chat, conversations, users, reactions, team, OAuth, incoming webhooks",
|
|
634
698
|
async load() {
|
|
635
|
-
const mod = await import("./dist-
|
|
699
|
+
const mod = await import("./dist-CE6BUCWQ.js");
|
|
636
700
|
return { plugin: mod.slackPlugin, seedFromConfig: mod.seedFromConfig };
|
|
637
701
|
},
|
|
638
702
|
defaultFallback() {
|
|
@@ -642,14 +706,19 @@ var SERVICE_REGISTRY = {
|
|
|
642
706
|
slack: {
|
|
643
707
|
team: { name: "My Workspace", domain: "my-workspace" },
|
|
644
708
|
users: [{ name: "developer", real_name: "Developer", email: "dev@example.com" }],
|
|
645
|
-
channels: [
|
|
709
|
+
channels: [
|
|
710
|
+
{ name: "general", topic: "General discussion" },
|
|
711
|
+
{ name: "random", topic: "Random stuff" }
|
|
712
|
+
],
|
|
646
713
|
bots: [{ name: "my-bot" }],
|
|
647
|
-
oauth_apps: [
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
714
|
+
oauth_apps: [
|
|
715
|
+
{
|
|
716
|
+
client_id: "12345.67890",
|
|
717
|
+
client_secret: "example_client_secret",
|
|
718
|
+
name: "My Slack App",
|
|
719
|
+
redirect_uris: ["http://localhost:3000/api/auth/callback/slack"]
|
|
720
|
+
}
|
|
721
|
+
]
|
|
653
722
|
}
|
|
654
723
|
}
|
|
655
724
|
},
|
|
@@ -657,7 +726,7 @@ var SERVICE_REGISTRY = {
|
|
|
657
726
|
label: "Apple Sign In / OAuth emulator",
|
|
658
727
|
endpoints: "OAuth authorize, token exchange, JWKS",
|
|
659
728
|
async load() {
|
|
660
|
-
const mod = await import("./dist-
|
|
729
|
+
const mod = await import("./dist-CFST4X4K.js");
|
|
661
730
|
return { plugin: mod.applePlugin, seedFromConfig: mod.seedFromConfig };
|
|
662
731
|
},
|
|
663
732
|
defaultFallback(cfg) {
|
|
@@ -667,12 +736,14 @@ var SERVICE_REGISTRY = {
|
|
|
667
736
|
initConfig: {
|
|
668
737
|
apple: {
|
|
669
738
|
users: [{ email: "testuser@icloud.com", name: "Test User" }],
|
|
670
|
-
oauth_clients: [
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
739
|
+
oauth_clients: [
|
|
740
|
+
{
|
|
741
|
+
client_id: "com.example.app",
|
|
742
|
+
team_id: "TEAM001",
|
|
743
|
+
name: "My Apple App",
|
|
744
|
+
redirect_uris: ["http://localhost:3000/api/auth/callback/apple"]
|
|
745
|
+
}
|
|
746
|
+
]
|
|
676
747
|
}
|
|
677
748
|
}
|
|
678
749
|
},
|
|
@@ -680,7 +751,7 @@ var SERVICE_REGISTRY = {
|
|
|
680
751
|
label: "Microsoft Entra ID OAuth 2.0 / OpenID Connect emulator",
|
|
681
752
|
endpoints: "OAuth authorize, token exchange, userinfo, OIDC discovery, Graph /me, logout, token revocation",
|
|
682
753
|
async load() {
|
|
683
|
-
const mod = await import("./dist-
|
|
754
|
+
const mod = await import("./dist-ETHHYBGF.js");
|
|
684
755
|
return { plugin: mod.microsoftPlugin, seedFromConfig: mod.seedFromConfig };
|
|
685
756
|
},
|
|
686
757
|
defaultFallback(cfg) {
|
|
@@ -690,12 +761,14 @@ var SERVICE_REGISTRY = {
|
|
|
690
761
|
initConfig: {
|
|
691
762
|
microsoft: {
|
|
692
763
|
users: [{ email: "testuser@outlook.com", name: "Test User" }],
|
|
693
|
-
oauth_clients: [
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
764
|
+
oauth_clients: [
|
|
765
|
+
{
|
|
766
|
+
client_id: "example-client-id",
|
|
767
|
+
client_secret: "example-client-secret",
|
|
768
|
+
name: "My Microsoft App",
|
|
769
|
+
redirect_uris: ["http://localhost:3000/api/auth/callback/microsoft-entra-id"]
|
|
770
|
+
}
|
|
771
|
+
]
|
|
699
772
|
}
|
|
700
773
|
}
|
|
701
774
|
},
|
|
@@ -703,7 +776,7 @@ var SERVICE_REGISTRY = {
|
|
|
703
776
|
label: "Okta OAuth 2.0 / OpenID Connect + management API emulator",
|
|
704
777
|
endpoints: "OIDC discovery, JWKS, OAuth authorize/token/userinfo/introspect/revoke/logout, users, groups, apps, authorization servers",
|
|
705
778
|
async load() {
|
|
706
|
-
const mod = await import("./dist-
|
|
779
|
+
const mod = await import("./dist-5JVGPOL3.js");
|
|
707
780
|
return { plugin: mod.oktaPlugin, seedFromConfig: mod.seedFromConfig };
|
|
708
781
|
},
|
|
709
782
|
defaultFallback(cfg) {
|
|
@@ -715,13 +788,15 @@ var SERVICE_REGISTRY = {
|
|
|
715
788
|
users: [{ login: "testuser@okta.local", email: "testuser@okta.local", first_name: "Test", last_name: "User" }],
|
|
716
789
|
groups: [{ name: "Everyone", description: "All users", type: "BUILT_IN", okta_id: "00g_everyone" }],
|
|
717
790
|
authorization_servers: [{ id: "default", name: "default", audiences: ["api://default"] }],
|
|
718
|
-
oauth_clients: [
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
791
|
+
oauth_clients: [
|
|
792
|
+
{
|
|
793
|
+
client_id: "okta-test-client",
|
|
794
|
+
client_secret: "okta-test-secret",
|
|
795
|
+
name: "Sample OIDC Client",
|
|
796
|
+
redirect_uris: ["http://localhost:3000/callback"],
|
|
797
|
+
auth_server_id: "default"
|
|
798
|
+
}
|
|
799
|
+
]
|
|
725
800
|
}
|
|
726
801
|
}
|
|
727
802
|
},
|
|
@@ -729,7 +804,7 @@ var SERVICE_REGISTRY = {
|
|
|
729
804
|
label: "AWS cloud service emulator",
|
|
730
805
|
endpoints: "S3 (buckets, objects), SQS (queues, messages), IAM (users, roles, access keys), STS (assume role, caller identity)",
|
|
731
806
|
async load() {
|
|
732
|
-
const mod = await import("./dist-
|
|
807
|
+
const mod = await import("./dist-LDUHEJAN.js");
|
|
733
808
|
return { plugin: mod.awsPlugin, seedFromConfig: mod.seedFromConfig };
|
|
734
809
|
},
|
|
735
810
|
defaultFallback() {
|
|
@@ -751,7 +826,7 @@ var SERVICE_REGISTRY = {
|
|
|
751
826
|
label: "Resend email API emulator",
|
|
752
827
|
endpoints: "emails, domains, contacts, API keys, inbox UI",
|
|
753
828
|
async load() {
|
|
754
|
-
const mod = await import("./dist-
|
|
829
|
+
const mod = await import("./dist-IBXD3O6A.js");
|
|
755
830
|
return { plugin: mod.resendPlugin, seedFromConfig: mod.seedFromConfig };
|
|
756
831
|
},
|
|
757
832
|
defaultFallback() {
|
|
@@ -766,9 +841,9 @@ var SERVICE_REGISTRY = {
|
|
|
766
841
|
},
|
|
767
842
|
stripe: {
|
|
768
843
|
label: "Stripe payments emulator",
|
|
769
|
-
endpoints: "customers, payment intents, charges, products, prices, checkout sessions, webhooks",
|
|
844
|
+
endpoints: "customers, payment methods, customer sessions, payment intents, charges, products, prices, checkout sessions, webhooks",
|
|
770
845
|
async load() {
|
|
771
|
-
const mod = await import("./dist-
|
|
846
|
+
const mod = await import("./dist-ENKE2S7V.js");
|
|
772
847
|
return { plugin: mod.stripePlugin, seedFromConfig: mod.seedFromConfig };
|
|
773
848
|
},
|
|
774
849
|
defaultFallback() {
|
|
@@ -786,7 +861,7 @@ var SERVICE_REGISTRY = {
|
|
|
786
861
|
label: "MongoDB Atlas service emulator",
|
|
787
862
|
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
863
|
async load() {
|
|
789
|
-
const mod = await import("./dist-
|
|
864
|
+
const mod = await import("./dist-PWGOAQC6.js");
|
|
790
865
|
return { plugin: mod.mongoatlasPlugin, seedFromConfig: mod.seedFromConfig };
|
|
791
866
|
},
|
|
792
867
|
defaultFallback() {
|
|
@@ -800,11 +875,71 @@ var SERVICE_REGISTRY = {
|
|
|
800
875
|
databases: [{ cluster: "Cluster0", name: "test", collections: ["items"] }]
|
|
801
876
|
}
|
|
802
877
|
}
|
|
878
|
+
},
|
|
879
|
+
clerk: {
|
|
880
|
+
label: "Clerk authentication and user management emulator",
|
|
881
|
+
endpoints: "OIDC discovery, JWKS, OAuth authorize/token/userinfo, users, email addresses, organizations, memberships, invitations, sessions",
|
|
882
|
+
async load() {
|
|
883
|
+
const mod = await import("./dist-J6LHUR52.js");
|
|
884
|
+
return { plugin: mod.clerkPlugin, seedFromConfig: mod.seedFromConfig };
|
|
885
|
+
},
|
|
886
|
+
defaultFallback(cfg) {
|
|
887
|
+
const firstEmail = cfg?.users?.[0]?.email_addresses?.[0] ?? "test@example.com";
|
|
888
|
+
return { login: firstEmail, id: 1, scopes: [] };
|
|
889
|
+
},
|
|
890
|
+
initConfig: {
|
|
891
|
+
clerk: {
|
|
892
|
+
users: [
|
|
893
|
+
{
|
|
894
|
+
first_name: "Test",
|
|
895
|
+
last_name: "User",
|
|
896
|
+
email_addresses: ["test@example.com"],
|
|
897
|
+
password: "clerk_test_password"
|
|
898
|
+
}
|
|
899
|
+
],
|
|
900
|
+
organizations: [
|
|
901
|
+
{
|
|
902
|
+
name: "My Company",
|
|
903
|
+
slug: "my-company",
|
|
904
|
+
members: [{ email: "test@example.com", role: "admin" }]
|
|
905
|
+
}
|
|
906
|
+
],
|
|
907
|
+
oauth_applications: [
|
|
908
|
+
{
|
|
909
|
+
client_id: "clerk_emulate_client",
|
|
910
|
+
client_secret: "clerk_emulate_secret",
|
|
911
|
+
name: "Emulate App",
|
|
912
|
+
redirect_uris: ["http://localhost:3000/api/auth/callback/clerk"]
|
|
913
|
+
}
|
|
914
|
+
]
|
|
915
|
+
}
|
|
916
|
+
}
|
|
803
917
|
}
|
|
804
918
|
};
|
|
805
919
|
|
|
806
920
|
// src/api.ts
|
|
807
921
|
import { serve } from "@hono/node-server";
|
|
922
|
+
|
|
923
|
+
// src/base-url.ts
|
|
924
|
+
function resolveBaseUrl(opts) {
|
|
925
|
+
if (opts.seedBaseUrl) {
|
|
926
|
+
return opts.seedBaseUrl.replace(/\{service\}/g, opts.service);
|
|
927
|
+
}
|
|
928
|
+
if (opts.baseUrl) {
|
|
929
|
+
return opts.baseUrl.replace(/\{service\}/g, opts.service);
|
|
930
|
+
}
|
|
931
|
+
const envBaseUrl = process.env.EMULATE_BASE_URL;
|
|
932
|
+
if (envBaseUrl) {
|
|
933
|
+
return envBaseUrl.replace(/\{service\}/g, opts.service);
|
|
934
|
+
}
|
|
935
|
+
const portlessUrl = process.env.PORTLESS_URL;
|
|
936
|
+
if (portlessUrl) {
|
|
937
|
+
return portlessUrl.replace(/\{service\}/g, opts.service);
|
|
938
|
+
}
|
|
939
|
+
return `http://localhost:${opts.port}`;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// src/api.ts
|
|
808
943
|
async function createEmulator(options) {
|
|
809
944
|
const { service, port = 4e3, seed: seedConfig } = options;
|
|
810
945
|
const entry = SERVICE_REGISTRY[service];
|
|
@@ -821,17 +956,18 @@ async function createEmulator(options) {
|
|
|
821
956
|
} else {
|
|
822
957
|
tokens["test_token_admin"] = { login: "admin", id: 2, scopes: ["repo", "user", "admin:org", "admin:repo_hook"] };
|
|
823
958
|
}
|
|
824
|
-
const
|
|
959
|
+
const svcSeedConfig = seedConfig?.[service];
|
|
960
|
+
const seedBaseUrl = typeof svcSeedConfig?.baseUrl === "string" && svcSeedConfig.baseUrl.length > 0 ? svcSeedConfig.baseUrl : void 0;
|
|
961
|
+
const baseUrl = resolveBaseUrl({ service, port, baseUrl: options.baseUrl, seedBaseUrl });
|
|
825
962
|
let cachedResolver;
|
|
826
963
|
const appKeyResolver = loaded.createAppKeyResolver ? (appId) => cachedResolver(appId) : void 0;
|
|
827
|
-
const svcSeedConfig = seedConfig?.[service];
|
|
828
964
|
const fallbackUser = entry.defaultFallback(svcSeedConfig);
|
|
829
|
-
const { app, store } = createServer(loaded.plugin, { port, baseUrl, tokens, appKeyResolver, fallbackUser });
|
|
965
|
+
const { app, store, webhooks } = createServer(loaded.plugin, { port, baseUrl, tokens, appKeyResolver, fallbackUser });
|
|
830
966
|
cachedResolver = loaded.createAppKeyResolver?.(store);
|
|
831
967
|
const seed = () => {
|
|
832
968
|
loaded.plugin.seed?.(store, baseUrl);
|
|
833
969
|
if (svcSeedConfig && loaded.seedFromConfig) {
|
|
834
|
-
loaded.seedFromConfig(store, baseUrl, svcSeedConfig);
|
|
970
|
+
loaded.seedFromConfig(store, baseUrl, svcSeedConfig, webhooks);
|
|
835
971
|
}
|
|
836
972
|
};
|
|
837
973
|
seed();
|