@crvouga/mockingbird-service-junction 3.2.0 → 3.3.1
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 +12 -4
- package/DISCOVERY.md +23 -0
- package/README.md +68 -76
- package/dist/chunk-7VCGZYMN.js +12825 -0
- package/dist/chunk-7VCGZYMN.js.map +7 -0
- package/dist/{chunk-R4LSSE3O.js → chunk-S2GN6HLD.js} +136 -25
- package/dist/chunk-S2GN6HLD.js.map +7 -0
- package/dist/cli.js +4 -3
- package/dist/cli.js.map +2 -2
- package/dist/index.d.ts +345 -40
- package/dist/index.js +1 -1
- package/dist/server.d.ts +678 -80
- package/dist/server.js +2 -2
- package/package.json +3 -2
- package/dist/chunk-R4LSSE3O.js.map +0 -7
- package/dist/chunk-WEQDMSPV.js +0 -11020
- package/dist/chunk-WEQDMSPV.js.map +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-junction
|
|
2
2
|
|
|
3
|
-
## 3.
|
|
3
|
+
## 3.3.1 (2026-10-15)
|
|
4
|
+
|
|
5
|
+
### Fixes and improvements
|
|
6
|
+
|
|
7
|
+
- diagnose blockers and identify deploys ([c0bc46c](https://github.com/crvouga/mockingbird/commit/c0bc46c0c5e485674338f2c23f9a46e6a273a0a7))
|
|
8
|
+
|
|
9
|
+
## 3.3.0 (2026-10-12)
|
|
4
10
|
|
|
5
11
|
### Features
|
|
6
12
|
|
|
7
|
-
-
|
|
13
|
+
- unify the emulator control plane ([2068547](https://github.com/crvouga/mockingbird/commit/2068547bedde4e2e2331c1657c1a78120e84428b))
|
|
14
|
+
|
|
15
|
+
## 3.2.0 (2026-10-08)
|
|
8
16
|
|
|
9
|
-
###
|
|
17
|
+
### Features
|
|
10
18
|
|
|
11
|
-
-
|
|
19
|
+
- match Emulate provider coverage ([16b65ce](https://github.com/crvouga/mockingbird/commit/16b65ce791fa216f28ec1521df636f57771d9c5e))
|
|
12
20
|
|
|
13
21
|
## 3.1.0 (2026-10-08)
|
|
14
22
|
|
package/DISCOVERY.md
CHANGED
|
@@ -35,6 +35,7 @@ repository; live parity is an additional oracle check, not a substitute for the
|
|
|
35
35
|
|
|
36
36
|
- `GET /__admin/health`
|
|
37
37
|
- `GET /__admin`
|
|
38
|
+
- `GET /__admin/controls`
|
|
38
39
|
- `GET /__admin/state`
|
|
39
40
|
- `GET /__admin/requests`
|
|
40
41
|
- `GET /__admin/metrics`
|
|
@@ -45,6 +46,28 @@ For HTTP services, use `x-mockingbird-namespace` (or the documented credential/p
|
|
|
45
46
|
parallel tests do not share state. Admin state, journal, metrics and fault-preset endpoints are
|
|
46
47
|
designed for assertions and diagnosis by consuming test suites.
|
|
47
48
|
|
|
49
|
+
## Admin controls
|
|
50
|
+
|
|
51
|
+
Every HTTP emulator takes the same controls, and each means the same thing at boot
|
|
52
|
+
(`createRuntime({ adminControls })`, `serve --admin-controls`), over `/__admin`, and in a request header:
|
|
53
|
+
|
|
54
|
+
| Control | Admin API | Request header | Interface |
|
|
55
|
+
| --- | --- | --- | --- |
|
|
56
|
+
| Isolate state by key | `GET /__admin/namespaces` | `x-mockingbird-namespace: <key>` | [Namespaces](https://mockingbird.chrisvouga.dev/docs/admin-controls/namespaces) |
|
|
57
|
+
| Reset to boot | `POST /__admin/reset` | `x-mockingbird-reset: true` | [Reset](https://mockingbird.chrisvouga.dev/docs/admin-controls/reset) |
|
|
58
|
+
| Snapshot and restore | `POST /__admin/snapshots`, `POST /__admin/snapshots/<id>/restore` | `x-mockingbird-snapshot: <name>`, `x-mockingbird-restore: <name>` | [Snapshot and restore](https://mockingbird.chrisvouga.dev/docs/admin-controls/snapshot-and-restore) |
|
|
59
|
+
| Move the clock | `POST /__admin/clock` | `x-mockingbird-time: <instant>`, `x-mockingbird-advance: <duration>` | [Clock](https://mockingbird.chrisvouga.dev/docs/admin-controls/clock) |
|
|
60
|
+
| Read or branch from old state | `GET /__admin/timeline` | `x-mockingbird-at: <checkpoint>`, `x-mockingbird-branch: <name>` | [Checkpoints and branches](https://mockingbird.chrisvouga.dev/docs/admin-controls/checkpoints-and-branches) |
|
|
61
|
+
| Inject a fault | `POST /__admin/faults` | `x-mockingbird-fault: <fault>` | [Faults](https://mockingbird.chrisvouga.dev/docs/admin-controls/faults) |
|
|
62
|
+
| Latency and timeouts | `PUT /__admin/latency` | `x-mockingbird-delay: <duration>`, `x-mockingbird-timeout: <duration>` | [Latency and timeouts](https://mockingbird.chrisvouga.dev/docs/admin-controls/latency-and-timeouts) |
|
|
63
|
+
| Replay from a seed | `PUT /__admin/controls` | `x-mockingbird-seed: <seed>` | [Seed and determinism](https://mockingbird.chrisvouga.dev/docs/admin-controls/seed-and-determinism) |
|
|
64
|
+
| See what happened | `GET /__admin/requests`, `GET /__admin/state` | | [Journal, metrics and state](https://mockingbird.chrisvouga.dev/docs/admin-controls/journal-and-state) |
|
|
65
|
+
|
|
66
|
+
`GET /__admin` lists every route and header with a one-line summary, and `GET /__admin/controls`
|
|
67
|
+
is the namespace's current control document. The same seed and the same requests give the same
|
|
68
|
+
bytes. Setting controls as an emulator starts is [Boot](https://mockingbird.chrisvouga.dev/docs/admin-controls/boot); every route and header is in
|
|
69
|
+
the [Reference](https://mockingbird.chrisvouga.dev/docs/admin-controls/reference). The full guide: <https://mockingbird.chrisvouga.dev/docs/admin-controls>.
|
|
70
|
+
|
|
48
71
|
## Report a mismatch or missing capability
|
|
49
72
|
|
|
50
73
|
Follow the [agent reporting contract](https://github.com/crvouga/mockingbird/blob/main/docs/REPORTING_ISSUES.md). Include package version,
|
package/README.md
CHANGED
|
@@ -108,13 +108,16 @@ npx mockingbird-junction serve --config mockingbird.json
|
|
|
108
108
|
### `createServer` (Node)
|
|
109
109
|
|
|
110
110
|
```ts
|
|
111
|
+
import { VitalClient } from "@tryvital/vital-node"
|
|
111
112
|
import { createServer } from "@crvouga/mockingbird-service-junction/server"
|
|
112
113
|
|
|
113
114
|
const server = await createServer() // shipped corpus, any free port
|
|
114
|
-
const health = await fetch(`${server.url}/__admin/health`)
|
|
115
|
-
console.log(health.status) // 200
|
|
116
115
|
|
|
117
|
-
//
|
|
116
|
+
// The Vital SDK your application already uses. Only `environment` changes.
|
|
117
|
+
const junction = new VitalClient({ apiKey: "sk_us_mockingbird", environment: server.url })
|
|
118
|
+
const user = await junction.user.create({ clientUserId: "app-user-1" })
|
|
119
|
+
console.log(user.userId) // "<uuid>"
|
|
120
|
+
|
|
118
121
|
await server.close()
|
|
119
122
|
```
|
|
120
123
|
|
|
@@ -125,100 +128,89 @@ or a loaded corpus). The result has `url`, `port`, `runtime` and `close()`.
|
|
|
125
128
|
### `createRuntime` (any Fetch server)
|
|
126
129
|
|
|
127
130
|
`createRuntime` is the whole served emulator — health, admin, namespaces, clock, faults, metrics — as a
|
|
128
|
-
single runtime-neutral `fetch(request)`. Hand it to `Bun.serve`, a Worker, or
|
|
131
|
+
single runtime-neutral `fetch(request)`. Hand it to `Bun.serve`, a Worker, or any other Fetch server:
|
|
129
132
|
|
|
130
133
|
```ts
|
|
134
|
+
import { VitalClient } from "@tryvital/vital-node"
|
|
131
135
|
import { createRuntime } from "@crvouga/mockingbird-service-junction"
|
|
132
136
|
import { defaultCorpus } from "@crvouga/mockingbird-service-junction/corpus"
|
|
133
137
|
|
|
134
138
|
const junction = createRuntime({ corpus: defaultCorpus })
|
|
135
|
-
const
|
|
136
|
-
"x-vital-api-key": "sk_us_mockingbird",
|
|
137
|
-
"x-mockingbird-namespace": worker, // isolates this worker's data
|
|
138
|
-
"content-type": "application/json",
|
|
139
|
-
})
|
|
139
|
+
const server = Bun.serve({ port: 0, fetch: (request) => junction.fetch(request) })
|
|
140
140
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
141
|
+
// One SDK client per worker. The URL prefix isolates that worker's data.
|
|
142
|
+
const as = (worker: string) =>
|
|
143
|
+
new VitalClient({
|
|
144
|
+
apiKey: "sk_us_mockingbird",
|
|
145
|
+
environment: `${server.url.origin}/__admin/ns/${worker}`,
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
await as("worker-1").user.create({ clientUserId: "patient-1" })
|
|
149
|
+
const other = await as("worker-2").user.getAll()
|
|
150
|
+
console.log(other.users) // [] (worker-2 sees none of worker-1's users)
|
|
150
151
|
|
|
151
152
|
await junction.reset("worker-1") // or junction.reset("*") for every namespace
|
|
153
|
+
await server.stop()
|
|
152
154
|
```
|
|
153
155
|
|
|
154
|
-
###
|
|
156
|
+
### Pointing `@tryvital/vital-node` at it
|
|
157
|
+
|
|
158
|
+
The official SDK takes the base URL as `environment` (this is how the package's SDK drop-in test
|
|
159
|
+
constructs it). It has no option for a custom `fetch`, so it needs a served URL; a client that
|
|
160
|
+
accepts a `fetch` can call `createRuntime().fetch` in-process instead.
|
|
155
161
|
|
|
156
162
|
```ts
|
|
157
|
-
import {
|
|
163
|
+
import { VitalClient } from "@tryvital/vital-node"
|
|
164
|
+
import { createServer } from "@crvouga/mockingbird-service-junction/server"
|
|
158
165
|
|
|
159
|
-
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
method,
|
|
166
|
-
headers,
|
|
167
|
-
body: body === undefined ? undefined : JSON.stringify(body),
|
|
168
|
-
}),
|
|
169
|
-
)
|
|
170
|
-
return { status: response.status, json: (await response.json()) as Record<string, unknown> }
|
|
171
|
-
}
|
|
166
|
+
// Test setup: the emulator, with its clock set.
|
|
167
|
+
const server = await createServer()
|
|
168
|
+
await fetch(`${server.url}/__admin/clock`, {
|
|
169
|
+
method: "POST",
|
|
170
|
+
body: JSON.stringify({ set: "2030-01-01T00:00:00Z" }),
|
|
171
|
+
})
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
const
|
|
173
|
+
// The application's calls, through the SDK.
|
|
174
|
+
const junction = new VitalClient({ apiKey: "sk_us_mockingbird", environment: server.url })
|
|
175
|
+
const user = await junction.user.create({ clientUserId: "app-user-1" })
|
|
175
176
|
|
|
176
|
-
const
|
|
177
|
-
const labTests = catalog.json.data as { id: string; method: string }[]
|
|
177
|
+
const labTests = await junction.labTests.get()
|
|
178
178
|
const testkit = labTests.find((test) => test.method === "testkit")
|
|
179
179
|
if (testkit === undefined) throw new Error("default catalog has a testkit test")
|
|
180
180
|
|
|
181
|
-
const order = await
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
181
|
+
const { order } = await junction.labTests.createOrder({
|
|
182
|
+
userId: user.userId,
|
|
183
|
+
patientDetails: {
|
|
184
|
+
firstName: "Ada",
|
|
185
|
+
lastName: "Lovelace",
|
|
186
186
|
dob: "1990-01-01",
|
|
187
187
|
gender: "female",
|
|
188
|
-
|
|
188
|
+
phoneNumber: "+14155551234",
|
|
189
189
|
email: "ada@example.com",
|
|
190
190
|
},
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
patientAddress: {
|
|
192
|
+
firstLine: "1 N Central Ave",
|
|
193
193
|
city: "Phoenix",
|
|
194
194
|
state: "AZ",
|
|
195
195
|
zip: "85004",
|
|
196
196
|
country: "US",
|
|
197
197
|
},
|
|
198
|
-
|
|
198
|
+
orderSet: { labTestIds: [testkit.id] },
|
|
199
199
|
})
|
|
200
|
-
console.log(order.
|
|
200
|
+
console.log(order.id, order.status) // "<uuid>" "received"
|
|
201
201
|
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
// Test assertion: every event the emulator published, oldest first.
|
|
203
|
+
const events = server.runtime.instance().webhookEvents()
|
|
204
|
+
console.log(events.map((event) => event.event_type)) // ["labtest.order.created"]
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
simulations (`POST /v3/order/{id}/test?final_status=completed&delay=<seconds>`) become due; the
|
|
208
|
-
delayed transition is applied on the next `GET /v3/order/{id}` whose `now` is past the due time.
|
|
209
|
-
|
|
210
|
-
### Pointing `@tryvital/vital-node` at it
|
|
211
|
-
|
|
212
|
-
The official SDK takes the base URL as `environment` (this is how the package's SDK drop-in test
|
|
213
|
-
constructs it):
|
|
214
|
-
|
|
215
|
-
```js
|
|
216
|
-
import { VitalClient } from "@tryvital/vital-node"
|
|
217
|
-
|
|
218
|
-
const client = new VitalClient({ apiKey: "sk_us_mockingbird", environment: baseUrl })
|
|
219
|
-
const user = await client.user.create({ clientUserId: "app-user-1" })
|
|
206
|
+
await server.close()
|
|
220
207
|
```
|
|
221
208
|
|
|
209
|
+
The clock (`POST /__admin/clock`, or `now` in milliseconds on `JunctionAPI`) drives
|
|
210
|
+
`created_on`/`updated_at` fields, webhook timestamps, and when delayed simulations
|
|
211
|
+
(`POST /v3/order/{id}/test?final_status=completed&delay=<seconds>`) become due; the delayed
|
|
212
|
+
transition is applied on the next `GET /v3/order/{id}` whose clock is past the due time.
|
|
213
|
+
|
|
222
214
|
If your app guards Junction hosts with an allowlist, allow `127.0.0.1` / `localhost` for tests
|
|
223
215
|
while keeping the production and sandbox guards intact.
|
|
224
216
|
|
|
@@ -646,24 +638,24 @@ With `webhooks` / `--webhook-url`, every event is delivered signed, with retries
|
|
|
646
638
|
retries now; `POST /__admin/webhooks/{id}/replay` redelivers one message.
|
|
647
639
|
|
|
648
640
|
```ts
|
|
649
|
-
import {
|
|
641
|
+
import { Webhook } from "svix"
|
|
642
|
+
import { createRuntime } from "@crvouga/mockingbird-service-junction"
|
|
650
643
|
|
|
651
644
|
const secret = "whsec_bW9ja2luZ2JpcmQtdGVzdC1zZWNyZXQ="
|
|
652
645
|
const junction = createRuntime({
|
|
653
646
|
webhooks: { url: "http://127.0.0.1:3100/webhooks/junction", secret, retryDelaysMs: [0, 500, 2000] },
|
|
654
647
|
})
|
|
655
648
|
|
|
656
|
-
//
|
|
657
|
-
const accept = async (request: Request) =>
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
)
|
|
649
|
+
// The application's receiver, verifying with the official svix package as it does for real Junction:
|
|
650
|
+
const accept = async (request: Request) => {
|
|
651
|
+
const body = await request.text()
|
|
652
|
+
new Webhook(secret).verify(body, {
|
|
653
|
+
"svix-id": request.headers.get("svix-id") ?? "",
|
|
654
|
+
"svix-timestamp": request.headers.get("svix-timestamp") ?? "",
|
|
655
|
+
"svix-signature": request.headers.get("svix-signature") ?? "",
|
|
656
|
+
}) // throws unless the signature is valid
|
|
657
|
+
return JSON.parse(body) as { event_type: string }
|
|
658
|
+
}
|
|
667
659
|
console.log(typeof accept, junction.webhooks?.deliveries().length) // "function" 0
|
|
668
660
|
```
|
|
669
661
|
|