@crvouga/mockingbird-service-junction 0.1.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 +319 -0
- package/dist/catalog.d.ts +63 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +736 -0
- package/dist/catalog.js.map +1 -0
- package/dist/coverage-corpus.d.ts +76 -0
- package/dist/coverage-corpus.d.ts.map +1 -0
- package/dist/coverage-corpus.js +166 -0
- package/dist/coverage-corpus.js.map +1 -0
- package/dist/generated/openapi.d.ts +7 -0
- package/dist/generated/openapi.d.ts.map +1 -0
- package/dist/generated/openapi.js +5 -0
- package/dist/generated/openapi.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +121 -0
- package/dist/index.js.map +1 -0
- package/dist/lab-accounts.d.ts +38 -0
- package/dist/lab-accounts.d.ts.map +1 -0
- package/dist/lab-accounts.js +213 -0
- package/dist/lab-accounts.js.map +1 -0
- package/dist/orders.d.ts +18 -0
- package/dist/orders.d.ts.map +1 -0
- package/dist/orders.js +1247 -0
- package/dist/orders.js.map +1 -0
- package/dist/prefetch.d.ts +43 -0
- package/dist/prefetch.d.ts.map +1 -0
- package/dist/prefetch.js +131 -0
- package/dist/prefetch.js.map +1 -0
- package/dist/reshape.d.ts +22 -0
- package/dist/reshape.d.ts.map +1 -0
- package/dist/reshape.js +190 -0
- package/dist/reshape.js.map +1 -0
- package/dist/results.d.ts +19 -0
- package/dist/results.d.ts.map +1 -0
- package/dist/results.js +221 -0
- package/dist/results.js.map +1 -0
- package/dist/scheduling.d.ts +113 -0
- package/dist/scheduling.d.ts.map +1 -0
- package/dist/scheduling.js +1508 -0
- package/dist/scheduling.js.map +1 -0
- package/dist/sealed-corpus.d.ts +28 -0
- package/dist/sealed-corpus.d.ts.map +1 -0
- package/dist/sealed-corpus.js +17 -0
- package/dist/sealed-corpus.js.map +1 -0
- package/dist/seed-from.d.ts +27 -0
- package/dist/seed-from.d.ts.map +1 -0
- package/dist/seed-from.js +523 -0
- package/dist/seed-from.js.map +1 -0
- package/dist/seeds.d.ts +9 -0
- package/dist/seeds.d.ts.map +1 -0
- package/dist/seeds.js +9 -0
- package/dist/seeds.js.map +1 -0
- package/dist/state.d.ts +270 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +448 -0
- package/dist/state.js.map +1 -0
- package/dist/users.d.ts +13 -0
- package/dist/users.d.ts.map +1 -0
- package/dist/users.js +669 -0
- package/dist/users.js.map +1 -0
- package/package.json +80 -0
package/README.md
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# @crvouga/mockingbird-service-junction
|
|
2
|
+
|
|
3
|
+
Stateful, in-process mock of the [Junction (formerly Vital) API](https://docs.junction.com/) for
|
|
4
|
+
test suites: users (`/v2/user`), the lab-testing catalog, lab orders (create, cancel, simulate,
|
|
5
|
+
results, requisitions), and at-home phlebotomy / patient-service-center (PSC) scheduling
|
|
6
|
+
(availability, booking, reschedule, cancel), plus the `labtest.order.*` and
|
|
7
|
+
`labtest.appointment.updated` webhook events. All 39 operations in the vendored OpenAPI subset are
|
|
8
|
+
served; behaviour is verified by differential property tests against the Junction sandbox.
|
|
9
|
+
|
|
10
|
+
Use it when your backend calls Junction via `fetch` or `@tryvital/vital-node` and you want tests to
|
|
11
|
+
run offline, deterministically, with no sandbox key.
|
|
12
|
+
|
|
13
|
+
- Operation coverage: [SUPPORT.md](https://github.com/crvouga/mockingbird/blob/main/packages/service/junction/SUPPORT.md)
|
|
14
|
+
- Drop-in readiness, sealed corpus and sandbox quirks the mock mirrors:
|
|
15
|
+
[docs/drop-in.md](https://github.com/crvouga/mockingbird/blob/main/packages/service/junction/docs/drop-in.md)
|
|
16
|
+
- Consumer wiring checklist: [docs/qa-followon.md](https://github.com/crvouga/mockingbird/blob/main/packages/service/junction/docs/qa-followon.md)
|
|
17
|
+
- Behaviour notes: [docs/behavior.md](https://github.com/crvouga/mockingbird/blob/main/packages/service/junction/docs/behavior.md)
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install -D @crvouga/mockingbird-service-junction
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
ESM only. Requires Node >= 22 or Bun >= 1.2. No native dependencies: state lives in an in-memory
|
|
26
|
+
SQLite engine (`@crvouga/mockingbird-service-sqlite`, pure TypeScript). To serve it over HTTP also
|
|
27
|
+
install `@crvouga/mockingbird-adapter-node` (works under Node and Bun) or
|
|
28
|
+
`@crvouga/mockingbird-adapter-bun`.
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
Behaviour the examples rely on (all from the source):
|
|
33
|
+
|
|
34
|
+
- **Any host works.** Routing uses only the path (`/v2/...`, `/v3/...`).
|
|
35
|
+
- **Auth:** every request needs an `x-vital-api-key` header; any value is accepted (sandbox keys
|
|
36
|
+
look like `sk_us_*` / `sk_eu_*`). Without it the response is `401 {"detail":"Missing x-vital-api-key"}`.
|
|
37
|
+
State is not partitioned by key: all requests share one team (`team_id`
|
|
38
|
+
`11111111-1111-4111-8111-111111111111`).
|
|
39
|
+
- Bodies are JSON with the API's snake_case field names.
|
|
40
|
+
- A default synthetic catalog is installed at construction (lab tests with methods
|
|
41
|
+
`walk_in_test`, `testkit` and `at_home_phlebotomy`); `installCorpus` replaces it with a recording.
|
|
42
|
+
|
|
43
|
+
### In-process (inject `fetch`)
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { JunctionAPI } from "@crvouga/mockingbird-service-junction"
|
|
47
|
+
|
|
48
|
+
const junction = new JunctionAPI({ now: () => Date.UTC(2030, 0, 1) })
|
|
49
|
+
const headers = { "x-vital-api-key": "sk_us_mockingbird", "content-type": "application/json" }
|
|
50
|
+
|
|
51
|
+
const call = async (method: string, path: string, body?: unknown) => {
|
|
52
|
+
const response = await junction.fetch(
|
|
53
|
+
new Request(`https://api.sandbox.tryvital.io${path}`, {
|
|
54
|
+
method,
|
|
55
|
+
headers,
|
|
56
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
57
|
+
}),
|
|
58
|
+
)
|
|
59
|
+
return { status: response.status, json: (await response.json()) as Record<string, unknown> }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const user = await call("POST", "/v2/user", { client_user_id: "app-user-1" })
|
|
63
|
+
const userId = String(user.json.user_id)
|
|
64
|
+
|
|
65
|
+
const catalog = await call("GET", "/v3/lab_test")
|
|
66
|
+
const labTests = catalog.json.data as { id: string; method: string }[]
|
|
67
|
+
const testkit = labTests.find((test) => test.method === "testkit")
|
|
68
|
+
if (testkit === undefined) throw new Error("default catalog has a testkit test")
|
|
69
|
+
|
|
70
|
+
const order = await call("POST", "/v3/order", {
|
|
71
|
+
user_id: userId,
|
|
72
|
+
patient_details: {
|
|
73
|
+
first_name: "Ada",
|
|
74
|
+
last_name: "Lovelace",
|
|
75
|
+
dob: "1990-01-01",
|
|
76
|
+
gender: "female",
|
|
77
|
+
phone_number: "+14155551234",
|
|
78
|
+
email: "ada@example.com",
|
|
79
|
+
},
|
|
80
|
+
patient_address: {
|
|
81
|
+
first_line: "1 N Central Ave",
|
|
82
|
+
city: "Phoenix",
|
|
83
|
+
state: "AZ",
|
|
84
|
+
zip: "85004",
|
|
85
|
+
country: "US",
|
|
86
|
+
},
|
|
87
|
+
order_set: { lab_test_ids: [testkit.id] },
|
|
88
|
+
})
|
|
89
|
+
console.log(order.status, (order.json.order as { id: string }).id) // 200 "<uuid>"
|
|
90
|
+
|
|
91
|
+
// Every event the mock published, oldest first:
|
|
92
|
+
console.log(junction.webhookEvents().map((event) => event.event_type)) // ["labtest.order.created"]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`now` (milliseconds) drives `created_on`/`updated_at` fields, webhook timestamps, and when delayed
|
|
96
|
+
simulations (`POST /v3/order/{id}/test?final_status=completed&delay=<seconds>`) become due; the
|
|
97
|
+
delayed transition is applied on the next `GET /v3/order/{id}` whose `now` is past the due time.
|
|
98
|
+
|
|
99
|
+
### Over HTTP
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { serve } from "@crvouga/mockingbird-adapter-node"
|
|
103
|
+
import { JunctionAPI } from "@crvouga/mockingbird-service-junction"
|
|
104
|
+
|
|
105
|
+
const junction = new JunctionAPI()
|
|
106
|
+
const server = await serve(junction, { port: 0, host: "127.0.0.1" }) // port 0 = ephemeral
|
|
107
|
+
const address = server.address()
|
|
108
|
+
if (address === null || typeof address === "string") throw new Error("not listening")
|
|
109
|
+
const baseUrl = `http://127.0.0.1:${address.port}`
|
|
110
|
+
|
|
111
|
+
const response = await fetch(`${baseUrl}/v2/user`, {
|
|
112
|
+
headers: { "x-vital-api-key": "sk_us_mockingbird" },
|
|
113
|
+
})
|
|
114
|
+
console.log(response.status) // 200
|
|
115
|
+
|
|
116
|
+
server.close()
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Pointing `@tryvital/vital-node` at it
|
|
120
|
+
|
|
121
|
+
The official SDK takes the base URL as `environment` (this is how the package's SDK drop-in test
|
|
122
|
+
constructs it):
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
import { VitalClient } from "@tryvital/vital-node"
|
|
126
|
+
|
|
127
|
+
const client = new VitalClient({ apiKey: "sk_us_mockingbird", environment: baseUrl })
|
|
128
|
+
const user = await client.user.create({ clientUserId: "app-user-1" })
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
If your app guards Junction hosts with an allowlist, allow `127.0.0.1` / `localhost` for tests
|
|
132
|
+
while keeping the production and sandbox guards intact.
|
|
133
|
+
|
|
134
|
+
### Webhooks
|
|
135
|
+
|
|
136
|
+
`onWebhook` is called synchronously with every event the mock records
|
|
137
|
+
(`labtest.order.created`, `labtest.order.updated`, `labtest.appointment.updated`). Delivery and
|
|
138
|
+
signing are up to you. Junction delivers through Svix; the contributor server script signs with
|
|
139
|
+
`svix-id`, `svix-timestamp` and `svix-signature: v1,<base64 HMAC-SHA256 of "<timestamp>.<body>">`,
|
|
140
|
+
keyed by the base64-decoded secret after stripping a `whsec_` prefix:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { createHmac, randomUUID } from "node:crypto"
|
|
144
|
+
import { JunctionAPI } from "@crvouga/mockingbird-service-junction"
|
|
145
|
+
|
|
146
|
+
const WEBHOOK_URL = "http://127.0.0.1:3100/webhooks/junction"
|
|
147
|
+
const WEBHOOK_SECRET = "whsec_bW9ja2luZ2JpcmQtdGVzdC1zZWNyZXQ="
|
|
148
|
+
|
|
149
|
+
const junction = new JunctionAPI({
|
|
150
|
+
onWebhook: (event) => {
|
|
151
|
+
const timestamp = Math.floor(Date.now() / 1000).toString()
|
|
152
|
+
const body = JSON.stringify(event)
|
|
153
|
+
const key = Buffer.from(WEBHOOK_SECRET.replace(/^whsec_/, ""), "base64")
|
|
154
|
+
const signature = createHmac("sha256", key).update(`${timestamp}.${body}`).digest("base64")
|
|
155
|
+
void fetch(WEBHOOK_URL, {
|
|
156
|
+
method: "POST",
|
|
157
|
+
headers: {
|
|
158
|
+
"content-type": "application/json",
|
|
159
|
+
"svix-id": randomUUID(),
|
|
160
|
+
"svix-timestamp": timestamp,
|
|
161
|
+
"svix-signature": `v1,${signature}`,
|
|
162
|
+
},
|
|
163
|
+
body,
|
|
164
|
+
}).catch(() => undefined)
|
|
165
|
+
},
|
|
166
|
+
})
|
|
167
|
+
console.log(junction.webhookEvents().length) // 0
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`webhookDeliveryAttempts()` returns the retry schedule Junction would follow for each event
|
|
171
|
+
(8 attempts: immediately, then 5s, 5m, 30m, 2h, 5h, 10h, 10h, with jitter controlled by the
|
|
172
|
+
`webhook` option). It is a record only; nothing is re-sent.
|
|
173
|
+
|
|
174
|
+
### Resetting between tests
|
|
175
|
+
|
|
176
|
+
`reset()` clears users, orders, appointments and webhook events, then re-installs the default
|
|
177
|
+
catalog, or the corpus passed to `installCorpus` if there was one:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { beforeEach, expect, test } from "bun:test"
|
|
181
|
+
import { JunctionAPI } from "@crvouga/mockingbird-service-junction"
|
|
182
|
+
|
|
183
|
+
const junction = new JunctionAPI()
|
|
184
|
+
beforeEach(() => junction.reset())
|
|
185
|
+
|
|
186
|
+
test("starts with no users", async () => {
|
|
187
|
+
const response = await junction.fetch(
|
|
188
|
+
new Request("https://api.sandbox.tryvital.io/v2/user", {
|
|
189
|
+
headers: { "x-vital-api-key": "sk_us_mockingbird" },
|
|
190
|
+
}),
|
|
191
|
+
)
|
|
192
|
+
expect(await response.json()).toMatchObject({ users: [], total: 0 })
|
|
193
|
+
})
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Sealed corpus (optional, higher fidelity)
|
|
197
|
+
|
|
198
|
+
Provider-owned inventory (area serviceability, PSC site lists, the team's lab catalog, labs, lab
|
|
199
|
+
accounts) cannot be synthesized faithfully. A sealed corpus is an exact sandbox recording of those
|
|
200
|
+
parameter-stable GETs; installing it makes the mock answer them byte-for-byte. The recording in the
|
|
201
|
+
repo (`corpus/sandbox-sealed.json`) is not shipped in the npm package; copy it or record your own
|
|
202
|
+
(see Development).
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { readFileSync } from "node:fs"
|
|
206
|
+
import { JunctionAPI, parseSealedCorpus } from "@crvouga/mockingbird-service-junction"
|
|
207
|
+
|
|
208
|
+
const junction = new JunctionAPI()
|
|
209
|
+
const recording: unknown = JSON.parse(readFileSync("test/junction-sandbox-sealed.json", "utf8"))
|
|
210
|
+
junction.installCorpus(parseSealedCorpus(recording)) // re-applied on every reset()
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Availability is never recorded (its slot dates and single-use `booking_key`s cannot be replayed);
|
|
214
|
+
it always comes from the deterministic generator.
|
|
215
|
+
|
|
216
|
+
## API
|
|
217
|
+
|
|
218
|
+
| Export | Description |
|
|
219
|
+
| --- | --- |
|
|
220
|
+
| `JunctionAPI` | Class. `new JunctionAPI(options?)`; implements `FetchAPI` (`fetch(request: Request): Promise<Response>`). |
|
|
221
|
+
| `JUNCTION_NAMESPACE` | `"junction"` — SQLite namespace holding the mock's state when sharing a `sqlite` client. |
|
|
222
|
+
| `document` | The vendored Junction OpenAPI document (Mockingbird subset) that drives routing. |
|
|
223
|
+
| `operationIds` | Every `operationId` in `document`. |
|
|
224
|
+
| `supportedOperationIds` | The `operationId`s the mock implements (all of them). |
|
|
225
|
+
| `parseSealedCorpus` | `(value: unknown) => SealedCorpus`; throws on a non-object or unsupported `version`. |
|
|
226
|
+
| `SEALED_CORPUS_VERSION` | Current sealed-corpus format version (`1`). |
|
|
227
|
+
| `observationCacheKey` | `(url, method = "GET", body?) => string` — the `"<METHOD> <path>?<sorted query> <json>"` key used by the observation cache and corpus. |
|
|
228
|
+
| `prefetchCoverageObservations` | Parity helper: records coverage-ZIP area/PSC/availability reads from the real sandbox into an observation cache before `seedFrom`. |
|
|
229
|
+
| `reshapeCoverageGeoCommand` | Parity-walk hook that pins geo/availability parameters onto the coverage corpus. |
|
|
230
|
+
| `COVERAGE_ZIPS` | ZIP codes the coverage corpus seals for area/PSC reads. |
|
|
231
|
+
| `PSC_AVAILABILITY_ZIPS` | ZIPs that also get PSC availability observations. |
|
|
232
|
+
| `PHLEBOTOMY_AVAILABILITY_ZIPS` | ZIPs served for phlebotomy availability (`["85004"]`). |
|
|
233
|
+
| `PSC_LAB_IDS` | Lab ids used for PSC reads (`[4, 6, 13, 25]`). |
|
|
234
|
+
| `AVAILABILITY_ADDRESS` | Stable Phoenix, AZ address used in availability requests so cache keys match. |
|
|
235
|
+
| `AVAILABILITY_START_DATE` | Far-future availability `start_date` (`"2099-06-15"`). |
|
|
236
|
+
|
|
237
|
+
`JunctionAPI` members:
|
|
238
|
+
|
|
239
|
+
| Member | Description |
|
|
240
|
+
| --- | --- |
|
|
241
|
+
| `fetch(request)` | Handle one Junction REST request. |
|
|
242
|
+
| `reset()` | `Promise<void>` — clear state; re-install the default catalog or the installed corpus. |
|
|
243
|
+
| `installCorpus(corpus)` | Install a `SealedCorpus` (observation cache, catalog, labs, lab accounts). |
|
|
244
|
+
| `seedFrom(source, observations?)` | `Promise<SeedReport>` — copy users, orders, appointments, catalog and labs from a real Junction environment. |
|
|
245
|
+
| `ensureLabTests(source, ids)` / `ensureOrders(source, ids)` | `Promise<number>` — fetch specific lab tests / orders from `source` if missing. |
|
|
246
|
+
| `markUserDeleted(userId)` | Mark a user as deleted (subsequent reads behave like a deleted user). |
|
|
247
|
+
| `webhookEvents()` | `JunctionWebhookEvent[]`, oldest first. |
|
|
248
|
+
| `webhookDeliveryAttempts()` | Recorded retry schedule per event. |
|
|
249
|
+
| `app` / `sqlite` | The underlying Hono app and `SqliteClient`. |
|
|
250
|
+
|
|
251
|
+
Options and types:
|
|
252
|
+
|
|
253
|
+
```text
|
|
254
|
+
type JunctionAPIOptions = {
|
|
255
|
+
sqlite?: SqliteClient // share one client across services; default: fresh in-memory DB
|
|
256
|
+
now?: () => number // clock in ms; default Date.now
|
|
257
|
+
onWebhook?: WebhookPublisher // called with every recorded event
|
|
258
|
+
webhook?: JunctionWebhookOptions
|
|
259
|
+
}
|
|
260
|
+
type JunctionWebhookOptions = { seed?: number; jitterRatio?: number /* 0..1, default 0.1 */; timeoutMs?: number /* default 15000 */ }
|
|
261
|
+
type JunctionWebhookEvent = {
|
|
262
|
+
event_type: "labtest.order.created" | "labtest.order.updated" | "labtest.appointment.updated"
|
|
263
|
+
data: Record<string, unknown>; team_id: string; user_id: string; client_user_id: string
|
|
264
|
+
}
|
|
265
|
+
type WebhookPublisher = (event: JunctionWebhookEvent) => void
|
|
266
|
+
type SeedSource = { fetch: (request: Request) => Promise<Response>; baseUrl: string; headers: Record<string, string> }
|
|
267
|
+
type SeedObservations = { getCache?: ReadonlyMap<string, GetCacheEntry> }
|
|
268
|
+
type SeedReport = { users; orders; appointments; catalogTests; labs; cacheEntries: number }
|
|
269
|
+
type GetCacheEntry = { status: number; headers: Record<string, string>; body: unknown }
|
|
270
|
+
type SealedCorpus = { version: 1; recordedAt; source; observations; catalog: { labTests; labs; expectedResults }; labAccounts }
|
|
271
|
+
type OperationId / SupportedOperationId // string unions of operationIds / supportedOperationIds
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Seeding from the real sandbox (needs network and a real key):
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
import { JunctionAPI } from "@crvouga/mockingbird-service-junction"
|
|
278
|
+
|
|
279
|
+
const junction = new JunctionAPI()
|
|
280
|
+
const report = await junction.seedFrom({
|
|
281
|
+
fetch: (request) => globalThis.fetch(request),
|
|
282
|
+
baseUrl: "https://api.sandbox.tryvital.io",
|
|
283
|
+
headers: { "x-vital-api-key": process.env.JUNCTION_API_KEY ?? "" },
|
|
284
|
+
})
|
|
285
|
+
console.log(report.users, report.orders)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Development
|
|
289
|
+
|
|
290
|
+
For contributors to the mockingbird repo only; these scripts and the corpus are not shipped in the
|
|
291
|
+
npm package.
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
bun test # offline suites, incl. scheduling state space
|
|
295
|
+
bun test junction.seed.property.test.ts # offline monkey (no network)
|
|
296
|
+
bun test junction.sdk.property.test.ts # drives the served mock through @tryvital/vital-node
|
|
297
|
+
|
|
298
|
+
bun run mock:serve # HOST=127.0.0.1 PORT=8787; GET /health, POST /__admin/reset
|
|
299
|
+
MOCKINGBIRD_JUNCTION_CORPUS=corpus/sandbox-sealed.json bun run mock:serve
|
|
300
|
+
bun run corpus:record # record corpus/sandbox-sealed.json (needs a sandbox key)
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
`mock:serve` installs `corpus/sandbox-sealed.json` when present, and signs webhooks to
|
|
304
|
+
`MOCKINGBIRD_JUNCTION_WEBHOOK_URL` with `MOCKINGBIRD_JUNCTION_WEBHOOK_SECRET` (delivery is disabled
|
|
305
|
+
without a secret). `/health` and `/__admin/reset` bypass the `x-vital-api-key` gate.
|
|
306
|
+
|
|
307
|
+
Live parity (primary proof is seedParity: warmup N on the sandbox, `seedFrom`, then lockstep M):
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
bun run parity # default mode=seed against api.sandbox.tryvital.io
|
|
311
|
+
bun run parity -- --warmup 15 --compare 30 --runs 25
|
|
312
|
+
bun run parity -- --mode=empty # legacy empty-start differential
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Webhook parity needs a public receiver; see
|
|
316
|
+
[docs/webhook-parity.md](https://github.com/crvouga/mockingbird/blob/main/packages/service/junction/docs/webhook-parity.md).
|
|
317
|
+
The `client-parity*.ts` SDK scenarios are deprecated as proof and kept only as manual probes.
|
|
318
|
+
|
|
319
|
+
Part of [mockingbird](https://github.com/crvouga/mockingbird) — agent integration guide: [`@crvouga/mockingbird`](https://github.com/crvouga/mockingbird/tree/main/packages/facade#readme).
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Lab-test catalog mirrored from the Junction sandbox (`GET /v3/lab_test`). */
|
|
2
|
+
export type CatalogMarker = {
|
|
3
|
+
id: number;
|
|
4
|
+
name: string;
|
|
5
|
+
slug: string;
|
|
6
|
+
description: string | null;
|
|
7
|
+
lab_id: number | null;
|
|
8
|
+
provider_id: string | null;
|
|
9
|
+
type: string | null;
|
|
10
|
+
unit: string | null;
|
|
11
|
+
price: string | null;
|
|
12
|
+
aoe: unknown;
|
|
13
|
+
a_la_carte_enabled: boolean;
|
|
14
|
+
common_tat_days: number | null;
|
|
15
|
+
worst_case_tat_days: number | null;
|
|
16
|
+
is_orderable: boolean | null;
|
|
17
|
+
};
|
|
18
|
+
export type ExpectedResultLoinc = {
|
|
19
|
+
id: number;
|
|
20
|
+
name: string;
|
|
21
|
+
slug: string;
|
|
22
|
+
code: string;
|
|
23
|
+
unit: string | null;
|
|
24
|
+
};
|
|
25
|
+
export type ExpectedResult = {
|
|
26
|
+
id: number;
|
|
27
|
+
name: string;
|
|
28
|
+
slug: string;
|
|
29
|
+
lab_id: number | null;
|
|
30
|
+
provider_id: string | null;
|
|
31
|
+
required: boolean;
|
|
32
|
+
loinc: ExpectedResultLoinc | null;
|
|
33
|
+
};
|
|
34
|
+
export type LabTestRecord = {
|
|
35
|
+
id: string;
|
|
36
|
+
slug: string;
|
|
37
|
+
name: string;
|
|
38
|
+
sample_type: string;
|
|
39
|
+
method: string;
|
|
40
|
+
price: number;
|
|
41
|
+
is_active: boolean;
|
|
42
|
+
status: string;
|
|
43
|
+
fasting: boolean;
|
|
44
|
+
lab: Record<string, unknown>;
|
|
45
|
+
markers: CatalogMarker[] | null;
|
|
46
|
+
is_delegated: boolean;
|
|
47
|
+
auto_generated: boolean;
|
|
48
|
+
has_collection_instructions: boolean | null;
|
|
49
|
+
common_tat_days: number | null;
|
|
50
|
+
worst_case_tat_days: number | null;
|
|
51
|
+
};
|
|
52
|
+
export declare const LAB_TEST_CATALOG: readonly LabTestRecord[];
|
|
53
|
+
export declare const labTestById: (id: string) => LabTestRecord | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Team lab inventory mirrored from the sandbox (`GET /v3/lab_tests/labs`). The endpoint
|
|
56
|
+
* returns the team's full lab list — not just the labs referenced by catalog tests — in
|
|
57
|
+
* this exact order.
|
|
58
|
+
*/
|
|
59
|
+
export declare const TEAM_LABS: readonly Record<string, unknown>[];
|
|
60
|
+
/** Panel constituents mirrored from the sandbox (`GET /v3/lab_tests/{id}/markers`). */
|
|
61
|
+
export declare const EXPECTED_RESULTS: Readonly<Record<string, readonly ExpectedResult[]>>;
|
|
62
|
+
export declare const expectedResultsFor: (labTestId: string) => readonly ExpectedResult[];
|
|
63
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,GAAG,EAAE,OAAO,CAAA;IACZ,kBAAkB,EAAE,OAAO,CAAA;IAC3B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,YAAY,EAAE,OAAO,GAAG,IAAI,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC5B,OAAO,EAAE,aAAa,EAAE,GAAG,IAAI,CAAA;IAC/B,YAAY,EAAE,OAAO,CAAA;IACrB,cAAc,EAAE,OAAO,CAAA;IACvB,2BAA2B,EAAE,OAAO,GAAG,IAAI,CAAA;IAC3C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;CACnC,CAAA;AAED,eAAO,MAAM,gBAAgB,EAAE,SAAS,aAAa,EAyJpD,CAAA;AAED,eAAO,MAAM,WAAW,GAAI,IAAI,MAAM,KAAG,aAAa,GAAG,SACR,CAAA;AAEjD;;;;GAIG;AACH,eAAO,MAAM,SAAS,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EA6NvD,CAAA;AAED,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC,CA8VhF,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,WAAW,MAAM,KAAG,SAAS,cAAc,EAC3C,CAAA"}
|