@crvouga/mockingbird-service-oauth 1.11.0 → 2.0.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/CHANGELOG.md +23 -1
- package/DISCOVERY.md +55 -0
- package/README.md +3 -3
- package/SUPPORT.md +42 -0
- package/dist/{chunk-K4RVFKFZ.js → chunk-2BINFVVC.js} +44 -23
- package/dist/chunk-2BINFVVC.js.map +7 -0
- package/dist/{chunk-XQVBZTJA.js → chunk-I76SEED6.js} +641 -246
- package/dist/chunk-I76SEED6.js.map +7 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/server.d.ts +4 -0
- package/dist/server.js +2 -2
- package/openapi.yaml +2885 -0
- package/package.json +29 -4
- package/dist/chunk-K4RVFKFZ.js.map +0 -7
- package/dist/chunk-XQVBZTJA.js.map +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-oauth
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 2.0.0 (2026-10-04)
|
|
4
|
+
|
|
5
|
+
### ⚠️ Breaking changes
|
|
6
|
+
|
|
7
|
+
- isolate internal routes ([5e19b21](https://github.com/crvouga/mockingbird/commit/5e19b21d403acc74b04778a8b545e80bcbdf8d51))
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- add route filtering ([4e59b1d](https://github.com/crvouga/mockingbird/commit/4e59b1d42a76550f844b744f8431a637433a0781))
|
|
12
|
+
- publish agent package indexes ([eca61d3](https://github.com/crvouga/mockingbird/commit/eca61d358b86982cf0b777ae8bd7feaa3663aaa6))
|
|
13
|
+
|
|
14
|
+
### Fixes and improvements
|
|
15
|
+
|
|
16
|
+
- honor configured prefixes ([4f569d7](https://github.com/crvouga/mockingbird/commit/4f569d74ca8089f2ed5e33a1abf32b90973f41dd))
|
|
17
|
+
- explain configurable internal paths ([21b47ae](https://github.com/crvouga/mockingbird/commit/21b47ae4d4a096cdf63189aaeaf721ccb369bcf3))
|
|
18
|
+
- parse ISO journal timestamps ([20b39a2](https://github.com/crvouga/mockingbird/commit/20b39a2c738e278533d610dfc91f1cb3ae9b3f1e))
|
|
19
|
+
- harden shared controls ([7822614](https://github.com/crvouga/mockingbird/commit/7822614f6cba0387aeb60ba7320b0379aa1ef690))
|
|
20
|
+
|
|
21
|
+
### Dependencies
|
|
22
|
+
|
|
23
|
+
- `@crvouga/mockingbird-service-sqlite`
|
|
24
|
+
|
|
25
|
+
## 1.11.0 (2026-10-01)
|
|
4
26
|
|
|
5
27
|
### Features
|
|
6
28
|
|
package/DISCOVERY.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# @crvouga/mockingbird-service-oauth discovery
|
|
2
|
+
|
|
3
|
+
This is the installed-package index for coding agents and tooling. All relative links resolve
|
|
4
|
+
inside `node_modules/@crvouga/mockingbird-service-oauth/`; no repository checkout is needed to discover the mock's
|
|
5
|
+
supported surface or documented behavior.
|
|
6
|
+
|
|
7
|
+
## Capability and behavior sources
|
|
8
|
+
|
|
9
|
+
| Question | Authoritative file | What it contains |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| Behaviour and integration | [`README.md`](README.md) | Routes, state transitions, auth, webhooks, controls, presets and deliberate omissions. |
|
|
12
|
+
| Exact capabilities | [`SUPPORT.md`](SUPPORT.md) | Supported, unsupported and parity-covered operations or commands, including reasons for gaps. |
|
|
13
|
+
| Wire contract | [`openapi.yaml`](openapi.yaml) | Machine-readable paths, methods, schemas, responses and parity annotations. |
|
|
14
|
+
| Public API | [`dist/index.d.ts`](dist/index.d.ts) | The installed package's exact TypeScript exports and signatures. |
|
|
15
|
+
| Package metadata | [`package.json`](package.json) | Runtime/entry-point claims, vendor links, parity scope/tier and `mockingbird.discovery`. |
|
|
16
|
+
|
|
17
|
+
Read these together: the contract/capability matrix says *what* is available, while the README
|
|
18
|
+
defines stateful behavior, lifecycle rules, test controls, and intentional oracle differences.
|
|
19
|
+
If prose and an executable surface disagree, report a parity mismatch instead of adding a
|
|
20
|
+
consumer-side workaround.
|
|
21
|
+
|
|
22
|
+
## Parity and oracle
|
|
23
|
+
|
|
24
|
+
- Declared parity surface: **OIDC and social login**.
|
|
25
|
+
- Parity tier: **cold** (the repository controls when live checks run).
|
|
26
|
+
- Oracle: **Live vendor API or sandbox**.
|
|
27
|
+
- Repository command: `bun run parity:service -- oauth`.
|
|
28
|
+
- Evidence model: Run from a Mockingbird checkout; credentials come only from .env.local or GitHub Actions secrets. Missing credentials exit 2.
|
|
29
|
+
|
|
30
|
+
The npm package contains evidence summaries and the exact contract, not credentials or the
|
|
31
|
+
repository-only parity harness. Self-parity/property and acceptance tests run in the Mockingbird
|
|
32
|
+
repository; live parity is an additional oracle check, not a substitute for the packaged matrix.
|
|
33
|
+
|
|
34
|
+
## Runtime introspection
|
|
35
|
+
|
|
36
|
+
- `GET /__admin/health`
|
|
37
|
+
- `GET /__admin`
|
|
38
|
+
- `GET /__admin/state`
|
|
39
|
+
- `GET /__admin/requests`
|
|
40
|
+
- `GET /__admin/metrics`
|
|
41
|
+
- `GET /__admin/faults/presets`
|
|
42
|
+
- `GET /__admin/ui`
|
|
43
|
+
|
|
44
|
+
For HTTP services, use `x-mockingbird-namespace` (or the documented credential/path carrier) so
|
|
45
|
+
parallel tests do not share state. Admin state, journal, metrics and fault-preset endpoints are
|
|
46
|
+
designed for assertions and diagnosis by consuming test suites.
|
|
47
|
+
|
|
48
|
+
## Report a mismatch or missing capability
|
|
49
|
+
|
|
50
|
+
Follow the [agent reporting contract](https://github.com/crvouga/mockingbird/blob/main/docs/REPORTING_ISSUES.md). Include package version,
|
|
51
|
+
operation/command, a minimal redacted request, actual mock result, expected oracle result or vendor
|
|
52
|
+
documentation, and whether the mismatch appears in the matrix. Never include keys, tokens,
|
|
53
|
+
customer data, prompts, PHI, card data, or unredacted recordings.
|
|
54
|
+
|
|
55
|
+
Service key: `oauth`.
|
package/README.md
CHANGED
|
@@ -71,7 +71,7 @@ Override the authorization, token, userinfo and JWKS endpoints in your applicati
|
|
|
71
71
|
|
|
72
72
|
For example, an Auth.js-style OIDC provider can use `type: "oidc"`, `issuer: "http://localhost:8810"`, `clientId`, `clientSecret`, and `checks: ["pkce", "state"]`. For existing Google/Apple presets, override **all** remote endpoints and issuer validation; changing the authorization URL alone is insufficient. In-process HTTP clients can route requests to `identity.fetch`. Browser navigation must reach a served mock or a service worker that routes those requests.
|
|
73
73
|
|
|
74
|
-
The issuer defaults to the incoming origin (and `/ns/<name>` when used). Set `issuer` to the public URL behind a reverse proxy; it may include a mount path. Avoid a fixed issuer shared across namespaces: use the namespace URL and its own discovery/JWKS so each namespace remains an independent issuer.
|
|
74
|
+
The issuer defaults to the incoming origin (and `/__admin/ns/<name>` when used). Set `issuer` to the public URL behind a reverse proxy; it may include a mount path. Avoid a fixed issuer shared across namespaces: use the namespace URL and its own discovery/JWKS so each namespace remains an independent issuer.
|
|
75
75
|
|
|
76
76
|
### Multiple providers on one listener
|
|
77
77
|
|
|
@@ -93,7 +93,7 @@ const server = await createMultiServer({
|
|
|
93
93
|
})
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
Use `/ns/<name>/<mount>/…` for URL-selected namespaces. `GET /health` reports every mount; `POST /__admin/reset?all=1` resets them atomically. Other aggregate admin requests select a runtime with `?mount=/google`, while mount-scoped controls are also available at `/google/__admin/*`. Duplicate or unsafe mount paths, duplicate explicit issuers, and duplicate client/key IDs within a mount fail before the listener starts.
|
|
96
|
+
Use `/__admin/ns/<name>/<mount>/…` for URL-selected namespaces. `GET /__admin/health` reports every mount; `POST /__admin/reset?all=1` resets them atomically. Other aggregate admin requests select a runtime with `?mount=/google`, while mount-scoped controls are also available at `/google/__admin/*`. Duplicate or unsafe mount paths, duplicate explicit issuers, and duplicate client/key IDs within a mount fail before the listener starts.
|
|
97
97
|
|
|
98
98
|
For the CLI, put the same `mounts` array in a JSON file and run `npx mockingbird-oauth serve --mounts oauth-mounts.json --port 8810`.
|
|
99
99
|
|
|
@@ -250,7 +250,7 @@ const runtime = createRuntime({
|
|
|
250
250
|
|
|
251
251
|
### Shared service controls
|
|
252
252
|
|
|
253
|
-
The runtime supplies `/health`, `/__admin/reset`, snapshots, mock clock, request journal, metrics, fault injection and namespace isolation. Use `x-mockingbird-namespace` for in-process tests or `/ns/<name>/…` for complete browser flows. Header-selected namespaces alone cannot persist across ordinary browser navigation. State, grants, sessions and consent live in the shared SQLite abstraction; there are no filesystem or Node imports in the main entry.
|
|
253
|
+
The runtime supplies `/__admin/health`, `/__admin/reset`, snapshots, mock clock, request journal, metrics, fault injection and namespace isolation. Use `x-mockingbird-namespace` for in-process tests or `/__admin/ns/<name>/…` for complete browser flows. Header-selected namespaces alone cannot persist across ordinary browser navigation. State, grants, sessions and consent live in the shared SQLite abstraction; there are no filesystem or Node imports in the main entry.
|
|
254
254
|
|
|
255
255
|
`OAUTH_PRESETS` includes `token_unavailable` and `access_denied`. Fault rules can also target a provider-specific path, e.g. `POST /__admin/faults` with `{"pathPrefix":"/auth/token","status":503,"body":{"error":"temporarily_unavailable"}}`. The only outbound requests are the Apple notifications above. Journals contain request metadata, never passwords or request bodies.
|
|
256
256
|
|
package/SUPPORT.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Mockingbird OAuth & Social Login — operation support
|
|
2
|
+
|
|
3
|
+
Generated from `openapi.yaml`; do not edit by hand.
|
|
4
|
+
|
|
5
|
+
- operations in spec: **32**
|
|
6
|
+
- supported by the mock: **32**
|
|
7
|
+
- parity enabled: **4**
|
|
8
|
+
|
|
9
|
+
| operationId | route | mock | parity | notes |
|
|
10
|
+
| --- | --- | --- | --- | --- |
|
|
11
|
+
| `Discovery` | `GET /.well-known/openid-configuration` | ✅ supported | ✅ | Google, Apple and Microsoft discovery metadata is checked against their public live endpoints by scripts/parity.ts. |
|
|
12
|
+
| `Jwks` | `GET /jwks` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
13
|
+
| `Authorize` | `GET /authorize` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
14
|
+
| `InteractionPage` | `GET /interaction` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. The pages carry the documented oauth-mock-* data-testid hooks (README, "Test hooks on the interaction pages"). |
|
|
15
|
+
| `Interact` | `POST /interaction` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. The pages carry the documented oauth-mock-* data-testid hooks (README, "Test hooks on the interaction pages"). |
|
|
16
|
+
| `Token` | `POST /token` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
17
|
+
| `UserInfo` | `GET /userinfo` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
18
|
+
| `UserInfoPost` | `POST /userinfo` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
19
|
+
| `Revoke` | `POST /revoke` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
20
|
+
| `GoogleAuthorize` | `GET /o/oauth2/v2/auth` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
21
|
+
| `GoogleLegacyAuthorize` | `GET /o/oauth2/auth` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
22
|
+
| `AppleAuthorize` | `GET /auth/authorize` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
23
|
+
| `AppleToken` | `POST /auth/token` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
24
|
+
| `AppleKeys` | `GET /auth/keys` | ✅ supported | ✅ | Apple's public signing-key contract is checked by scripts/parity.ts. |
|
|
25
|
+
| `GoogleKeys` | `GET /oauth2/v3/certs` | ✅ supported | ✅ | Google's public signing-key contract is checked by scripts/parity.ts. |
|
|
26
|
+
| `GoogleUserInfo` | `GET /v1/userinfo` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
27
|
+
| `GoogleUserInfoPost` | `POST /v1/userinfo` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
28
|
+
| `GoogleV3UserInfo` | `GET /oauth2/v3/userinfo` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
29
|
+
| `GoogleV3UserInfoPost` | `POST /oauth2/v3/userinfo` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
30
|
+
| `AppleRevoke` | `POST /auth/revoke` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
31
|
+
| `MicrosoftAuthorize` | `GET /oauth2/v2.0/authorize` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
32
|
+
| `MicrosoftToken` | `POST /oauth2/v2.0/token` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
33
|
+
| `MicrosoftKeys` | `GET /discovery/v2.0/keys` | ✅ supported | ✅ | Microsoft's public signing-key contract is checked by scripts/parity.ts. |
|
|
34
|
+
| `MicrosoftUserInfo` | `GET /oidc/userinfo` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
35
|
+
| `MicrosoftUserInfoPost` | `POST /oidc/userinfo` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
36
|
+
| `GitHubAuthorize` | `GET /login/oauth/authorize` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
37
|
+
| `GitHubToken` | `POST /login/oauth/access_token` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
38
|
+
| `GitHubUser` | `GET /user` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
39
|
+
| `GitHubUserPost` | `POST /user` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
40
|
+
| `GitHubEmails` | `GET /user/emails` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
41
|
+
| `GitHubEmailsPost` | `POST /user/emails` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
42
|
+
| `Welcome` | `GET /` | ✅ supported | ❌ disabled | Interactive OAuth transactions are exercised by protocol and independent JOSE tests. |
|
|
@@ -3,8 +3,9 @@ import {
|
|
|
3
3
|
createClock,
|
|
4
4
|
createMultiRuntime,
|
|
5
5
|
createRuntime,
|
|
6
|
-
parseDuration
|
|
7
|
-
|
|
6
|
+
parseDuration,
|
|
7
|
+
resolveAdminPrefix
|
|
8
|
+
} from "./chunk-I76SEED6.js";
|
|
8
9
|
|
|
9
10
|
// src/server.ts
|
|
10
11
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
@@ -164,10 +165,12 @@ var removeOwned = async (path, id) => {
|
|
|
164
165
|
};
|
|
165
166
|
var httpChild = async (target, entry, options) => {
|
|
166
167
|
const runtime = await target.create(entry.options ?? {}, {
|
|
168
|
+
adminPrefix: resolveAdminPrefix(entry.adminPrefix),
|
|
167
169
|
adminKey: entry.adminKey,
|
|
168
170
|
seed: entry.seed,
|
|
169
171
|
onLog: options.onLog
|
|
170
172
|
});
|
|
173
|
+
const adminPrefix = resolveAdminPrefix(entry.adminPrefix);
|
|
171
174
|
runtime.isolateNamespaces();
|
|
172
175
|
const locked = /* @__PURE__ */ new Set();
|
|
173
176
|
const active = /* @__PURE__ */ new Map();
|
|
@@ -201,12 +204,12 @@ var httpChild = async (target, entry, options) => {
|
|
|
201
204
|
return {
|
|
202
205
|
protocol: "http",
|
|
203
206
|
url: server.url,
|
|
204
|
-
healthUrl: `${server.url}/health`,
|
|
205
|
-
adminUrl: `${server.url}
|
|
206
|
-
namespaces: { header: "x-mockingbird-namespace", path:
|
|
207
|
+
healthUrl: `${server.url}${adminPrefix}/health`,
|
|
208
|
+
adminUrl: `${server.url}${adminPrefix}`,
|
|
209
|
+
namespaces: { header: "x-mockingbird-namespace", path: `${adminPrefix}/ns/{name}` },
|
|
207
210
|
ready: async () => {
|
|
208
211
|
try {
|
|
209
|
-
return (await fetch(`${server.url}/health`, { signal: AbortSignal.timeout(2e3) })).ok;
|
|
212
|
+
return (await fetch(`${server.url}${adminPrefix}/health`, { signal: AbortSignal.timeout(2e3) })).ok;
|
|
210
213
|
} catch {
|
|
211
214
|
return false;
|
|
212
215
|
}
|
|
@@ -345,7 +348,9 @@ async function startFleet(config, options) {
|
|
|
345
348
|
const api = {
|
|
346
349
|
async fetch(request) {
|
|
347
350
|
const url = new URL(request.url);
|
|
348
|
-
|
|
351
|
+
const adminPrefix = resolveAdminPrefix(config.adminPrefix);
|
|
352
|
+
const path = url.pathname.startsWith(`${adminPrefix}/`) ? url.pathname.slice(adminPrefix.length) : "";
|
|
353
|
+
if (path === "/health" && request.method === "GET") {
|
|
349
354
|
const states = await Promise.all([...children].map(async ([name, child]) => {
|
|
350
355
|
let protocolReady = false;
|
|
351
356
|
try {
|
|
@@ -369,18 +374,18 @@ async function startFleet(config, options) {
|
|
|
369
374
|
services: Object.fromEntries(states)
|
|
370
375
|
});
|
|
371
376
|
}
|
|
372
|
-
if (!
|
|
377
|
+
if (!path.startsWith("/fleet/"))
|
|
373
378
|
return json(404, { error: "not found" });
|
|
374
379
|
const adminKey = config.adminKey ?? process.env.MOCKINGBIRD_ADMIN_KEY;
|
|
375
380
|
if (adminKey !== void 0 && request.headers.get("x-mockingbird-admin-key") !== adminKey)
|
|
376
381
|
return json(401, { error: "admin key required" });
|
|
377
|
-
const route = /^\/
|
|
382
|
+
const route = /^\/fleet\/namespaces\/([^/]+)\/(reset|snapshots)(?:\/([^/]+)\/restore)?$/.exec(path);
|
|
378
383
|
const namespace = route ? decodeURIComponent(route[1]) : url.searchParams.get("namespace") ?? request.headers.get("x-mockingbird-namespace") ?? config.namespace ?? "default";
|
|
379
384
|
if (!NS.test(namespace))
|
|
380
385
|
return json(400, { error: "invalid namespace" });
|
|
381
|
-
if (request.method === "GET" &&
|
|
386
|
+
if (request.method === "GET" && path === "/fleet/metrics")
|
|
382
387
|
return json(200, { namespace, services: diagnostics(namespace) });
|
|
383
|
-
if (request.method === "GET" &&
|
|
388
|
+
if (request.method === "GET" && path === "/fleet/clock")
|
|
384
389
|
return json(200, {
|
|
385
390
|
namespace,
|
|
386
391
|
...clock(namespace).state(),
|
|
@@ -404,12 +409,12 @@ async function startFleet(config, options) {
|
|
|
404
409
|
} catch (error) {
|
|
405
410
|
return json(400, { error: error instanceof Error ? error.message : "bad selection" });
|
|
406
411
|
}
|
|
407
|
-
if (
|
|
412
|
+
if (path === "/fleet/wait-until-idle") {
|
|
408
413
|
const services = diagnostics(namespace);
|
|
409
414
|
const idle = Object.values(services).every((d) => pending(d) === 0);
|
|
410
415
|
return json(idle ? 200 : 409, { status: idle ? "idle" : "busy", namespace, services });
|
|
411
416
|
}
|
|
412
|
-
if (
|
|
417
|
+
if (path === "/fleet/clock") {
|
|
413
418
|
const current = clock(namespace);
|
|
414
419
|
let target = current.now();
|
|
415
420
|
if (body.set !== void 0) {
|
|
@@ -485,6 +490,7 @@ async function startFleet(config, options) {
|
|
|
485
490
|
}
|
|
486
491
|
};
|
|
487
492
|
try {
|
|
493
|
+
const adminPrefix = resolveAdminPrefix(config.adminPrefix);
|
|
488
494
|
if (!isObject(config.services) || Object.keys(config.services).length === 0)
|
|
489
495
|
throw new Error("config must contain at least one service");
|
|
490
496
|
for (const [name, entry] of Object.entries(config.services)) {
|
|
@@ -495,7 +501,11 @@ async function startFleet(config, options) {
|
|
|
495
501
|
if (entry.protocol && entry.protocol !== ("protocol" in target ? target.protocol : "http"))
|
|
496
502
|
throw new Error("configured protocol does not match target");
|
|
497
503
|
const adminKey = entry.adminKey ?? config.adminKey ?? process.env.MOCKINGBIRD_ADMIN_KEY;
|
|
498
|
-
const child = "start" in target ? await target.start(entry) : await httpChild(target, {
|
|
504
|
+
const child = "start" in target ? await target.start(entry) : await httpChild(target, {
|
|
505
|
+
...entry,
|
|
506
|
+
adminPrefix: resolveAdminPrefix(entry.adminPrefix ?? adminPrefix),
|
|
507
|
+
...adminKey !== void 0 ? { adminKey } : {}
|
|
508
|
+
}, options);
|
|
499
509
|
children.set(name, child);
|
|
500
510
|
if (!await child.ready())
|
|
501
511
|
throw new Error("protocol readiness failed");
|
|
@@ -513,15 +523,15 @@ async function startFleet(config, options) {
|
|
|
513
523
|
pid: process.pid,
|
|
514
524
|
id,
|
|
515
525
|
startedAt,
|
|
516
|
-
adminBase: `${supervisor.url}/
|
|
517
|
-
healthUrl: `${supervisor.url}/health`,
|
|
526
|
+
adminBase: `${supervisor.url}${adminPrefix}/fleet`,
|
|
527
|
+
healthUrl: `${supervisor.url}${adminPrefix}/health`,
|
|
518
528
|
services: Object.fromEntries([...children].map(([name, child]) => [
|
|
519
529
|
name,
|
|
520
530
|
{
|
|
521
531
|
protocol: child.protocol,
|
|
522
532
|
url: publicUrl(child.url),
|
|
523
|
-
healthUrl: child.healthUrl ?? `${supervisor?.url}/health`,
|
|
524
|
-
adminUrl: child.adminUrl ?? `${supervisor?.url}/
|
|
533
|
+
healthUrl: child.healthUrl ?? `${supervisor?.url}${adminPrefix}/health`,
|
|
534
|
+
adminUrl: child.adminUrl ?? `${supervisor?.url}${adminPrefix}/fleet`,
|
|
525
535
|
namespaces: child.namespaces,
|
|
526
536
|
processReady: true,
|
|
527
537
|
protocolReady: true
|
|
@@ -617,6 +627,11 @@ ${commandHelp(spec, name, command)}`);
|
|
|
617
627
|
var COMMON_SERVE_OPTIONS = {
|
|
618
628
|
port: { type: "string", value: "<port>", description: "Port to listen on" },
|
|
619
629
|
host: { type: "string", value: "<host>", description: "Interface to bind", default: "127.0.0.1" },
|
|
630
|
+
"admin-prefix": {
|
|
631
|
+
type: "string",
|
|
632
|
+
value: "<path>",
|
|
633
|
+
description: "Internal API prefix (default /__admin; env MOCKINGBIRD_ADMIN_PREFIX)"
|
|
634
|
+
},
|
|
620
635
|
"admin-key": {
|
|
621
636
|
type: "string",
|
|
622
637
|
value: "<key>",
|
|
@@ -690,7 +705,9 @@ var loadTarget = async (name, own) => {
|
|
|
690
705
|
}
|
|
691
706
|
};
|
|
692
707
|
var start = async (target, values, config) => {
|
|
708
|
+
const adminPrefix = resolveAdminPrefix(config.adminPrefix);
|
|
693
709
|
const runtime = await target.create(values, {
|
|
710
|
+
adminPrefix,
|
|
694
711
|
adminKey: config.adminKey,
|
|
695
712
|
seed: config.seed,
|
|
696
713
|
onLog: formatLog(config.log)
|
|
@@ -698,11 +715,11 @@ var start = async (target, values, config) => {
|
|
|
698
715
|
const listening = await listen(runtime, { port: config.port, host: config.host });
|
|
699
716
|
target.listening?.(listening);
|
|
700
717
|
console.log(`${target.name} mock listening on ${listening.url}`);
|
|
701
|
-
console.log(`${target.name} health: GET ${listening.url}/health`);
|
|
702
|
-
console.log(`${target.name} admin: ${listening.url}
|
|
703
|
-
console.log(`${target.name} admin ui: ${listening.url}/
|
|
718
|
+
console.log(`${target.name} health: GET ${listening.url}${adminPrefix}/health`);
|
|
719
|
+
console.log(`${target.name} admin: ${listening.url}${adminPrefix} (${config.adminKey ? "x-mockingbird-admin-key required" : "open \u2014 pass --admin-key to lock"})`);
|
|
720
|
+
console.log(`${target.name} admin ui: ${listening.url}${adminPrefix}/ui`);
|
|
704
721
|
for (const line of target.banner?.(runtime) ?? [])
|
|
705
|
-
console.log(`${target.name} ${line}`);
|
|
722
|
+
console.log(`${target.name} ${line.replaceAll("/__admin", adminPrefix)}`);
|
|
706
723
|
return listening;
|
|
707
724
|
};
|
|
708
725
|
var untilSignal = async (servers) => new Promise((resolve2) => {
|
|
@@ -729,6 +746,7 @@ var serveCommand = (target) => ({
|
|
|
729
746
|
if (configPath !== void 0) {
|
|
730
747
|
try {
|
|
731
748
|
const config = JSON.parse(await readFile2(configPath, "utf8"));
|
|
749
|
+
config.adminPrefix = resolveAdminPrefix(config.adminPrefix ?? asString(values["admin-prefix"]) ?? process.env.MOCKINGBIRD_ADMIN_PREFIX);
|
|
732
750
|
const adminKey2 = config.adminKey ?? asString(values["admin-key"]) ?? process.env.MOCKINGBIRD_ADMIN_KEY;
|
|
733
751
|
if (adminKey2 !== void 0)
|
|
734
752
|
config.adminKey = adminKey2;
|
|
@@ -761,6 +779,7 @@ var serveCommand = (target) => ({
|
|
|
761
779
|
let listening;
|
|
762
780
|
try {
|
|
763
781
|
listening = await start(target, values, {
|
|
782
|
+
adminPrefix: resolveAdminPrefix(asString(values["admin-prefix"]) ?? process.env.MOCKINGBIRD_ADMIN_PREFIX),
|
|
764
783
|
port: port === void 0 ? target.defaultPort : Number.parseInt(port, 10),
|
|
765
784
|
host: asString(values.host) ?? "127.0.0.1",
|
|
766
785
|
...adminKey !== void 0 ? { adminKey } : {},
|
|
@@ -816,6 +835,7 @@ var serveTarget = {
|
|
|
816
835
|
return createMultiRuntime({
|
|
817
836
|
mounts: parsed.mounts,
|
|
818
837
|
...common.seed !== void 0 ? { seed: common.seed } : {},
|
|
838
|
+
...common.adminPrefix !== void 0 ? { adminPrefix: common.adminPrefix } : {},
|
|
819
839
|
...common.adminKey !== void 0 ? { adminKey: common.adminKey } : {}
|
|
820
840
|
});
|
|
821
841
|
}
|
|
@@ -829,6 +849,7 @@ var serveTarget = {
|
|
|
829
849
|
...typeof values.scenario === "string" ? { behavior: { preset: values.scenario } } : {},
|
|
830
850
|
...common.seed !== void 0 ? { seed: common.seed } : {},
|
|
831
851
|
...typeof values.issuer === "string" ? { issuer: values.issuer } : {},
|
|
852
|
+
...common.adminPrefix !== void 0 ? { adminPrefix: common.adminPrefix } : {},
|
|
832
853
|
...common.adminKey !== void 0 ? { adminKey: common.adminKey } : {}
|
|
833
854
|
});
|
|
834
855
|
},
|
|
@@ -846,4 +867,4 @@ export {
|
|
|
846
867
|
createMultiServer,
|
|
847
868
|
serveTarget
|
|
848
869
|
};
|
|
849
|
-
//# sourceMappingURL=chunk-
|
|
870
|
+
//# sourceMappingURL=chunk-2BINFVVC.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/server.ts", "../../../adapters/node/src/cli.ts", "../../../adapters/node/src/fleet.ts", "../../../adapters/node/src/serve.ts", "../../../adapters/node/src/listen.ts"],
|
|
4
|
+
"sourcesContent": ["/// <reference types=\"node\" />\nimport { readFile } from \"node:fs/promises\"\nimport { type Listening, listen, type ServeTarget } from \"@crvouga/mockingbird-adapter-node\"\nimport { OAUTH_SCENARIOS, type OAuthScenario } from \"./behavior.js\"\nimport {\n createMultiRuntime,\n type OAuthMount,\n type OAuthMultiRuntime,\n type OAuthMultiRuntimeOptions,\n} from \"./multi.js\"\nimport { createRuntime, type OAuthRuntime, type OAuthRuntimeOptions } from \"./runtime.js\"\nexport const DEFAULT_PORT = 8810\nexport type OAuthServerOptions = OAuthRuntimeOptions & { port?: number; host?: string }\nexport const createServer = async (\n options: OAuthServerOptions = {},\n): Promise<Listening & { runtime: OAuthRuntime }> => {\n const { port, host, ...rest } = options\n const runtime = createRuntime(rest)\n return { ...(await listen(runtime, { port: port ?? 0, ...(host ? { host } : {}) })), runtime }\n}\nexport type OAuthMultiServerOptions = OAuthMultiRuntimeOptions & { port?: number; host?: string }\nexport const createMultiServer = async (\n options: OAuthMultiServerOptions,\n): Promise<Listening & { runtime: OAuthMultiRuntime }> => {\n const { port, host, ...rest } = options\n const runtime = createMultiRuntime(rest)\n return { ...(await listen(runtime, { port: port ?? 0, ...(host ? { host } : {}) })), runtime }\n}\nexport const serveTarget: ServeTarget = {\n name: \"oauth\",\n defaultPort: DEFAULT_PORT,\n options: {\n provider: {\n type: \"string\",\n value: \"<google|apple|microsoft|github|oidc>\",\n description: \"Provider wire profile (default oidc)\",\n },\n scenario: {\n type: \"string\",\n value: \"<name>\",\n description: \"Initial behavior scenario (GET /__admin/scenarios lists all)\",\n },\n issuer: { type: \"string\", value: \"<url>\", description: \"Public issuer URL\" },\n mounts: {\n type: \"string\",\n value: \"<file>\",\n description: \"JSON file with a mounts array; serves every provider on one listener\",\n },\n },\n create: async (values, common) => {\n if (typeof values.mounts === \"string\") {\n const parsed = JSON.parse(await readFile(values.mounts, \"utf8\")) as { mounts?: OAuthMount[] }\n if (!Array.isArray(parsed.mounts))\n throw new Error(\"OAuth mounts file requires a mounts array\")\n return createMultiRuntime({\n mounts: parsed.mounts,\n ...(common.seed !== undefined ? { seed: common.seed } : {}),\n ...(common.adminPrefix !== undefined ? { adminPrefix: common.adminPrefix } : {}),\n ...(common.adminKey !== undefined ? { adminKey: common.adminKey } : {}),\n }) as unknown as OAuthRuntime\n }\n const provider = values.provider ?? \"oidc\"\n if (\n provider !== \"google\" &&\n provider !== \"apple\" &&\n provider !== \"oidc\" &&\n provider !== \"microsoft\" &&\n provider !== \"github\"\n )\n throw new Error(\"provider must be google, apple, microsoft, github or oidc\")\n if (\n values.scenario !== undefined &&\n (typeof values.scenario !== \"string\" || !Object.hasOwn(OAUTH_SCENARIOS, values.scenario))\n )\n throw new Error(\"Unknown OAuth scenario\")\n return createRuntime({\n provider,\n ...(typeof values.scenario === \"string\"\n ? { behavior: { preset: values.scenario as OAuthScenario } }\n : {}),\n ...(common.seed !== undefined ? { seed: common.seed } : {}),\n ...(typeof values.issuer === \"string\" ? { issuer: values.issuer } : {}),\n ...(common.adminPrefix !== undefined ? { adminPrefix: common.adminPrefix } : {}),\n ...(common.adminKey !== undefined ? { adminKey: common.adminKey } : {}),\n })\n },\n banner: () => [\n \"Register clients: POST /__admin/clients; seed identities: POST /__admin/accounts\",\n \"Discovery: /.well-known/openid-configuration\",\n ],\n}\n", "import type { EventEmitter } from \"node:events\"\nimport { readFile } from \"node:fs/promises\"\nimport { createRequire } from \"node:module\"\nimport { resolve } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\nimport { type ParseArgsConfig, parseArgs } from \"node:util\"\nimport type { RequestLog, ServiceInstance, ServiceRuntime } from \"@crvouga/mockingbird-service\"\nimport { resolveAdminPrefix } from \"@crvouga/mockingbird-service\"\nimport { type FleetTarget, startFleet } from \"./fleet.js\"\nimport { type Listening, listen } from \"./listen.js\"\n\nexport type CliOption = {\n type: \"string\" | \"boolean\"\n description: string\n /** Shown in help; the value placeholder, e.g. `<port>`. */\n value?: string\n default?: string | boolean\n}\n\nexport type CliValues = Record<string, string | boolean | undefined>\n\nexport type CliCommand = {\n summary: string\n usage?: string\n options?: Record<string, CliOption>\n /** Resolves to an exit code; a server command resolves only when it stops. */\n run(values: CliValues, positionals: string[]): Promise<number>\n}\n\nexport type CliSpec = {\n bin: string\n description: string\n commands: Record<string, CliCommand>\n}\n\nconst optionHelp = (options: Record<string, CliOption>): string[] =>\n Object.entries(options).map(([name, option]) => {\n const flag = `--${name}${option.type === \"string\" ? ` ${option.value ?? \"<value>\"}` : \"\"}`\n const fallback = option.default !== undefined ? ` (default: ${String(option.default)})` : \"\"\n return ` ${flag.padEnd(30)} ${option.description}${fallback}`\n })\n\nconst help = (spec: CliSpec): string =>\n [\n `${spec.bin} \u2014 ${spec.description}`,\n \"\",\n \"Usage:\",\n ` ${spec.bin} <command> [options]`,\n \"\",\n \"Commands:\",\n ...Object.entries(spec.commands).map(([name, c]) => ` ${name.padEnd(30)} ${c.summary}`),\n \"\",\n `Run \\`${spec.bin} <command> --help\\` for a command's options.`,\n ].join(\"\\n\")\n\nconst commandHelp = (spec: CliSpec, name: string, command: CliCommand): string =>\n [\n `${spec.bin} ${name} \u2014 ${command.summary}`,\n \"\",\n \"Usage:\",\n ` ${command.usage ?? `${spec.bin} ${name} [options]`}`,\n ...(command.options ? [\"\", \"Options:\", ...optionHelp(command.options)] : []),\n ].join(\"\\n\")\n\n/** Parse `argv` against `spec` and run the chosen command. Resolves to an exit code. */\nexport const runCli = async (spec: CliSpec, argv: string[]): Promise<number> => {\n // Two-word commands (`corpus pull`) win over one-word ones.\n const pair = argv.length >= 2 ? `${argv[0]} ${argv[1]}` : undefined\n const words = pair !== undefined && spec.commands[pair] ? 2 : 1\n const name = words === 2 ? pair : argv[0]\n const rest = argv.slice(words)\n if (name === undefined || name === \"--help\" || name === \"-h\" || name === \"help\") {\n console.log(help(spec))\n return 0\n }\n const command = spec.commands[name]\n if (!command) {\n console.error(`${spec.bin}: unknown command ${JSON.stringify(name)}\\n\\n${help(spec)}`)\n return 2\n }\n if (rest.includes(\"--help\") || rest.includes(\"-h\")) {\n console.log(commandHelp(spec, name, command))\n return 0\n }\n let parsed: { values: CliValues; positionals: string[] }\n try {\n parsed = parseArgs({\n args: rest,\n allowPositionals: true,\n strict: true,\n options: Object.fromEntries(\n Object.entries(command.options ?? {}).map(([key, option]) => [\n key,\n {\n type: option.type,\n ...(option.default !== undefined ? { default: option.default } : {}),\n },\n ]),\n ) as ParseArgsConfig[\"options\"],\n }) as { values: CliValues; positionals: string[] }\n } catch (error) {\n console.error(\n `${spec.bin} ${name}: ${error instanceof Error ? error.message : String(error)}\\n\\n${commandHelp(spec, name, command)}`,\n )\n return 2\n }\n return command.run(parsed.values, parsed.positionals)\n}\n\n// \u2500\u2500 serve \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type LogFormat = \"pretty\" | \"json\" | \"off\"\n\nexport type CommonServeOptions = {\n adminPrefix?: string\n adminKey: string | undefined\n seed: string | undefined\n onLog: ((entry: RequestLog) => void) | undefined\n}\n\n/**\n * What a service contributes to `serve`: how to build its runtime from CLI flags,\n * and what to say at startup. Every service's `./server` entry exports one as\n * `serveTarget`, which is also how `serve --config` finds services by name.\n */\nexport type ServeTarget = {\n name: string\n defaultPort: number\n /** Serve flags beyond the common ones. */\n options?: Record<string, CliOption>\n create(\n values: CliValues,\n common: CommonServeOptions,\n ): Promise<ServiceRuntime<ServiceInstance>> | ServiceRuntime<ServiceInstance>\n /** Startup lines after the listen address, e.g. the loaded corpus version. */\n banner?(runtime: ServiceRuntime<ServiceInstance>): string[]\n /** After the HTTP server is bound. A service that speaks more than HTTP attaches it here. */\n listening?(server: Listening): void\n}\n\nconst COMMON_SERVE_OPTIONS: Record<string, CliOption> = {\n port: { type: \"string\", value: \"<port>\", description: \"Port to listen on\" },\n host: { type: \"string\", value: \"<host>\", description: \"Interface to bind\", default: \"127.0.0.1\" },\n \"admin-prefix\": {\n type: \"string\",\n value: \"<path>\",\n description: \"Internal API prefix (default /__admin; env MOCKINGBIRD_ADMIN_PREFIX)\",\n },\n \"admin-key\": {\n type: \"string\",\n value: \"<key>\",\n description: \"Require x-mockingbird-admin-key on /__admin/* (env MOCKINGBIRD_ADMIN_KEY)\",\n },\n seed: { type: \"string\", value: \"<seed>\", description: \"Seed for every random choice\" },\n log: {\n type: \"string\",\n value: \"<pretty|json|off>\",\n description: \"Request log format\",\n default: \"pretty\",\n },\n \"log-requests\": {\n type: \"boolean\",\n description:\n \"One JSON line per request: namespace, operationId, status, ids touched (never bodies). Same as --log json\",\n },\n config: {\n type: \"string\",\n value: \"<file>\",\n description: \"Serve every service in a mockingbird.json config instead\",\n },\n \"ready-file\": {\n type: \"string\",\n description: \"Atomically publish fleet endpoint JSON\",\n value: \"<file>\",\n },\n \"connections-file\": {\n type: \"string\",\n description: \"Write private connection URLs with mode 0600\",\n value: \"<file>\",\n },\n \"ready-json\": {\n type: \"boolean\",\n description: \"Emit one public JSON record after fleet readiness\",\n },\n}\n\nconst formatLog = (format: LogFormat) => {\n if (format === \"off\") return undefined\n if (format === \"json\") return (entry: RequestLog) => console.log(JSON.stringify(entry))\n return (entry: RequestLog) => {\n const op = entry.operationId ?? (entry.unmatched ? \"UNMATCHED\" : \"-\")\n const ns = entry.namespace === \"default\" ? \"\" : ` [${entry.namespace}]`\n const fault = entry.faultId ? ` fault=${entry.faultId}` : \"\"\n const adopted = entry.adopted ? \" adopted\" : \"\"\n // A rejection says what arrived (never the body) and why the service refused it.\n const received = entry.request\n ? ` received=${entry.request.bodyBytes === null ? \"?\" : `${entry.request.bodyBytes}B`} ct=${entry.request.contentType ?? \"-\"}${\n entry.request.transferEncoding ? ` te=${entry.request.transferEncoding}` : \"\"\n }`\n : \"\"\n const issues = entry.issues?.length\n ? ` issues=${entry.issues\n .slice(0, 3)\n .map((issue) => `${issue.path || \"$\"}: ${issue.message}`)\n .join(\"; \")}${entry.issues.length > 3 ? ` (+${entry.issues.length - 3})` : \"\"}`\n : \"\"\n console.log(\n `${entry.service} ${entry.method} ${entry.path} ${entry.status} ${op} ${entry.durationMs}ms${ns}${fault}${adopted}${received}${issues}`,\n )\n }\n}\n\nconst asString = (value: string | boolean | undefined): string | undefined =>\n typeof value === \"string\" ? value : undefined\n\n/** A service entry in `mockingbird.json`. */\nexport type ConfigService = {\n protocol?: \"http\" | \"postgres\" | \"redis\"\n port?: number\n host?: string\n adminPrefix?: string\n adminKey?: string\n seed?: string\n password?: string\n user?: string\n database?: string\n /** Protocol namespace selectors: database names for Postgres, logical DB indexes for Redis. */\n namespaces?: Record<string, string | number>\n /** Service-specific serve flags, by long name: `{ \"webhook-url\": \"\u2026\" }`. */\n options?: Record<string, string | boolean>\n}\n\nexport type MockingbirdConfig = {\n /** Keyed by service name: `junction` loads `@crvouga/mockingbird-service-junction`. */\n services: Record<string, ConfigService>\n log?: LogFormat\n adminPrefix?: string\n adminKey?: string\n namespace?: string\n control?: { host?: string; port?: number }\n}\n\nconst loadTarget = async (name: string, own: FleetTarget): Promise<FleetTarget> => {\n if (name === own.name) return own\n const specifier = `@crvouga/mockingbird-service-${name}/server`\n try {\n // A CLI run with npx may live outside the consumer project. Discover its locally\n // installed services as well as siblings of the bundled CLI.\n let mod: { serveTarget?: FleetTarget }\n try {\n mod = (await import(specifier)) as { serveTarget?: FleetTarget }\n } catch {\n const fromProject = createRequire(resolve(process.cwd(), \"package.json\"))\n mod = (await import(pathToFileURL(fromProject.resolve(specifier)).href)) as {\n serveTarget?: FleetTarget\n }\n }\n if (!mod.serveTarget) throw new Error(`${specifier} exports no serveTarget`)\n return mod.serveTarget\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n throw new Error(\n `cannot load service \"${name}\": ${reason}. Install @crvouga/mockingbird-service-${name}.`,\n )\n }\n}\n\nconst start = async (\n target: ServeTarget,\n values: CliValues,\n config: {\n port: number\n host: string\n adminPrefix?: string\n adminKey?: string\n seed?: string\n log: LogFormat\n },\n): Promise<Listening> => {\n const adminPrefix = resolveAdminPrefix(config.adminPrefix)\n const runtime = await target.create(values, {\n adminPrefix,\n adminKey: config.adminKey,\n seed: config.seed,\n onLog: formatLog(config.log),\n })\n const listening = await listen(runtime, { port: config.port, host: config.host })\n target.listening?.(listening)\n console.log(`${target.name} mock listening on ${listening.url}`)\n console.log(`${target.name} health: GET ${listening.url}${adminPrefix}/health`)\n console.log(\n `${target.name} admin: ${listening.url}${adminPrefix} (${config.adminKey ? \"x-mockingbird-admin-key required\" : \"open \u2014 pass --admin-key to lock\"})`,\n )\n console.log(`${target.name} admin ui: ${listening.url}${adminPrefix}/ui`)\n for (const line of target.banner?.(runtime) ?? [])\n console.log(`${target.name} ${line.replaceAll(\"/__admin\", adminPrefix)}`)\n return listening\n}\n\nconst untilSignal = async (servers: { close(): Promise<void> }[]): Promise<number> =>\n new Promise((resolve) => {\n const stop = async () => {\n ;(process as EventEmitter).removeListener(\"SIGINT\", stop)\n ;(process as EventEmitter).removeListener(\"SIGTERM\", stop)\n await Promise.allSettled(servers.map((s) => s.close()))\n resolve(0)\n }\n process.once(\"SIGINT\", stop)\n process.once(\"SIGTERM\", stop)\n })\n\n/** The standard `serve` command for a service, including multi-service `--config`. */\nexport const serveCommand = (target: FleetTarget): CliCommand => ({\n summary: `Serve the ${target.name} mock or a configured fleet`,\n options: { ...COMMON_SERVE_OPTIONS, ...(\"options\" in target ? target.options : {}) },\n async run(values) {\n const log = (\n values[\"log-requests\"] === true ? \"json\" : (asString(values.log) ?? \"pretty\")\n ) as LogFormat\n if (![\"pretty\", \"json\", \"off\"].includes(log)) {\n console.error(`--log must be pretty, json or off (got ${log})`)\n return 2\n }\n const configPath = asString(values.config)\n if (configPath !== undefined) {\n try {\n const config = JSON.parse(await readFile(configPath, \"utf8\")) as MockingbirdConfig\n config.adminPrefix = resolveAdminPrefix(\n config.adminPrefix ??\n asString(values[\"admin-prefix\"]) ??\n process.env.MOCKINGBIRD_ADMIN_PREFIX,\n )\n const adminKey =\n config.adminKey ?? asString(values[\"admin-key\"]) ?? process.env.MOCKINGBIRD_ADMIN_KEY\n if (adminKey !== undefined) config.adminKey = adminKey\n const fleet = await startFleet(config, {\n load: (name) => loadTarget(name, target),\n onLog: formatLog(config.log ?? log),\n ...(asString(values[\"ready-file\"]) ? { readyFile: asString(values[\"ready-file\"]) } : {}),\n ...(asString(values[\"connections-file\"])\n ? { connectionsFile: asString(values[\"connections-file\"]) }\n : {}),\n onReady: (manifest) => {\n if (values[\"ready-json\"]) console.log(JSON.stringify(manifest))\n else\n for (const [name, endpoint] of Object.entries(manifest.services))\n console.log(`${name} mock listening on ${endpoint.url}`)\n },\n })\n return untilSignal([fleet])\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n return 1\n }\n }\n if (\"start\" in target) {\n console.error(\"Use --config to supervise protocol services with this command\")\n return 2\n }\n const port = asString(values.port)\n const adminKey = asString(values[\"admin-key\"]) ?? process.env.MOCKINGBIRD_ADMIN_KEY\n const seed = asString(values.seed)\n let listening: Listening\n try {\n listening = await start(target, values, {\n adminPrefix: resolveAdminPrefix(\n asString(values[\"admin-prefix\"]) ?? process.env.MOCKINGBIRD_ADMIN_PREFIX,\n ),\n port: port === undefined ? target.defaultPort : Number.parseInt(port, 10),\n host: asString(values.host) ?? \"127.0.0.1\",\n ...(adminKey !== undefined ? { adminKey } : {}),\n ...(seed !== undefined ? { seed } : {}),\n log,\n })\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n return 1\n }\n return untilSignal([listening])\n },\n})\n", "import { randomUUID } from \"node:crypto\"\nimport { chmod, readFile, rename, rm, writeFile } from \"node:fs/promises\"\nimport type { FetchAPI } from \"@crvouga/mockingbird-core\"\nimport type { Clock, ServiceInstance, ServiceRuntime } from \"@crvouga/mockingbird-service\"\nimport { createClock, parseDuration, resolveAdminPrefix } from \"@crvouga/mockingbird-service\"\nimport type { CommonServeOptions, ConfigService, MockingbirdConfig, ServeTarget } from \"./cli.js\"\nimport type { EndpointManifest } from \"./fleet-manifest.js\"\nimport { type Listening, listen } from \"./listen.js\"\n\nexport type { EndpointManifest } from \"./fleet-manifest.js\"\n\nexport type FleetDiagnostics = {\n activeRequests: number\n activeConnections: number\n blockedCommands: number\n pendingJobs: number\n activeJobs?: number\n pendingWebhooks: number\n unmatchedRequests: number\n}\n\n/** Protocol packages own their state, listeners, credentials and namespace selection. */\nexport type FleetChild = {\n protocol: \"http\" | \"postgres\" | \"redis\"\n url: string\n connection?: string\n connections?: Record<string, string>\n healthUrl?: string\n adminUrl?: string\n namespaces: Record<string, unknown>\n ready(): Promise<boolean>\n close(): Promise<void>\n /** Fence new work synchronously; diagnostics must also account for work already in flight. */\n lock(namespace: string): () => void\n diagnostics(namespace: string): FleetDiagnostics\n checkpoint(namespace: string): Promise<unknown>\n restore(namespace: string, checkpoint: unknown): Promise<void>\n reset(namespace: string): Promise<void>\n clock(namespace: string): Clock\n settle?(namespace: string): Promise<void>\n}\n\nexport type ProtocolTarget = {\n name: string\n protocol: \"postgres\" | \"redis\"\n defaultPort: number\n start(entry: ConfigService): Promise<FleetChild>\n}\nexport type FleetTarget = ServeTarget | ProtocolTarget\n\nexport type FleetOptions = {\n load(name: string): Promise<FleetTarget>\n onLog?: CommonServeOptions[\"onLog\"]\n readyFile?: string | undefined\n connectionsFile?: string | undefined\n onReady?(manifest: EndpointManifest): void\n}\n\nconst EMPTY: FleetDiagnostics = {\n activeRequests: 0,\n activeConnections: 0,\n blockedCommands: 0,\n pendingJobs: 0,\n pendingWebhooks: 0,\n unmatchedRequests: 0,\n}\nconst NS = /^[A-Za-z0-9_.-]{1,64}$/\nconst json = (status: number, value: unknown) => Response.json(value, { status })\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value)\nconst pending = (d: FleetDiagnostics) =>\n d.activeRequests + d.blockedCommands + d.pendingJobs + d.pendingWebhooks\n\nconst publicUrl = (raw: string): string => {\n const url = new URL(raw)\n url.password = \"\"\n return url.href\n}\n\n/** Atomic publication; credentials only go in an explicitly requested 0600 file. */\nconst atomicWrite = async (path: string, value: unknown) => {\n const temp = `${path}.${randomUUID()}.tmp`\n try {\n await writeFile(temp, `${JSON.stringify(value, null, 2)}\\n`, { mode: 0o600, flag: \"wx\" })\n await chmod(temp, 0o600)\n await rename(temp, path)\n } finally {\n await rm(temp, { force: true })\n }\n}\n\nconst removeOwned = async (path: string | undefined, id: string) => {\n if (!path) return\n try {\n const value: unknown = JSON.parse(await readFile(path, \"utf8\"))\n if (isObject(value) && value.id === id) await rm(path, { force: true })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error\n }\n}\n\nconst httpChild = async (\n target: ServeTarget,\n entry: ConfigService,\n options: FleetOptions,\n): Promise<FleetChild> => {\n const runtime: ServiceRuntime<ServiceInstance> = await target.create(entry.options ?? {}, {\n adminPrefix: resolveAdminPrefix(entry.adminPrefix),\n adminKey: entry.adminKey,\n seed: entry.seed,\n onLog: options.onLog,\n })\n const adminPrefix = resolveAdminPrefix(entry.adminPrefix)\n runtime.isolateNamespaces()\n const locked = new Set<string>()\n const active = new Map<string, number>()\n const api: FetchAPI = {\n async fetch(request) {\n const namespace = runtime.namespaceOf(request)\n if (locked.has(namespace)) return json(409, { error: \"fleet control in progress\", namespace })\n active.set(namespace, (active.get(namespace) ?? 0) + 1)\n try {\n return await runtime.fetch(request)\n } finally {\n active.set(namespace, (active.get(namespace) ?? 1) - 1)\n }\n },\n }\n let server: Listening | undefined\n try {\n server = await listen(api, {\n port: entry.port ?? target.defaultPort,\n host: entry.host ?? \"127.0.0.1\",\n })\n target.listening?.(server)\n } catch (error) {\n runtime.stop?.()\n await server?.close()\n runtime.webhooks?.clear()\n await runtime.webhooks?.idle()\n throw error\n }\n return {\n protocol: \"http\",\n url: server.url,\n healthUrl: `${server.url}${adminPrefix}/health`,\n adminUrl: `${server.url}${adminPrefix}`,\n namespaces: { header: \"x-mockingbird-namespace\", path: `${adminPrefix}/ns/{name}` },\n ready: async () => {\n try {\n return (\n await fetch(`${server.url}${adminPrefix}/health`, { signal: AbortSignal.timeout(2000) })\n ).ok\n } catch {\n return false\n }\n },\n async close() {\n runtime.stop?.()\n runtime.webhooks?.clear()\n await server.close()\n await runtime.webhooks?.idle()\n },\n lock(namespace) {\n if (locked.has(namespace)) throw new Error(\"namespace already locked\")\n locked.add(namespace)\n return () => locked.delete(namespace)\n },\n diagnostics(namespace) {\n return {\n ...EMPTY,\n activeRequests: active.get(namespace) ?? 0,\n pendingWebhooks: runtime.webhooks?.pending(namespace) ?? 0,\n unmatchedRequests: runtime.metrics\n .report(namespace)\n .unmatched.reduce((n, r) => n + r.count, 0),\n }\n },\n async checkpoint(namespace) {\n return runtime.fleetSnapshot(namespace)\n },\n async restore(namespace, checkpoint) {\n runtime.fleetRestore(checkpoint, namespace)\n },\n reset: (namespace) => runtime.reset(namespace),\n clock: (namespace) => runtime.namespaceClock(namespace),\n async settle(namespace) {\n const instance = runtime.instance(namespace) as ServiceInstance & { tick?(): unknown }\n instance.tick?.()\n await runtime.webhooks?.idle()\n },\n }\n}\n\nexport type Fleet = {\n readonly manifest: EndpointManifest\n readonly children: ReadonlyMap<string, FleetChild>\n fetch(request: Request): Promise<Response>\n close(): Promise<void>\n}\n\n/** Resolves only after every child and the aggregate control listener are healthy. */\nexport async function startFleet(config: MockingbirdConfig, options: FleetOptions): Promise<Fleet> {\n const id = randomUUID()\n const startedAt = new Date().toISOString()\n const children = new Map<string, FleetChild>()\n const clocks = new Map<string, Clock>()\n const checkpoints = new Map<\n string,\n { namespace: string; values: Map<string, unknown>; clock: ReturnType<Clock[\"state\"]> }\n >()\n let supervisor: Listening | undefined\n let stopped = false\n let controlling = false\n const close = async () => {\n if (stopped) return\n stopped = true\n await Promise.allSettled([...children.values()].map((child) => child.close()))\n await supervisor?.close()\n await removeOwned(options.readyFile, id)\n await removeOwned(options.connectionsFile, id)\n }\n // Invalidate a previous process's artifacts before attempting startup.\n if (options.readyFile) await rm(options.readyFile, { force: true })\n if (options.connectionsFile) await rm(options.connectionsFile, { force: true })\n\n const clock = (namespace: string) => {\n let value = clocks.get(namespace)\n if (!value) {\n value = createClock()\n clocks.set(namespace, value)\n }\n return value\n }\n const select = (body: Record<string, unknown>): Map<string, FleetChild> => {\n if (body.services === undefined) return children\n if (!Array.isArray(body.services) || body.services.length === 0)\n throw new Error(\"services must be a nonempty list\")\n const selected = new Map<string, FleetChild>()\n for (const name of body.services) {\n if (typeof name !== \"string\" || !children.has(name))\n throw new Error(\"unknown service in selection\")\n selected.set(name, children.get(name) as FleetChild)\n }\n return selected\n }\n const diagnostics = (namespace: string) =>\n Object.fromEntries([...children].map(([name, child]) => [name, child.diagnostics(namespace)]))\n const transaction = async (\n namespace: string,\n selected: Map<string, FleetChild>,\n run: (name: string, child: FleetChild) => Promise<unknown>,\n ): Promise<Response> => {\n if (controlling) return json(409, { error: \"another fleet operation is in progress\" })\n controlling = true\n const release: (() => void)[] = []\n const before = new Map<string, unknown>()\n const results: Record<string, unknown> = {}\n let failure: string | undefined\n try {\n for (const [name, child] of selected) {\n failure = name\n release.push(child.lock(namespace))\n }\n const busy = Object.fromEntries(\n [...selected]\n .filter(([, child]) => {\n const state = child.diagnostics(namespace)\n return (\n state.activeRequests +\n state.blockedCommands +\n state.pendingWebhooks +\n (state.activeJobs ?? 0) >\n 0\n )\n })\n .map(([name, child]) => [name, child.diagnostics(namespace)]),\n )\n if (Object.keys(busy).length)\n return json(409, { error: \"namespace is not quiescent\", services: busy })\n for (const [name, child] of selected) {\n failure = name\n before.set(name, await child.checkpoint(namespace))\n }\n for (const [name, child] of selected) {\n failure = name\n results[name] = { status: \"ok\", result: await run(name, child) }\n }\n return json(200, { status: \"ok\", namespace, services: results })\n } catch {\n const rollback: Record<string, string> = {}\n // Roll back even the child whose mutation failed; it may have partially changed state.\n for (const [name, point] of before) {\n try {\n await selected.get(name)?.restore(namespace, point)\n rollback[name] = \"restored\"\n } catch {\n rollback[name] = \"failed\"\n }\n }\n return json(500, {\n status: \"failed\",\n namespace,\n failedService: failure,\n services: results,\n rollback,\n rolledBack:\n before.size > 0 && Object.values(rollback).every((value) => value === \"restored\"),\n })\n } finally {\n for (const unlock of release.reverse()) unlock()\n controlling = false\n }\n }\n\n const api: FetchAPI = {\n async fetch(request) {\n const url = new URL(request.url)\n const adminPrefix = resolveAdminPrefix(config.adminPrefix)\n const path = url.pathname.startsWith(`${adminPrefix}/`)\n ? url.pathname.slice(adminPrefix.length)\n : \"\"\n if (path === \"/health\" && request.method === \"GET\") {\n const states = await Promise.all(\n [...children].map(async ([name, child]) => {\n let protocolReady = false\n try {\n protocolReady = await child.ready()\n } catch {\n /* reported as failed */\n }\n return [\n name,\n {\n processReady: !stopped,\n protocolReady,\n status: protocolReady ? \"ready\" : \"failed\",\n url: publicUrl(child.url),\n },\n ]\n }),\n )\n const ready =\n !stopped &&\n states.every(([, state]) => (state as { protocolReady: boolean }).protocolReady)\n return json(ready ? 200 : 503, {\n status: ready ? \"ready\" : \"failed\",\n id,\n services: Object.fromEntries(states),\n })\n }\n if (!path.startsWith(\"/fleet/\")) return json(404, { error: \"not found\" })\n const adminKey = config.adminKey ?? process.env.MOCKINGBIRD_ADMIN_KEY\n if (adminKey !== undefined && request.headers.get(\"x-mockingbird-admin-key\") !== adminKey)\n return json(401, { error: \"admin key required\" })\n const route = /^\\/fleet\\/namespaces\\/([^/]+)\\/(reset|snapshots)(?:\\/([^/]+)\\/restore)?$/.exec(\n path,\n )\n const namespace = route\n ? decodeURIComponent(route[1] as string)\n : (url.searchParams.get(\"namespace\") ??\n request.headers.get(\"x-mockingbird-namespace\") ??\n config.namespace ??\n \"default\")\n if (!NS.test(namespace)) return json(400, { error: \"invalid namespace\" })\n if (request.method === \"GET\" && path === \"/fleet/metrics\")\n return json(200, { namespace, services: diagnostics(namespace) })\n if (request.method === \"GET\" && path === \"/fleet/clock\")\n return json(200, {\n namespace,\n ...clock(namespace).state(),\n services: Object.fromEntries(\n [...children].map(([name, child]) => [name, child.clock(namespace).state()]),\n ),\n })\n if (request.method !== \"POST\") return json(405, { error: \"method not allowed\" })\n let body: Record<string, unknown>\n try {\n const text = await request.text()\n const value: unknown = text ? JSON.parse(text) : {}\n if (!isObject(value)) throw new Error(\"expected object\")\n body = value\n } catch {\n return json(400, { error: \"expected JSON object\" })\n }\n let selected: Map<string, FleetChild>\n try {\n selected = select(body)\n } catch (error) {\n return json(400, { error: error instanceof Error ? error.message : \"bad selection\" })\n }\n if (path === \"/fleet/wait-until-idle\") {\n const services = diagnostics(namespace)\n const idle = Object.values(services).every((d) => pending(d) === 0)\n return json(idle ? 200 : 409, { status: idle ? \"idle\" : \"busy\", namespace, services })\n }\n if (path === \"/fleet/clock\") {\n const current = clock(namespace)\n let target = current.now()\n if (body.set !== undefined) {\n target =\n typeof body.set === \"number\"\n ? body.set\n : typeof body.set === \"string\"\n ? Date.parse(body.set)\n : NaN\n if (!Number.isFinite(target))\n return json(400, { error: \"set must be epoch ms or ISO-8601\" })\n }\n if (body.advance !== undefined) {\n const delta = parseDuration(body.advance)\n if (delta === undefined) return json(400, { error: \"invalid advance\" })\n target += delta\n }\n const frozen = body.freeze === undefined ? current.state().frozen : body.freeze\n if (typeof frozen !== \"boolean\") return json(400, { error: \"freeze must be boolean\" })\n const response = await transaction(namespace, selected, async (_name, child) => {\n const childClock = child.clock(namespace)\n childClock.freeze()\n childClock.set(target)\n if (!frozen) childClock.unfreeze()\n await child.settle?.(namespace)\n return childClock.state()\n })\n if (response.ok) {\n current.freeze()\n current.set(target)\n if (!frozen) current.unfreeze()\n }\n return response\n }\n if (!route) return json(404, { error: \"not found\" })\n if (route[2] === \"reset\") {\n if (route[3]) return json(404, { error: \"not found\" })\n const response = await transaction(namespace, selected, async (_name, child) =>\n child.reset(namespace),\n )\n if (response.ok && selected.size === children.size) clock(namespace).reset()\n return response\n }\n if (route[3]) {\n const point = checkpoints.get(route[3])\n if (!point || point.namespace !== namespace)\n return json(404, { error: \"no snapshot in this namespace\" })\n const restored = new Map(\n [...point.values].map(([name]) => [name, children.get(name) as FleetChild]),\n )\n const response = await transaction(namespace, restored, async (name, child) =>\n child.restore(namespace, point.values.get(name)),\n )\n if (response.ok) {\n const current = clock(namespace)\n current.freeze()\n current.set(point.clock.now)\n if (!point.clock.frozen) current.unfreeze()\n }\n return response\n }\n const snapshotId = randomUUID()\n const values = new Map<string, unknown>()\n const response = await transaction(namespace, selected, async (name, child) => {\n values.set(name, await child.checkpoint(namespace))\n return { checkpoint: `${snapshotId}:${name}` }\n })\n if (!response.ok) return response\n checkpoints.set(snapshotId, { namespace, values, clock: clock(namespace).state() })\n return json(201, {\n ...((await response.json()) as Record<string, unknown>),\n id: snapshotId,\n label: body.label,\n })\n },\n }\n try {\n const adminPrefix = resolveAdminPrefix(config.adminPrefix)\n if (!isObject(config.services) || Object.keys(config.services).length === 0)\n throw new Error(\"config must contain at least one service\")\n for (const [name, entry] of Object.entries(config.services)) {\n if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) throw new Error(\"invalid service name\")\n try {\n const target = await options.load(name)\n if (entry.protocol && entry.protocol !== (\"protocol\" in target ? target.protocol : \"http\"))\n throw new Error(\"configured protocol does not match target\")\n const adminKey = entry.adminKey ?? config.adminKey ?? process.env.MOCKINGBIRD_ADMIN_KEY\n const child =\n \"start\" in target\n ? await target.start(entry)\n : await httpChild(\n target,\n {\n ...entry,\n adminPrefix: resolveAdminPrefix(entry.adminPrefix ?? adminPrefix),\n ...(adminKey !== undefined ? { adminKey } : {}),\n },\n options,\n )\n children.set(name, child)\n if (!(await child.ready())) throw new Error(\"protocol readiness failed\")\n } catch {\n throw new Error(`service ${name} failed to start; all started listeners were closed`)\n }\n }\n supervisor = await listen(api, {\n port: config.control?.port ?? 0,\n host: config.control?.host ?? \"127.0.0.1\",\n })\n const manifest: EndpointManifest = {\n version: 1,\n state: \"ready\",\n pid: process.pid,\n id,\n startedAt,\n adminBase: `${supervisor.url}${adminPrefix}/fleet`,\n healthUrl: `${supervisor.url}${adminPrefix}/health`,\n services: Object.fromEntries(\n [...children].map(([name, child]) => [\n name,\n {\n protocol: child.protocol,\n url: publicUrl(child.url),\n healthUrl: child.healthUrl ?? `${supervisor?.url}${adminPrefix}/health`,\n adminUrl: child.adminUrl ?? `${supervisor?.url}${adminPrefix}/fleet`,\n namespaces: child.namespaces,\n processReady: true,\n protocolReady: true,\n },\n ]),\n ),\n }\n if (options.connectionsFile)\n await atomicWrite(options.connectionsFile, {\n version: 1,\n id,\n pid: process.pid,\n services: Object.fromEntries(\n [...children].map(([name, child]) => [\n name,\n { url: child.connection ?? child.url, namespaces: child.connections },\n ]),\n ),\n })\n if (options.readyFile) await atomicWrite(options.readyFile, manifest)\n options.onReady?.(manifest)\n return { manifest, children, fetch: api.fetch, close }\n } catch (error) {\n await close()\n throw error\n }\n}\n", "import { createServer } from \"node:http\"\nimport type { FetchAPI } from \"@crvouga/mockingbird-core\"\n\n/** Options for {@link serve}. */\nexport type NodeServeOptions = {\n port?: number\n host?: string\n}\n\n/**\n * Serve any Mockingbird {@link FetchAPI} over `node:http`.\n * Port defaults to `0`, so the OS assigns an ephemeral port (read from `server.address()`).\n */\nexport const serve = async (api: FetchAPI, options: NodeServeOptions = {}) => {\n const server = createServer(async (req, res) => {\n const chunks: Buffer[] = []\n for await (const chunk of req) {\n chunks.push(Buffer.from(chunk))\n }\n const body = Buffer.concat(chunks)\n const address = server.address()\n const port = typeof address === \"object\" && address !== null ? address.port : undefined\n const base = `http://${req.headers.host ?? `localhost:${port ?? 80}`}`\n const raw = req.url ?? \"/\"\n const url = new URL(raw.replace(/^\\/+/, \"/\"), base)\n const method = req.method ?? \"GET\"\n const init: RequestInit = { method, headers: req.headers as Record<string, string> }\n if (method !== \"GET\" && method !== \"HEAD\" && body.length > 0) {\n init.body = body\n }\n const aborted = new AbortController()\n res.once(\"close\", () => {\n if (!res.writableFinished) aborted.abort()\n })\n init.signal = aborted.signal\n const request = new Request(url, init)\n let response: Response\n try {\n response = await api.fetch(request)\n } catch (error) {\n // A `drop` fault: destroy the socket so the client sees the connection die.\n if ((error as { code?: string }).code === \"MOCKINGBIRD_DROP\") {\n req.socket.destroy()\n return\n }\n res.writeHead(500, { \"content-type\": \"application/json\" })\n res.end(\n JSON.stringify({\n error: {\n type: \"mockingbird_internal\",\n message: error instanceof Error ? error.message : String(error),\n },\n }),\n )\n return\n }\n // Headers#entries() joins repeated headers; Set-Cookie must stay one header per cookie.\n const headers: Record<string, string | string[]> = Object.fromEntries(response.headers)\n const cookies = response.headers.getSetCookie()\n if (cookies.length > 0) headers[\"set-cookie\"] = cookies\n if (!response.body) {\n res.writeHead(response.status, headers)\n res.end()\n return\n }\n // Stream the body chunk by chunk: event streams and long-polls must not be buffered.\n res.writeHead(response.status, headers)\n res.flushHeaders()\n const reader = response.body.getReader()\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n if (!res.write(value)) await new Promise<void>((resolve) => res.once(\"drain\", resolve))\n }\n res.end()\n } catch {\n res.destroy()\n } finally {\n reader.releaseLock()\n }\n })\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject)\n server.listen(options.port ?? 0, options.host, resolve)\n })\n return server\n}\n", "import type { Server } from \"node:http\"\nimport type { FetchAPI } from \"@crvouga/mockingbird-core\"\nimport { serve } from \"./serve.js\"\n\nexport type ListenOptions = {\n /** Default `0`: the OS picks a free port. */\n port?: number\n /** Default `127.0.0.1`: a mock should not be reachable off the machine by accident. */\n host?: string\n}\n\n/** A running server, with the address it actually bound. */\nexport type Listening = {\n url: string\n port: number\n host: string\n server: Server\n close(): Promise<void>\n}\n\nexport const listen = async (api: FetchAPI, options: ListenOptions = {}): Promise<Listening> => {\n const host = options.host ?? \"127.0.0.1\"\n const server = await serve(api, { port: options.port ?? 0, host })\n const address = server.address()\n const port = typeof address === \"object\" && address !== null ? address.port : (options.port ?? 0)\n const shown = host.includes(\":\") ? `[${host}]` : host\n return {\n url: `http://${shown}:${port}`,\n port,\n host,\n server,\n close: () =>\n new Promise<void>((resolve, reject) => {\n // Stop accepting first, then drop keep-alive sockets so close() can finish.\n server.close((error) => {\n if (error && (error as { code?: string }).code !== \"ERR_SERVER_NOT_RUNNING\") reject(error)\n else resolve()\n })\n server.closeAllConnections?.()\n }),\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["readFile", "readFile", "resolve", "resolve", "response", "resolve", "readFile", "adminKey", "createServer", "readFile"]
|
|
7
|
+
}
|