@crvouga/mockingbird-service-junction 0.1.1 → 0.2.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +448 -133
  3. package/dist/chunk-4C6VYRJN.js +18 -0
  4. package/dist/chunk-4C6VYRJN.js.map +7 -0
  5. package/dist/chunk-KKNULV7X.js +406 -0
  6. package/dist/chunk-KKNULV7X.js.map +7 -0
  7. package/dist/chunk-MA3HSUVD.js +7771 -0
  8. package/dist/chunk-MA3HSUVD.js.map +7 -0
  9. package/dist/chunk-MD4KYEIM.js +145758 -0
  10. package/dist/chunk-MD4KYEIM.js.map +7 -0
  11. package/dist/cli.js +214 -0
  12. package/dist/cli.js.map +7 -0
  13. package/dist/corpus.d.ts +97 -0
  14. package/dist/corpus.js +8 -0
  15. package/dist/corpus.js.map +7 -0
  16. package/dist/index.d.ts +1131 -21
  17. package/dist/index.js +82 -120
  18. package/dist/index.js.map +7 -1
  19. package/dist/server.d.ts +978 -0
  20. package/dist/server.js +16 -0
  21. package/dist/server.js.map +7 -0
  22. package/package.json +32 -12
  23. package/dist/catalog.d.ts +0 -63
  24. package/dist/catalog.d.ts.map +0 -1
  25. package/dist/catalog.js +0 -736
  26. package/dist/catalog.js.map +0 -1
  27. package/dist/coverage-corpus.d.ts +0 -76
  28. package/dist/coverage-corpus.d.ts.map +0 -1
  29. package/dist/coverage-corpus.js +0 -166
  30. package/dist/coverage-corpus.js.map +0 -1
  31. package/dist/generated/openapi.d.ts +0 -7
  32. package/dist/generated/openapi.d.ts.map +0 -1
  33. package/dist/generated/openapi.js +0 -5
  34. package/dist/generated/openapi.js.map +0 -1
  35. package/dist/index.d.ts.map +0 -1
  36. package/dist/lab-accounts.d.ts +0 -38
  37. package/dist/lab-accounts.d.ts.map +0 -1
  38. package/dist/lab-accounts.js +0 -213
  39. package/dist/lab-accounts.js.map +0 -1
  40. package/dist/orders.d.ts +0 -18
  41. package/dist/orders.d.ts.map +0 -1
  42. package/dist/orders.js +0 -1247
  43. package/dist/orders.js.map +0 -1
  44. package/dist/prefetch.d.ts +0 -43
  45. package/dist/prefetch.d.ts.map +0 -1
  46. package/dist/prefetch.js +0 -131
  47. package/dist/prefetch.js.map +0 -1
  48. package/dist/reshape.d.ts +0 -22
  49. package/dist/reshape.d.ts.map +0 -1
  50. package/dist/reshape.js +0 -190
  51. package/dist/reshape.js.map +0 -1
  52. package/dist/results.d.ts +0 -19
  53. package/dist/results.d.ts.map +0 -1
  54. package/dist/results.js +0 -221
  55. package/dist/results.js.map +0 -1
  56. package/dist/scheduling.d.ts +0 -113
  57. package/dist/scheduling.d.ts.map +0 -1
  58. package/dist/scheduling.js +0 -1508
  59. package/dist/scheduling.js.map +0 -1
  60. package/dist/sealed-corpus.d.ts +0 -28
  61. package/dist/sealed-corpus.d.ts.map +0 -1
  62. package/dist/sealed-corpus.js +0 -17
  63. package/dist/sealed-corpus.js.map +0 -1
  64. package/dist/seed-from.d.ts +0 -27
  65. package/dist/seed-from.d.ts.map +0 -1
  66. package/dist/seed-from.js +0 -523
  67. package/dist/seed-from.js.map +0 -1
  68. package/dist/seeds.d.ts +0 -9
  69. package/dist/seeds.d.ts.map +0 -1
  70. package/dist/seeds.js +0 -9
  71. package/dist/seeds.js.map +0 -1
  72. package/dist/state.d.ts +0 -270
  73. package/dist/state.d.ts.map +0 -1
  74. package/dist/state.js +0 -448
  75. package/dist/state.js.map +0 -1
  76. package/dist/users.d.ts +0 -13
  77. package/dist/users.d.ts.map +0 -1
  78. package/dist/users.js +0 -669
  79. package/dist/users.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,36 +1,1118 @@
1
- import type { FetchAPI } from "@crvouga/mockingbird-core";
2
- import { type APIOptions } from "@crvouga/mockingbird-service";
3
- import type { SqliteClient } from "@crvouga/mockingbird-sqlite";
4
- import type { Hono } from "hono";
5
- import type { SealedCorpus } from "./sealed-corpus.js";
6
- import { type SeedObservations, type SeedReport, type SeedSource } from "./seed-from.js";
7
- import { type JunctionWebhookEvent, type JunctionWebhookOptions, type WebhookPublisher } from "./state.js";
8
- export { COVERAGE_ZIPS, PHLEBOTOMY_AVAILABILITY_ZIPS, PSC_AVAILABILITY_ZIPS, PSC_LAB_IDS, } from "./coverage-corpus.js";
9
- export type { OperationId, SupportedOperationId } from "./generated/openapi.js";
10
- export { document, operationIds, supportedOperationIds } from "./generated/openapi.js";
11
- export { prefetchCoverageObservations } from "./prefetch.js";
12
- export { AVAILABILITY_ADDRESS, AVAILABILITY_START_DATE, reshapeCoverageGeoCommand, } from "./reshape.js";
13
- export { parseSealedCorpus, SEALED_CORPUS_VERSION, type SealedCorpus, } from "./sealed-corpus.js";
14
- export type { SeedObservations, SeedReport, SeedSource } from "./seed-from.js";
15
- export type { GetCacheEntry, JunctionWebhookEvent, JunctionWebhookOptions, WebhookPublisher, } from "./state.js";
16
- export { observationCacheKey } from "./state.js";
17
- export declare const JUNCTION_NAMESPACE = "junction";
1
+ import { Hono } from 'hono';
2
+
3
+ /**
4
+ * The single source of time for a service.
5
+ *
6
+ * Every timestamp a mock writes reads from here, so a suite moves time instead of
7
+ * sleeping: appointment windows, result delays and expiries become reachable in
8
+ * milliseconds. A frozen clock also makes timestamps reproducible from a seed.
9
+ */
10
+ type ClockState = {
11
+ /** Current epoch milliseconds. */
12
+ now: number;
13
+ /** True while time does not advance on its own. */
14
+ frozen: boolean;
15
+ /** Milliseconds this clock adds to its underlying source. */
16
+ offsetMs: number;
17
+ };
18
+ type Clock = {
19
+ now(): number;
20
+ /** Pin the clock to an exact instant, keeping it frozen if it already was. */
21
+ set(epochMs: number): void;
22
+ /** Move the clock forward, or back with a negative delta. */
23
+ advance(deltaMs: number): void;
24
+ /** Stop time at the current instant. */
25
+ freeze(): void;
26
+ /** Resume from the current instant. */
27
+ unfreeze(): void;
28
+ /** Drop back to the underlying source, live. */
29
+ reset(): void;
30
+ state(): ClockState;
31
+ };
32
+
33
+ /** Bind values accepted by Mockingbird's SQLite port (matches sqlite-mem / better-sqlite3). */
34
+ type SqliteValue$1 = null | number | bigint | string | Uint8Array | boolean;
35
+ /** Mutation counters returned by {@link SqliteStatement.run}. */
36
+ type SqliteRunResult$1 = {
37
+ changes: number;
38
+ lastInsertRowid: number | bigint;
39
+ };
40
+ /**
41
+ * Prepared statement bound to a {@link SqliteClient}.
42
+ *
43
+ * Pass bind values as rest arguments on each call (no sticky `bind()`).
44
+ */
45
+ interface SqliteStatement$1 {
46
+ run(...params: SqliteValue$1[]): SqliteRunResult$1;
47
+ all<T = Record<string, unknown>>(...params: SqliteValue$1[]): T[];
48
+ get<T = Record<string, unknown>>(...params: SqliteValue$1[]): T | undefined;
49
+ }
50
+ /**
51
+ * Sync SQLite client port owned by Mockingbird.
52
+ *
53
+ * Duck-typed so `@crvouga/mockingbird-service-sqlite` `Database`, better-sqlite3, and wrapped
54
+ * `bun:sqlite` instances all work when they expose this surface.
55
+ */
56
+ interface SqliteClient$1 {
57
+ exec(sql: string): void;
58
+ prepare(sql: string): SqliteStatement$1;
59
+ transaction<T>(fn: () => T): T;
60
+ }
61
+
62
+ /**
63
+ * Seeded pseudo-random numbers, so anything a mock invents — ids, jitter, which
64
+ * request a percentage fault hits — is reproducible from a seed.
65
+ *
66
+ * mulberry32: small, fast, and stable across runtimes, which matters more here
67
+ * than statistical quality.
68
+ */
69
+ type Rng = {
70
+ /** Next value in `[0, 1)`. */
71
+ next(): number;
72
+ /** Next integer in `[min, max]`. */
73
+ int(min: number, max: number): number;
74
+ /** Restart the stream from its seed. */
75
+ reset(): void;
76
+ seed: number;
77
+ };
78
+
79
+ /**
80
+ * A deliberate failure injected in front of an operation.
81
+ *
82
+ * This is how a suite reaches the vendor's failure modes without the vendor: the
83
+ * quota error that only appears when a shared sandbox is full, the 429 that only
84
+ * appears under load, the 5xx that proves a retry path works.
85
+ */
86
+ type FaultRule = {
87
+ /** Stable id, so a suite can retire exactly the rule it added. */
88
+ id: string;
89
+ /** Fault only this operation. Omit to match every operation. */
90
+ operationId?: string;
91
+ /** Fault only this HTTP method, case-insensitive. Omit to match every method. */
92
+ method?: string;
93
+ /** Fault only paths starting with this prefix. Omit to match every path. */
94
+ pathPrefix?: string;
95
+ /**
96
+ * Fault only this namespace. Omit (or `"*"`) to fault every namespace — which is what
97
+ * an in-process caller usually wants, and what a parallel worker usually does not:
98
+ * rules added through `POST /__admin/faults` default to the calling namespace.
99
+ */
100
+ namespace?: string;
101
+ status: number;
102
+ /** Response body, serialized as JSON. A string is sent as-is. */
103
+ body?: unknown;
104
+ headers?: Record<string, string>;
105
+ /** Retire the rule after this many faults. Omit to keep it until removed. */
106
+ count?: number;
107
+ /** Fault this fraction of matching requests, `0`–`1`. Default `1`. */
108
+ rate?: number;
109
+ /** Hold the response back this long, to exercise timeouts. */
110
+ delayMs?: number;
111
+ };
112
+ /** What a request looks like to the fault matcher. */
113
+ type FaultCandidate = {
114
+ operationId: string | undefined;
115
+ method: string;
116
+ path: string;
117
+ namespace: string;
118
+ };
119
+ type FaultRegistry = {
120
+ add(rule: FaultRule): FaultRule;
121
+ list(): (FaultRule & {
122
+ remaining: number | null;
123
+ hits: number;
124
+ })[];
125
+ remove(id: string): boolean;
126
+ clear(): void;
127
+ /**
128
+ * The fault this request should get, or `undefined` to let it through.
129
+ * Consumes one of the matching rule's remaining uses.
130
+ */
131
+ take(candidate: FaultCandidate): Promise<{
132
+ id: string;
133
+ response: Response;
134
+ } | undefined>;
135
+ };
136
+
137
+ /** One handled request, as the structured log sees it. */
138
+ type RequestLog = {
139
+ service: string;
140
+ namespace: string;
141
+ operationId: string | undefined;
142
+ method: string;
143
+ path: string;
144
+ status: number;
145
+ durationMs: number;
146
+ /** True when the path matched no operation in the contract. */
147
+ unmatched: boolean;
148
+ /** Set when a fault rule produced the response. */
149
+ faultId?: string;
150
+ };
151
+ type MetricsReport = {
152
+ requests: number;
153
+ /** Counts keyed `<operationId> <status>`. */
154
+ byOperation: Record<string, number>;
155
+ /**
156
+ * Paths that matched no operation, most frequent first.
157
+ *
158
+ * This is the early-warning signal: a consumer calling something the mock does
159
+ * not implement shows up here as a count, before it fails a suite as a 404.
160
+ */
161
+ unmatched: {
162
+ method: string;
163
+ path: string;
164
+ count: number;
165
+ }[];
166
+ faults: number;
167
+ totalDurationMs: number;
168
+ };
169
+ type Metrics = {
170
+ record(entry: RequestLog): void;
171
+ report(): MetricsReport;
172
+ reset(): void;
173
+ };
174
+
175
+ /**
176
+ * A point-in-time copy of everything a service namespace holds.
177
+ *
178
+ * All service state lives in the two core tables keyed by namespace, so a snapshot
179
+ * is generic: any service gets per-test rollback without knowing its own schema.
180
+ * Restoring is much cheaper than rebuilding a namespace from a corpus.
181
+ */
182
+ type NamespaceSnapshot = {
183
+ namespace: string;
184
+ records: {
185
+ collection: string;
186
+ id: string;
187
+ seq: number;
188
+ value: string;
189
+ }[];
190
+ sequences: {
191
+ name: string;
192
+ kind: string;
193
+ value: number;
194
+ }[];
195
+ };
196
+
197
+ /**
198
+ * Anything that can answer a Fetch `Request` with a `Response`.
199
+ *
200
+ * Every Mockingbird service implements this, and every runtime adapter consumes it.
201
+ * It is the only contract shared across the whole graph.
202
+ */
203
+ interface FetchAPI$1 {
204
+ fetch(request: Request): Promise<Response>;
205
+ }
206
+
207
+ /** What the runtime needs from a service: a Fetch handler it can reset. */
208
+ type ServiceInstance = FetchAPI$1 & {
209
+ reset(): Promise<void>;
210
+ };
211
+ type ServiceRuntime<T extends ServiceInstance> = FetchAPI$1 & {
212
+ readonly name: string;
213
+ readonly sqlite: SqliteClient$1;
214
+ readonly clock: Clock;
215
+ readonly faults: FaultRegistry;
216
+ readonly metrics: Metrics;
217
+ readonly rng: Rng;
218
+ /** The instance behind `namespace` (the default one when omitted), created on first use. */
219
+ instance(namespace?: string): T;
220
+ /** Public names of every namespace created so far. */
221
+ namespaces(): string[];
222
+ /** Reset one namespace, or every namespace with `"*"`. */
223
+ reset(namespace?: string): Promise<void>;
224
+ snapshot(namespace?: string): NamespaceSnapshot;
225
+ restore(snapshot: NamespaceSnapshot, namespace?: string): void;
226
+ };
227
+
228
+ /** Options every provider constructor accepts. */
229
+ type APIOptions = {
230
+ /** Sync SQLite client. Defaults to `@crvouga/mockingbird-service-sqlite`. */
231
+ sqlite?: SqliteClient$1;
232
+ /** Clock used for `created`-style fields. Default `Date.now`. */
233
+ now?: () => number;
234
+ /**
235
+ * Storage namespace for this instance's records. Instances sharing one SQLite
236
+ * client stay isolated when their namespaces differ. Defaults to the service name.
237
+ */
238
+ namespace?: string;
239
+ };
240
+
241
+ /** Bind values accepted by Mockingbird's SQLite port (matches sqlite-mem / better-sqlite3). */
242
+ type SqliteValue = null | number | bigint | string | Uint8Array | boolean;
243
+ /** Mutation counters returned by {@link SqliteStatement.run}. */
244
+ type SqliteRunResult = {
245
+ changes: number;
246
+ lastInsertRowid: number | bigint;
247
+ };
248
+ /**
249
+ * Prepared statement bound to a {@link SqliteClient}.
250
+ *
251
+ * Pass bind values as rest arguments on each call (no sticky `bind()`).
252
+ */
253
+ interface SqliteStatement {
254
+ run(...params: SqliteValue[]): SqliteRunResult;
255
+ all<T = Record<string, unknown>>(...params: SqliteValue[]): T[];
256
+ get<T = Record<string, unknown>>(...params: SqliteValue[]): T | undefined;
257
+ }
258
+ /**
259
+ * Sync SQLite client port owned by Mockingbird.
260
+ *
261
+ * Duck-typed so `@crvouga/mockingbird-service-sqlite` `Database`, better-sqlite3, and wrapped
262
+ * `bun:sqlite` instances all work when they expose this surface.
263
+ */
264
+ interface SqliteClient {
265
+ exec(sql: string): void;
266
+ prepare(sql: string): SqliteStatement;
267
+ transaction<T>(fn: () => T): T;
268
+ }
269
+
270
+ /** Lab-test catalog mirrored from the Junction sandbox (`GET /v3/lab_test`). */
271
+ type CatalogMarker = {
272
+ id: number;
273
+ name: string;
274
+ slug: string;
275
+ description: string | null;
276
+ lab_id: number | null;
277
+ provider_id: string | null;
278
+ type: string | null;
279
+ unit: string | null;
280
+ price: string | null;
281
+ aoe: unknown;
282
+ a_la_carte_enabled: boolean;
283
+ common_tat_days: number | null;
284
+ worst_case_tat_days: number | null;
285
+ is_orderable: boolean | null;
286
+ };
287
+ type ExpectedResultLoinc = {
288
+ id: number;
289
+ name: string;
290
+ slug: string;
291
+ code: string;
292
+ unit: string | null;
293
+ };
294
+ type ExpectedResult = {
295
+ id: number;
296
+ name: string;
297
+ slug: string;
298
+ lab_id: number | null;
299
+ provider_id: string | null;
300
+ required: boolean;
301
+ loinc: ExpectedResultLoinc | null;
302
+ };
303
+ type LabTestRecord = {
304
+ id: string;
305
+ slug: string;
306
+ name: string;
307
+ sample_type: string;
308
+ method: string;
309
+ price: number;
310
+ is_active: boolean;
311
+ status: string;
312
+ fasting: boolean;
313
+ lab: Record<string, unknown>;
314
+ markers: CatalogMarker[] | null;
315
+ is_delegated: boolean;
316
+ auto_generated: boolean;
317
+ has_collection_instructions: boolean | null;
318
+ common_tat_days: number | null;
319
+ worst_case_tat_days: number | null;
320
+ };
321
+
322
+ type GetCacheEntry = {
323
+ status: number;
324
+ headers: Record<string, string>;
325
+ body: unknown;
326
+ };
327
+ declare const observationCacheKey: (url: URL | string, method?: string, body?: unknown) => string;
328
+ type JunctionWebhookEvent = {
329
+ event_type: "labtest.order.created" | "labtest.order.updated" | "labtest.appointment.updated";
330
+ data: Record<string, unknown>;
331
+ team_id: string;
332
+ user_id: string;
333
+ client_user_id: string;
334
+ };
335
+ type WebhookDeliveryAttempt = {
336
+ message_id: string;
337
+ event: JunctionWebhookEvent;
338
+ attempt: number;
339
+ scheduled_at: string;
340
+ timeout_ms: number;
341
+ acknowledged: boolean;
342
+ };
343
+ type OrderEventRecord = {
344
+ id: number;
345
+ created_at: string;
346
+ status: string;
347
+ status_detail: null;
348
+ };
349
+ type OrderTransactionEmbed = {
350
+ id: string;
351
+ status: "active" | "completed" | "cancelled";
352
+ orders: Array<{
353
+ id: string;
354
+ low_level_status: string;
355
+ low_level_status_created_at: string;
356
+ origin: string;
357
+ parent_id: null;
358
+ created_at: string;
359
+ updated_at: string;
360
+ }>;
361
+ };
362
+ type OrderRecord = {
363
+ id: string;
364
+ user_id: string;
365
+ team_id: string;
366
+ patient_details: Record<string, unknown>;
367
+ patient_address: Record<string, unknown>;
368
+ lab_test: LabTestRecord;
369
+ details: Record<string, unknown>;
370
+ sample_id: string | null;
371
+ notes: null;
372
+ clinical_notes: string | null;
373
+ passthrough: string | null;
374
+ /** Echoed from the request's opaque provider account id; absent when the order had none. */
375
+ lab_account_id?: string;
376
+ created_at: string;
377
+ updated_at: string;
378
+ events: OrderEventRecord[];
379
+ status: string;
380
+ last_event: OrderEventRecord;
381
+ physician: {
382
+ first_name: string;
383
+ last_name: string;
384
+ npi: string;
385
+ };
386
+ health_insurance_id: null;
387
+ requisition_form_url: string | null;
388
+ shipping_details: null;
389
+ has_abn: boolean;
390
+ billing_type: string;
391
+ priority: boolean;
392
+ activate_by: null;
393
+ icd_codes: string[] | null;
394
+ interpretation: string | null;
395
+ has_missing_results: boolean | null;
396
+ result_types: string[] | null;
397
+ expected_result_by_date: string | null;
398
+ worst_case_result_by_date: string | null;
399
+ origin: string;
400
+ order_transaction: OrderTransactionEmbed;
401
+ };
402
+ /**
403
+ * A lab account as the ordering rules read it. Structurally `LabAccountRecord` from
404
+ * lab-accounts.ts, restated here because that module imports this one.
405
+ */
406
+ type StoredLabAccount = {
407
+ id: string;
408
+ lab: string;
409
+ org_id: string | null;
410
+ status: "active" | "pending" | "suspended" | "ready_to_launch";
411
+ delegated_flow: "order_delegated" | "result_delegated" | "fully_delegated" | "not_delegated";
412
+ provider_account_id: string;
413
+ account_name: string | null;
414
+ default_clinical_notes: string | null;
415
+ business_units: string[] | null;
416
+ allowed_billing: Record<string, readonly string[]>;
417
+ team_id_allowlist: readonly string[];
418
+ };
419
+ /**
420
+ * A chosen result for one order, installed through the admin API. Replaces the
421
+ * generated biomarker lines, so a suite can assert on an exact payload — an abnormal
422
+ * panel, a critical value, a missing marker — instead of whatever the generator made.
423
+ */
424
+ type ResultFixture = {
425
+ /** The fixture's name, for logs and `GET /__admin/results`. */
426
+ name: string;
427
+ results?: unknown[];
428
+ missing_results?: unknown[] | null;
429
+ interpretation?: string;
430
+ /** Served from `GET /v3/order/{id}/result/pdf` instead of the generated PDF. */
431
+ pdf_base64?: string;
432
+ };
433
+ /**
434
+ * How serviceability reads answer a ZIP the loaded corpus has no record for.
435
+ *
436
+ * - `synthetic`: invent plausible coverage. Right for property tests that walk random ZIPs.
437
+ * - `corpus`: refuse with a documented Mockingbird error. Right for a mock standing in for
438
+ * the vendor, where invented coverage would silently disagree with production.
439
+ */
440
+ type GeoMode = "synthetic" | "corpus";
441
+ type WebhookPublisher = (event: JunctionWebhookEvent) => void;
442
+ type JunctionWebhookOptions = {
443
+ seed?: number;
444
+ jitterRatio?: number;
445
+ timeoutMs?: number;
446
+ };
447
+
448
+ /**
449
+ * Anything that can answer a Fetch `Request` with a `Response`.
450
+ *
451
+ * Every Mockingbird service implements this, and every runtime adapter consumes it.
452
+ * It is the only contract shared across the whole graph.
453
+ */
454
+ interface FetchAPI {
455
+ fetch(request: Request): Promise<Response>;
456
+ }
457
+
458
+ type LabAccountStatus = "active" | "pending" | "suspended" | "ready_to_launch";
459
+ type LabAccountDelegatedFlow = "order_delegated" | "result_delegated" | "fully_delegated" | "not_delegated";
460
+ type LabAccountRecord = {
461
+ id: string;
462
+ lab: string;
463
+ org_id: string | null;
464
+ status: LabAccountStatus;
465
+ delegated_flow: LabAccountDelegatedFlow;
466
+ provider_account_id: string;
467
+ account_name: string | null;
468
+ default_clinical_notes: string | null;
469
+ business_units: string[] | null;
470
+ allowed_billing: Record<string, readonly string[]>;
471
+ team_id_allowlist: readonly string[];
472
+ };
473
+ /** The 50 `USState` values from the docs, alphabetical by state name. */
474
+ declare const US_STATES: readonly string[];
475
+ declare const TEAM_LAB_ACCOUNTS: readonly LabAccountRecord[];
476
+ /**
477
+ * A lab account as a consumer configures it. Mirrors `ClientFacingLabAccount`, with
478
+ * every field but `id` and `lab` optional, plus a `states` shorthand for the common
479
+ * case of an account that client-bills in a fixed set of states.
480
+ */
481
+ type LabAccountInput = {
482
+ id: string;
483
+ /** Lab slug, e.g. `"quest"`, `"labcorp"`, `"bioreference"`. */
484
+ lab: string;
485
+ status?: LabAccountStatus;
486
+ delegated_flow?: LabAccountDelegatedFlow;
487
+ account_name?: string | null;
488
+ provider_account_id?: string;
489
+ org_id?: string | null;
490
+ business_units?: string[] | null;
491
+ default_clinical_notes?: string | null;
492
+ /** Billing type → states it is allowed in. Takes precedence over `states`. */
493
+ allowed_billing?: Record<string, readonly string[]>;
494
+ /** Shorthand for `allowed_billing: { client_bill: states }`. Default: every state. */
495
+ states?: readonly string[];
496
+ /** Teams the account is linked to. Default: the mock team, so it is selectable. */
497
+ team_id_allowlist?: readonly string[];
498
+ };
499
+ /** Normalize a consumer's configuration into the record the ordering rules read. */
500
+ declare const labAccountFromInput: (input: LabAccountInput) => LabAccountRecord;
501
+
502
+ /**
503
+ * Version of the sealed-corpus on-disk format. Bumped whenever the shape changes so a
504
+ * stale recording is rejected instead of half-applied.
505
+ */
506
+ declare const SEALED_CORPUS_VERSION = 1;
507
+ /**
508
+ * Exact sandbox recording of the provider-owned reads a drop-in consumer depends on:
509
+ * parameter-stable GET observations (area/PSC inventory), the lab catalog, team labs, and
510
+ * lab accounts. Availability is intentionally absent — its slot dates and single-use
511
+ * `booking_key`s can never match a live cache key, so it stays with the generator.
512
+ */
513
+ type SealedCorpus = {
514
+ version: typeof SEALED_CORPUS_VERSION;
515
+ recordedAt: string;
516
+ source: string;
517
+ /** SHA-256 of the recording's content, written by `corpus pull`. Identifies it in logs. */
518
+ fingerprint?: string;
519
+ observations: Record<string, GetCacheEntry>;
520
+ catalog: {
521
+ labTests: LabTestRecord[];
522
+ labs: Record<string, unknown>[];
523
+ expectedResults: Record<string, ExpectedResult[]>;
524
+ };
525
+ labAccounts: Record<string, unknown>[];
526
+ };
527
+ /** Throws on a non-object or an unrecognized `version`. */
528
+ declare const parseSealedCorpus: (value: unknown) => SealedCorpus;
529
+
530
+ type SeedSource = {
531
+ fetch: (request: Request) => Promise<Response>;
532
+ baseUrl: string;
533
+ headers: Record<string, string>;
534
+ };
535
+ type SeedObservations = {
536
+ getCache?: ReadonlyMap<string, GetCacheEntry>;
537
+ };
538
+ type SeedReport = {
539
+ users: number;
540
+ orders: number;
541
+ appointments: number;
542
+ catalogTests: number;
543
+ labs: number;
544
+ cacheEntries: number;
545
+ };
546
+
547
+ /**
548
+ * Signed webhook delivery, the way Junction delivers through Svix.
549
+ *
550
+ * Signing lives here, next to the events, so a consumer never hand-rolls it. The
551
+ * signature is standard Svix — `v1,` + base64 HMAC-SHA256, keyed by the base64 part of
552
+ * `whsec_…`, over `"<svix-id>.<svix-timestamp>.<body>"` — so the official `svix`
553
+ * verifier, and a receiver written for real Junction webhooks, accept it unchanged.
554
+ */
555
+ type WebhookEndpoint = {
556
+ url: string;
557
+ /** Svix signing secret, `whsec_<base64>`. A bare base64 secret is accepted too. */
558
+ secret: string;
559
+ /**
560
+ * Delay before each attempt, in ms; the first entry delays the first attempt. Default:
561
+ * Svix's schedule (immediately, 5 s, 5 min, 30 min, 2 h, 5 h, 10 h, 10 h).
562
+ */
563
+ retryDelaysMs?: readonly number[];
564
+ /** Abort an attempt that takes longer than this. Default 15 s, like Svix. */
565
+ timeoutMs?: number;
566
+ /** Added to every delivery, e.g. a routing scope for a shared receiver. */
567
+ headers?: Record<string, string>;
568
+ fetch?: (request: Request) => Promise<Response>;
569
+ };
570
+ type WebhookAttempt = {
571
+ attempt: number;
572
+ /** Wall-clock time of the attempt, ISO-8601. */
573
+ at: string;
574
+ /** Receiver's status code, or `null` when the request never completed. */
575
+ status: number | null;
576
+ error: string | null;
577
+ durationMs: number;
578
+ };
579
+ type WebhookDelivery = {
580
+ messageId: string;
581
+ namespace: string;
582
+ event: JunctionWebhookEvent;
583
+ state: "pending" | "delivered" | "failed";
584
+ attempts: WebhookAttempt[];
585
+ };
586
+ type WebhookDispatcher = {
587
+ /** Queue an event for delivery. Returns immediately; delivery runs in the background. */
588
+ publish(event: JunctionWebhookEvent, namespace: string): WebhookDelivery;
589
+ deliveries(namespace?: string): WebhookDelivery[];
590
+ /** Deliver a message again now, as a new attempt, whatever its state. */
591
+ replay(messageId: string): Promise<WebhookDelivery | undefined>;
592
+ /** Run every pending retry now instead of waiting out its delay. */
593
+ flush(): Promise<void>;
594
+ /** Resolve once no delivery is in flight. */
595
+ idle(): Promise<void>;
596
+ /** Cancel pending retries and forget every delivery. */
597
+ clear(namespace?: string): void;
598
+ };
599
+ /** The `svix-signature` value for one message: `v1,<base64 HMAC-SHA256>`. */
600
+ declare const signSvix: (secret: string, messageId: string, timestampSeconds: number, body: string) => Promise<string>;
601
+ /**
602
+ * Check a delivery's signature the way the `svix` verifier does: any `v1,` entry in the
603
+ * space-separated header matches, and the timestamp is within `toleranceSeconds`.
604
+ */
605
+ declare const verifySvix: (secret: string, headers: {
606
+ "svix-id": string;
607
+ "svix-timestamp": string;
608
+ "svix-signature": string;
609
+ }, body: string, options?: {
610
+ toleranceSeconds?: number;
611
+ now?: () => number;
612
+ }) => Promise<boolean>;
613
+ declare const createWebhookDispatcher: (endpoint: WebhookEndpoint) => WebhookDispatcher;
614
+
615
+ /**
616
+ * Every order status the contract defines, by collection method (from the vendored
617
+ * OpenAPI enum). An admin transition may only target a status real Junction can emit.
618
+ */
619
+ declare const ORDER_STATUSES_BY_METHOD: Readonly<Record<string, readonly string[]>>;
620
+ /**
621
+ * Friendly transition targets, per method, mapped onto the contract's own statuses.
622
+ * A name is absent for a method when no real status means it — walk-in tests have no
623
+ * draw event, so `collected` is refused there rather than approximated.
624
+ */
625
+ declare const TRANSITION_ALIASES: Readonly<Record<string, Readonly<Record<string, string>>>>;
626
+ /**
627
+ * Named results, as the flags `simulate_order` accepts. They steer the generator, so
628
+ * the payload keeps Junction's exact shape; `PUT /__admin/results/{order}` installs an
629
+ * exact payload instead.
630
+ */
631
+ declare const RESULT_FIXTURES: Readonly<Record<string, Record<string, unknown>>>;
632
+ /**
633
+ * Junction failure modes a suite can switch on by name. `sandbox_user_quota` reproduces
634
+ * the shared-sandbox error byte for byte (message as observed from a real sandbox team);
635
+ * the others are shape-plausible, not verified against Junction.
636
+ */
637
+ declare const FAULT_PRESETS: Readonly<Record<string, Omit<FaultRule, "id">>>;
638
+ /** Resolve a target (`completed`, `completed.completed`, or a full status) for a method. */
639
+ declare const resolveTransition: (method: string, target: string) => {
640
+ status: string;
641
+ } | {
642
+ error: string;
643
+ };
644
+ type JunctionAdminOptions = {
645
+ webhooks: WebhookDispatcher | undefined;
646
+ };
647
+
648
+ declare const DEFAULT_JUNCTION_BASE_URL = "https://api.sandbox.tryvital.io";
649
+ /** Key prefixes of sandbox team keys. `corpus pull` and `verify` refuse any other key. */
650
+ declare const SANDBOX_KEY_PREFIXES: readonly ["sk_us_", "sk_eu_"];
651
+ declare const isSandboxKey: (key: string) => boolean;
652
+ type PullCorpusOptions = {
653
+ apiKey: string;
654
+ baseUrl?: string;
655
+ /** ZIPs to record area and PSC coverage for. Default: the shipped coverage set. */
656
+ zips?: readonly string[];
657
+ /**
658
+ * Refresh only `zips` on top of this corpus, keeping everything else. How a single
659
+ * ZIP is added to a committed corpus without re-recording the whole thing.
660
+ */
661
+ base?: SealedCorpus;
662
+ /** Skip the team's catalog and lab accounts (coverage only). */
663
+ coverageOnly?: boolean;
664
+ fetch?: (request: Request) => Promise<Response>;
665
+ /** Pause between vendor calls, to stay under rate limits. Default 50 ms. */
666
+ minIntervalMs?: number;
667
+ onProgress?: (message: string) => void;
668
+ };
669
+ /** SHA-256 over a corpus's content — not its recording time — so a re-pull of unchanged data matches. */
670
+ declare const fingerprintCorpus: (corpus: SealedCorpus) => Promise<string>;
671
+ /** Record a sealed corpus from a real Junction team. Read-only: it only issues GETs. */
672
+ declare const pullCorpus: (options: PullCorpusOptions) => Promise<SealedCorpus>;
673
+ type SetDiff = {
674
+ added: string[];
675
+ removed: string[];
676
+ changed: string[];
677
+ };
678
+ type CorpusDiff = {
679
+ identical: boolean;
680
+ observations: SetDiff;
681
+ labTests: SetDiff;
682
+ labAccounts: SetDiff;
683
+ zips: {
684
+ added: string[];
685
+ removed: string[];
686
+ };
687
+ };
688
+ /** What changed between two recordings: coverage, catalog, accounts and raw observations. */
689
+ declare const diffCorpus: (before: SealedCorpus, after: SealedCorpus) => CorpusDiff;
690
+
691
+ /** High-signal ZIPs for availability prefetch (full routing corpus still used for area/psc). */
692
+ declare const PSC_AVAILABILITY_ZIPS: readonly ["85004", "85234", "11050", "10006", "90012", "96101", "92101", "60601", "33130", "98104", "75034", "07030"];
693
+ /**
694
+ * Phlebotomy availability is only served for a narrow Vital sandbox set.
695
+ * Probed: 85004 works; broader scheduling zips often 400. Keep prefetch/reshape sealed.
696
+ */
697
+ declare const PHLEBOTOMY_AVAILABILITY_ZIPS: readonly ["85004"];
698
+ /**
699
+ * Curated real ZIPs used as this package's parity coverage corpus (one per US state plus DC,
700
+ * plus scheduling pins). Prefetched into the observation cache so area/PSC parity is sealed
701
+ * for those geos.
702
+ */
703
+ declare const COVERAGE_ZIPS: readonly ["02108", "02903", "03101", "04101", "05401", "06103", "07030", "11050", "17316", "19107", "19801", "20001", "21201", "23219", "25301", "28202", "29201", "30303", "33130", "35203", "37219", "39201", "40202", "43215", "46204", "47630", "48226", "50309", "53202", "54977", "55415", "57104", "58102", "59101", "60601", "64106", "67202", "68102", "70112", "72201", "73102", "75034", "77002", "80202", "82001", "83702", "84111", "85004", "85234", "87102", "89101", "90012", "96101", "96813", "97204", "98104", "99501"];
704
+ /** Lab ids exercised by walk-in / PSC parity flows (quest, labcorp, bioreference, sonora). */
705
+ declare const PSC_LAB_IDS: readonly [4, 6, 13, 25];
706
+
707
+ /**
708
+ * The slice of OpenAPI 3.1 (and JSON Schema 2020-12) Mockingbird understands.
709
+ * Unknown keys (including `x-*` extensions) are preserved on every object.
710
+ */
711
+ type JsonPrimitive = string | number | boolean | null;
712
+ type JsonValue = JsonPrimitive | JsonValue[] | {
713
+ [key: string]: JsonValue;
714
+ };
715
+ type ReferenceObject = {
716
+ $ref: string;
717
+ description?: string;
718
+ summary?: string;
719
+ };
720
+ type SchemaType = "string" | "number" | "integer" | "boolean" | "object" | "array" | "null";
721
+ type SchemaObject = {
722
+ $ref?: string;
723
+ type?: SchemaType | SchemaType[];
724
+ title?: string;
725
+ description?: string;
726
+ format?: string;
727
+ enum?: JsonValue[];
728
+ const?: JsonValue;
729
+ default?: JsonValue;
730
+ example?: JsonValue;
731
+ examples?: JsonValue[];
732
+ nullable?: boolean;
733
+ deprecated?: boolean;
734
+ readOnly?: boolean;
735
+ writeOnly?: boolean;
736
+ minimum?: number;
737
+ maximum?: number;
738
+ exclusiveMinimum?: number;
739
+ exclusiveMaximum?: number;
740
+ multipleOf?: number;
741
+ minLength?: number;
742
+ maxLength?: number;
743
+ pattern?: string;
744
+ minItems?: number;
745
+ maxItems?: number;
746
+ uniqueItems?: boolean;
747
+ items?: SchemaObject;
748
+ prefixItems?: SchemaObject[];
749
+ minProperties?: number;
750
+ maxProperties?: number;
751
+ required?: string[];
752
+ properties?: Record<string, SchemaObject>;
753
+ additionalProperties?: boolean | SchemaObject;
754
+ propertyNames?: SchemaObject;
755
+ oneOf?: SchemaObject[];
756
+ anyOf?: SchemaObject[];
757
+ allOf?: SchemaObject[];
758
+ not?: SchemaObject;
759
+ discriminator?: {
760
+ propertyName: string;
761
+ mapping?: Record<string, string>;
762
+ };
763
+ [extension: `x-${string}`]: unknown;
764
+ };
765
+ type ParameterLocation = "path" | "query" | "header" | "cookie";
766
+ type ParameterObject = {
767
+ name: string;
768
+ in: ParameterLocation;
769
+ description?: string;
770
+ required?: boolean;
771
+ deprecated?: boolean;
772
+ style?: string;
773
+ explode?: boolean;
774
+ schema?: SchemaObject;
775
+ content?: Record<string, MediaTypeObject>;
776
+ example?: JsonValue;
777
+ [extension: `x-${string}`]: unknown;
778
+ };
779
+ type MediaTypeObject = {
780
+ schema?: SchemaObject;
781
+ example?: JsonValue;
782
+ examples?: Record<string, unknown>;
783
+ encoding?: Record<string, unknown>;
784
+ [extension: `x-${string}`]: unknown;
785
+ };
786
+ type RequestBodyObject = {
787
+ description?: string;
788
+ required?: boolean;
789
+ content: Record<string, MediaTypeObject>;
790
+ [extension: `x-${string}`]: unknown;
791
+ };
792
+ type HeaderObject = {
793
+ description?: string;
794
+ required?: boolean;
795
+ schema?: SchemaObject;
796
+ [extension: `x-${string}`]: unknown;
797
+ };
798
+ type ResponseObject = {
799
+ description: string;
800
+ headers?: Record<string, HeaderObject | ReferenceObject>;
801
+ content?: Record<string, MediaTypeObject>;
802
+ [extension: `x-${string}`]: unknown;
803
+ };
804
+ type ResponsesObject = Record<string, ResponseObject | ReferenceObject>;
805
+ type SecurityRequirementObject = Record<string, string[]>;
806
+ type OperationObject = {
807
+ operationId?: string;
808
+ summary?: string;
809
+ description?: string;
810
+ tags?: string[];
811
+ deprecated?: boolean;
812
+ parameters?: Array<ParameterObject | ReferenceObject>;
813
+ requestBody?: RequestBodyObject | ReferenceObject;
814
+ responses: ResponsesObject;
815
+ security?: SecurityRequirementObject[];
816
+ [extension: `x-${string}`]: unknown;
817
+ };
818
+ declare const HTTP_METHODS: readonly ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
819
+ type HttpMethod = (typeof HTTP_METHODS)[number];
820
+ type PathItemObject = {
821
+ summary?: string;
822
+ description?: string;
823
+ parameters?: Array<ParameterObject | ReferenceObject>;
824
+ [extension: `x-${string}`]: unknown;
825
+ } & Partial<Record<HttpMethod, OperationObject>>;
826
+ type SecuritySchemeObject = {
827
+ type: "apiKey" | "http" | "oauth2" | "openIdConnect" | "mutualTLS";
828
+ description?: string;
829
+ name?: string;
830
+ in?: ParameterLocation;
831
+ scheme?: string;
832
+ bearerFormat?: string;
833
+ flows?: Record<string, unknown>;
834
+ openIdConnectUrl?: string;
835
+ [extension: `x-${string}`]: unknown;
836
+ };
837
+ type ComponentsObject = {
838
+ schemas?: Record<string, SchemaObject>;
839
+ responses?: Record<string, ResponseObject>;
840
+ parameters?: Record<string, ParameterObject>;
841
+ requestBodies?: Record<string, RequestBodyObject>;
842
+ headers?: Record<string, HeaderObject>;
843
+ securitySchemes?: Record<string, SecuritySchemeObject>;
844
+ [extension: `x-${string}`]: unknown;
845
+ };
846
+ type ServerObject = {
847
+ url: string;
848
+ description?: string;
849
+ variables?: Record<string, unknown>;
850
+ [extension: `x-${string}`]: unknown;
851
+ };
852
+ type InfoObject = {
853
+ title: string;
854
+ version: string;
855
+ description?: string;
856
+ [extension: `x-${string}`]: unknown;
857
+ };
858
+ type OpenAPIDocument = {
859
+ openapi: string;
860
+ info: InfoObject;
861
+ servers?: ServerObject[];
862
+ paths: Record<string, PathItemObject>;
863
+ components?: ComponentsObject;
864
+ security?: SecurityRequirementObject[];
865
+ tags?: Array<{
866
+ name: string;
867
+ description?: string;
868
+ }>;
869
+ [extension: `x-${string}`]: unknown;
870
+ };
871
+
872
+ declare const document: OpenAPIDocument;
873
+ type OperationId = "get_teams_users_v2_user_get" | "create_user_v2_user_post" | "get_user_v2_user__user_id__get" | "delete_user_v2_user__user_id__delete" | "patch_user_v2_user__user_id__patch" | "get_user_by_client_user_id_v2_user_resolve__client_user_id__get" | "patch_user_info_v2_user__user_id__info_patch" | "get_latest_user_info_user_v2_user__user_id__info_latest_get" | "get_paginated_lab_tests_for_team_v3_lab_test_get" | "get_team_lab_accounts_v3_lab_test_lab_account_get" | "get_lab_test_for_team_v3_lab_tests__lab_test_id__get" | "get_labs_v3_lab_tests_labs_get" | "get_markers_for_lab_test_v3_lab_tests__lab_test_id__markers_get" | "list_order_set_markers_v3_lab_tests_list_order_set_markers_post" | "create_order_v3_order_post" | "get_area_info_v3_order_area_info_get" | "get_psc_info_v3_order_psc_info_get" | "get_phlebotomy_appointment_availability_v3_order_phlebotomy_appointment_availability_post" | "get_phlebotomy_appointment_cancellation_reason_v3_order_phlebotomy_appointment_cancellation_reasons_get" | "get_psc_appointment_availability_v3_order_psc_appointment_availability_post" | "get_psc_appointment_cancellation_reason_v3_order_psc_appointment_cancellation_reasons_get" | "get_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_get" | "book_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_book_post" | "reschedule_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_reschedule_patch" | "cancel_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_cancel_patch" | "get_psc_appointment_v3_order__order_id__psc_appointment_get" | "book_psc_appointment_v3_order__order_id__psc_appointment_book_post" | "reschedule_psc_appointment_v3_order__order_id__psc_appointment_reschedule_patch" | "cancel_psc_appointment_v3_order__order_id__psc_appointment_cancel_patch" | "get_order_v3_order__order_id__get" | "cancel_order_v3_order__order_id__cancel_post" | "simulate_order_v3_order__order_id__test_post" | "get_result_raw_v3_order__order_id__result_get" | "get_result_metadata_v3_order__order_id__result_metadata_get" | "get_result_pdf_v3_order__order_id__result_pdf_get" | "get_order_requisition_pdf_v3_order__order_id__requisition_pdf_get" | "get_orders_v3_orders_get" | "get_order_transaction_v3_order_transaction__transaction_id__get" | "get_order_transaction_result_v3_order_transaction__transaction_id__result_get";
874
+ type SupportedOperationId = "get_teams_users_v2_user_get" | "create_user_v2_user_post" | "get_user_v2_user__user_id__get" | "delete_user_v2_user__user_id__delete" | "patch_user_v2_user__user_id__patch" | "get_user_by_client_user_id_v2_user_resolve__client_user_id__get" | "patch_user_info_v2_user__user_id__info_patch" | "get_latest_user_info_user_v2_user__user_id__info_latest_get" | "get_paginated_lab_tests_for_team_v3_lab_test_get" | "get_team_lab_accounts_v3_lab_test_lab_account_get" | "get_lab_test_for_team_v3_lab_tests__lab_test_id__get" | "get_labs_v3_lab_tests_labs_get" | "get_markers_for_lab_test_v3_lab_tests__lab_test_id__markers_get" | "list_order_set_markers_v3_lab_tests_list_order_set_markers_post" | "create_order_v3_order_post" | "get_area_info_v3_order_area_info_get" | "get_psc_info_v3_order_psc_info_get" | "get_phlebotomy_appointment_availability_v3_order_phlebotomy_appointment_availability_post" | "get_phlebotomy_appointment_cancellation_reason_v3_order_phlebotomy_appointment_cancellation_reasons_get" | "get_psc_appointment_availability_v3_order_psc_appointment_availability_post" | "get_psc_appointment_cancellation_reason_v3_order_psc_appointment_cancellation_reasons_get" | "get_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_get" | "book_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_book_post" | "reschedule_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_reschedule_patch" | "cancel_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_cancel_patch" | "get_psc_appointment_v3_order__order_id__psc_appointment_get" | "book_psc_appointment_v3_order__order_id__psc_appointment_book_post" | "reschedule_psc_appointment_v3_order__order_id__psc_appointment_reschedule_patch" | "cancel_psc_appointment_v3_order__order_id__psc_appointment_cancel_patch" | "get_order_v3_order__order_id__get" | "cancel_order_v3_order__order_id__cancel_post" | "simulate_order_v3_order__order_id__test_post" | "get_result_raw_v3_order__order_id__result_get" | "get_result_metadata_v3_order__order_id__result_metadata_get" | "get_result_pdf_v3_order__order_id__result_pdf_get" | "get_order_requisition_pdf_v3_order__order_id__requisition_pdf_get" | "get_orders_v3_orders_get" | "get_order_transaction_v3_order_transaction__transaction_id__get" | "get_order_transaction_result_v3_order_transaction__transaction_id__result_get";
875
+ declare const operationIds: readonly ["get_teams_users_v2_user_get", "create_user_v2_user_post", "get_user_v2_user__user_id__get", "delete_user_v2_user__user_id__delete", "patch_user_v2_user__user_id__patch", "get_user_by_client_user_id_v2_user_resolve__client_user_id__get", "patch_user_info_v2_user__user_id__info_patch", "get_latest_user_info_user_v2_user__user_id__info_latest_get", "get_paginated_lab_tests_for_team_v3_lab_test_get", "get_team_lab_accounts_v3_lab_test_lab_account_get", "get_lab_test_for_team_v3_lab_tests__lab_test_id__get", "get_labs_v3_lab_tests_labs_get", "get_markers_for_lab_test_v3_lab_tests__lab_test_id__markers_get", "list_order_set_markers_v3_lab_tests_list_order_set_markers_post", "create_order_v3_order_post", "get_area_info_v3_order_area_info_get", "get_psc_info_v3_order_psc_info_get", "get_phlebotomy_appointment_availability_v3_order_phlebotomy_appointment_availability_post", "get_phlebotomy_appointment_cancellation_reason_v3_order_phlebotomy_appointment_cancellation_reasons_get", "get_psc_appointment_availability_v3_order_psc_appointment_availability_post", "get_psc_appointment_cancellation_reason_v3_order_psc_appointment_cancellation_reasons_get", "get_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_get", "book_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_book_post", "reschedule_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_reschedule_patch", "cancel_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_cancel_patch", "get_psc_appointment_v3_order__order_id__psc_appointment_get", "book_psc_appointment_v3_order__order_id__psc_appointment_book_post", "reschedule_psc_appointment_v3_order__order_id__psc_appointment_reschedule_patch", "cancel_psc_appointment_v3_order__order_id__psc_appointment_cancel_patch", "get_order_v3_order__order_id__get", "cancel_order_v3_order__order_id__cancel_post", "simulate_order_v3_order__order_id__test_post", "get_result_raw_v3_order__order_id__result_get", "get_result_metadata_v3_order__order_id__result_metadata_get", "get_result_pdf_v3_order__order_id__result_pdf_get", "get_order_requisition_pdf_v3_order__order_id__requisition_pdf_get", "get_orders_v3_orders_get", "get_order_transaction_v3_order_transaction__transaction_id__get", "get_order_transaction_result_v3_order_transaction__transaction_id__result_get"];
876
+ declare const supportedOperationIds: readonly ["get_teams_users_v2_user_get", "create_user_v2_user_post", "get_user_v2_user__user_id__get", "delete_user_v2_user__user_id__delete", "patch_user_v2_user__user_id__patch", "get_user_by_client_user_id_v2_user_resolve__client_user_id__get", "patch_user_info_v2_user__user_id__info_patch", "get_latest_user_info_user_v2_user__user_id__info_latest_get", "get_paginated_lab_tests_for_team_v3_lab_test_get", "get_team_lab_accounts_v3_lab_test_lab_account_get", "get_lab_test_for_team_v3_lab_tests__lab_test_id__get", "get_labs_v3_lab_tests_labs_get", "get_markers_for_lab_test_v3_lab_tests__lab_test_id__markers_get", "list_order_set_markers_v3_lab_tests_list_order_set_markers_post", "create_order_v3_order_post", "get_area_info_v3_order_area_info_get", "get_psc_info_v3_order_psc_info_get", "get_phlebotomy_appointment_availability_v3_order_phlebotomy_appointment_availability_post", "get_phlebotomy_appointment_cancellation_reason_v3_order_phlebotomy_appointment_cancellation_reasons_get", "get_psc_appointment_availability_v3_order_psc_appointment_availability_post", "get_psc_appointment_cancellation_reason_v3_order_psc_appointment_cancellation_reasons_get", "get_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_get", "book_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_book_post", "reschedule_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_reschedule_patch", "cancel_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_cancel_patch", "get_psc_appointment_v3_order__order_id__psc_appointment_get", "book_psc_appointment_v3_order__order_id__psc_appointment_book_post", "reschedule_psc_appointment_v3_order__order_id__psc_appointment_reschedule_patch", "cancel_psc_appointment_v3_order__order_id__psc_appointment_cancel_patch", "get_order_v3_order__order_id__get", "cancel_order_v3_order__order_id__cancel_post", "simulate_order_v3_order__order_id__test_post", "get_result_raw_v3_order__order_id__result_get", "get_result_metadata_v3_order__order_id__result_metadata_get", "get_result_pdf_v3_order__order_id__result_pdf_get", "get_order_requisition_pdf_v3_order__order_id__requisition_pdf_get", "get_orders_v3_orders_get", "get_order_transaction_v3_order_transaction__transaction_id__get", "get_order_transaction_result_v3_order_transaction__transaction_id__result_get"];
877
+
878
+ type SeedCacheEntry = {
879
+ status: number;
880
+ headers: Record<string, string>;
881
+ body: unknown;
882
+ };
883
+
884
+ type PrefetchTarget = {
885
+ baseUrl: string;
886
+ fetch: (request: Request) => Promise<Response>;
887
+ headers: () => Promise<Record<string, string>> | Record<string, string>;
888
+ };
889
+ /**
890
+ * Seal coverage-corpus routing ZIPs + availability POSTs into the seed observation cache.
891
+ * Call after warmup, before `seedFrom`, so compare walks can forceInclude geo/scheduling reads.
892
+ */
893
+ declare const prefetchCoverageObservations: (args: {
894
+ real: PrefetchTarget;
895
+ getCache: Map<string, SeedCacheEntry>;
896
+ zips?: readonly string[];
897
+ /** ZIPs that also get PSC availability POST seals. Defaults to scheduling corpus. */
898
+ schedulingZips?: readonly string[];
899
+ /** ZIPs for phlebotomy availability (narrow served set). Defaults to PHLEBOTOMY_ZIPS. */
900
+ phlebotomyZips?: readonly string[];
901
+ labIds?: readonly number[];
902
+ /** Also prefetch phlebotomy + PSC availability. Default true. */
903
+ includeAvailability?: boolean;
904
+ sleep?: (ms: number) => Promise<void>;
905
+ minIntervalMs?: number;
906
+ }) => Promise<void>;
907
+
908
+ type Violation = "wrong-type" | "string-too-long" | "string-too-short" | "number-too-large" | "number-too-small" | "not-in-enum" | "missing-required" | "unexpected-property" | "array-too-long" | "array-too-short" | "bad-format";
909
+ type Mutation = {
910
+ /** Location of the mutated value in generated *values* (not schema keywords). */
911
+ valuePath: Array<string | number>;
912
+ violation: Violation;
913
+ detail: string;
914
+ };
915
+
916
+ /**
917
+ * One generated API call. Concrete ids never appear here: resource references are
918
+ * {@link Placeholder}s resolved per side at execution time, which is what lets the same command
919
+ * run against the real API and the mock.
920
+ */
921
+ type LogicalCommand = {
922
+ operationId: string;
923
+ /** Path, query and header parameter values by name. */
924
+ parameters: Record<string, unknown>;
925
+ body: unknown;
926
+ mediaType: string | undefined;
927
+ /** Set when the body was deliberately mutated to violate its schema. */
928
+ invalid: Mutation | undefined;
929
+ };
930
+
931
+ /** Mutable walk state consulted by {@link DynamicWeightFn}. */
932
+ type ExploreState = {
933
+ /** operationId → times exercised in this walk. */
934
+ coverage: Record<string, number>;
935
+ /** Recent operationIds (oldest → newest), typically capped. */
936
+ history: readonly string[];
937
+ /** Active resource counts by type (user, order, …). */
938
+ resourceCounts: Record<string, number>;
939
+ phase: "warmup" | "compare" | "walk";
940
+ step: number;
941
+ maxSteps: number;
942
+ /** Optional observed GET cache size — used to unlock geo/scheduling reads. */
943
+ observationCacheSize?: number;
944
+ };
945
+ /** Mulberry32 PRNG — deterministic from a 32-bit seed. */
946
+ declare const createExploreRng: (seed: number) => {
947
+ /** Uniform float in [0, 1). */
948
+ next: () => number;
949
+ /** Uniform int in [0, max] inclusive. */
950
+ nextInt: (max: number) => number;
951
+ };
952
+ type ExploreRng = ReturnType<typeof createExploreRng>;
953
+
954
+ /** Stable address fields so availability observation-cache keys match prefetch (85004). */
955
+ declare const AVAILABILITY_ADDRESS: {
956
+ readonly first_line: "1 N Central Ave";
957
+ readonly second_line: string | null;
958
+ readonly city: "Phoenix";
959
+ readonly state: "AZ";
960
+ readonly unit: string | null;
961
+ };
962
+ /** Far-future start date so slots are always generated relative to frozen test clocks. */
963
+ declare const AVAILABILITY_START_DATE = "2099-06-15";
964
+ /**
965
+ * Pin geo / availability params onto the coverage corpus so observation-cache hits stay
966
+ * sealed during seedParity compare walks.
967
+ */
968
+ declare const reshapeCoverageGeoCommand: (command: LogicalCommand, _state: ExploreState, rng: ExploreRng, options?: {
969
+ zips?: readonly string[];
970
+ schedulingZips?: readonly string[];
971
+ phlebotomyZips?: readonly string[];
972
+ labIds?: readonly number[];
973
+ }) => LogicalCommand;
974
+
975
+ type JunctionRuntimeOptions = {
976
+ /**
977
+ * Recorded vendor data every namespace starts from. The served mock loads the corpus
978
+ * shipped at `@crvouga/mockingbird-service-junction/corpus` unless told otherwise.
979
+ */
980
+ corpus?: SealedCorpus;
981
+ /** Default: `corpus` when a corpus is loaded, otherwise `synthetic`. */
982
+ geo?: GeoMode;
983
+ /** The team's lab accounts; see {@link JunctionAPIOptions.labAccounts}. */
984
+ labAccounts?: readonly LabAccountInput[];
985
+ /** Deliver signed webhooks here, with Svix's retry schedule. */
986
+ webhooks?: WebhookEndpoint;
987
+ /** Called in-process for every webhook event, delivered or not. */
988
+ onWebhook?: (event: Parameters<WebhookPublisher>[0], namespace: string) => void;
989
+ sqlite?: SqliteClient;
990
+ clock?: Clock;
991
+ /** Seeds fault rates and webhook retry jitter. */
992
+ seed?: number | string;
993
+ /** Require `x-mockingbird-admin-key` on `/__admin/*`. */
994
+ adminKey?: string;
995
+ onLog?: (entry: RequestLog) => void;
996
+ };
997
+ type JunctionRuntime = ServiceRuntime<JunctionAPI> & {
998
+ /** Signed delivery, when `webhooks` was configured. */
999
+ readonly webhooks: WebhookDispatcher | undefined;
1000
+ };
1001
+ /**
1002
+ * The Junction mock with Mockingbird's full service contract: unauthenticated
1003
+ * `/health`, the `/__admin/*` control plane, per-request namespaces
1004
+ * (`x-mockingbird-namespace`), clock control, fault injection and request metrics.
1005
+ * Runtime-neutral: serve it with any Fetch-native server, or use `./server` for Node.
1006
+ */
1007
+ declare const createRuntime: (options?: JunctionRuntimeOptions) => JunctionRuntime;
1008
+
1009
+ /**
1010
+ * Appointment scheduling surface: phlebotomy + PSC availability/booking lifecycle,
1011
+ * cancellation reasons, area serviceability, and PSC site info.
1012
+ *
1013
+ * Slot data is generated deterministically (seeded by zip/site/date), so walks are
1014
+ * reproducible without any provider-side state.
1015
+ */
1016
+
1017
+ /** Status of the unknown-ZIP error: unused by Junction, and not retried by HTTP clients. */
1018
+ declare const UNKNOWN_ZIP_STATUS = 424;
1019
+ declare const UNKNOWN_ZIP_ERROR_TYPE = "MOCKINGBIRD_UNKNOWN_ZIP";
1020
+
1021
+ /**
1022
+ * Differential verification: the same requests against real Junction and the mock,
1023
+ * with every divergence reported. Two checks:
1024
+ *
1025
+ * - drift: each recorded corpus observation re-fetched from the real API. A stale
1026
+ * entry means the corpus no longer matches production and should be re-pulled.
1027
+ * - scenario: a fixed script of stateful calls (users, catalog, serviceability, and
1028
+ * optionally orders) run against both sides, comparing status codes, response
1029
+ * shapes, and — for the documented error bodies — exact bytes.
1030
+ *
1031
+ * Only a sandbox team should be used: the scenario creates, and always deletes, one
1032
+ * user (and, with `orders`, places and cancels one order).
1033
+ */
1034
+ type VerifyOptions = {
1035
+ realKey: string;
1036
+ realUrl?: string;
1037
+ /** The mock under test, e.g. `createRuntime({ corpus })`. */
1038
+ mock: {
1039
+ fetch(request: Request): Promise<Response>;
1040
+ };
1041
+ corpus: SealedCorpus;
1042
+ /** Re-fetch at most this many observations (spread evenly). Default: all of them. */
1043
+ sample?: number;
1044
+ /** Skip the drift check. */
1045
+ skipDrift?: boolean;
1046
+ /** Also place, read and cancel an order on the real team. */
1047
+ orders?: boolean;
1048
+ fetch?: (request: Request) => Promise<Response>;
1049
+ minIntervalMs?: number;
1050
+ onProgress?: (message: string) => void;
1051
+ };
1052
+ type Divergence = {
1053
+ check: string;
1054
+ kind: "status" | "shape" | "body" | "error";
1055
+ /** JSON path of the first difference, when there is one. */
1056
+ at?: string;
1057
+ real: unknown;
1058
+ mock: unknown;
1059
+ };
1060
+ type VerifyReport = {
1061
+ ok: boolean;
1062
+ drift: {
1063
+ checked: number;
1064
+ stale: number;
1065
+ };
1066
+ scenario: {
1067
+ steps: number;
1068
+ divergent: number;
1069
+ };
1070
+ divergences: Divergence[];
1071
+ };
1072
+ declare const verifyAgainstReal: (options: VerifyOptions) => Promise<VerifyReport>;
1073
+
1074
+ declare const JUNCTION_NAMESPACE = "junction";
18
1075
  /**
19
1076
  * Stateful mock of the Junction (Vital) API user and lab-testing surfaces.
20
1077
  *
21
1078
  * Docs: https://docs.junction.com/
22
1079
  * Auth: `x-vital-api-key` — https://docs.junction.com/api-details/junction-api
23
1080
  */
24
- export type JunctionAPIOptions = APIOptions & {
1081
+ type JunctionAPIOptions = APIOptions & {
25
1082
  onWebhook?: WebhookPublisher;
26
1083
  webhook?: JunctionWebhookOptions;
1084
+ /** Install a recorded corpus at construction; it is re-applied on every `reset()`. */
1085
+ corpus?: SealedCorpus;
1086
+ /**
1087
+ * How serviceability reads answer a ZIP the corpus does not cover. Default: `corpus`
1088
+ * when a corpus is given (refuse, loudly), otherwise `synthetic` (invent coverage).
1089
+ */
1090
+ geo?: GeoMode;
1091
+ /**
1092
+ * The team's lab accounts, replacing the built-in fixtures (and any from the corpus).
1093
+ * `create_order` routes, accepts and rejects `lab_account_id` against these.
1094
+ */
1095
+ labAccounts?: readonly LabAccountInput[];
27
1096
  };
28
- export declare class JunctionAPI implements FetchAPI {
1097
+ /** What a loaded corpus covers, for `/health` and startup logs. */
1098
+ type CorpusInfo = {
1099
+ label: string;
1100
+ recordedAt: string;
1101
+ source: string;
1102
+ observations: number;
1103
+ labTests: number;
1104
+ labAccounts: number;
1105
+ zips: number;
1106
+ };
1107
+ /** A short, stable name for a corpus: its recording date plus a digest of what it holds. */
1108
+ declare const corpusLabel: (corpus: SealedCorpus) => string;
1109
+ declare class JunctionAPI implements FetchAPI {
29
1110
  readonly app: Hono;
30
1111
  readonly sqlite: SqliteClient;
31
1112
  private readonly service;
32
1113
  private readonly state;
33
1114
  private corpus;
1115
+ private labAccountConfig;
34
1116
  constructor(options?: JunctionAPIOptions);
35
1117
  fetch(request: Request): Promise<Response>;
36
1118
  reset(): Promise<void>;
@@ -39,11 +1121,39 @@ export declare class JunctionAPI implements FetchAPI {
39
1121
  * labs, and lab accounts with the recording's exact values. Re-applied on `reset()`.
40
1122
  */
41
1123
  installCorpus(corpus: SealedCorpus): void;
1124
+ /**
1125
+ * Which lab accounts the ordering rules see, in order of precedence: the configured
1126
+ * ones, then a corpus's real accounts, then the built-in fixtures (which a corpus
1127
+ * recorded from a team with no accounts leaves in place).
1128
+ */
1129
+ private seedLabAccounts;
1130
+ /** Replace the team's lab accounts; `undefined` restores the default. Kept across resets. */
1131
+ configureLabAccounts(accounts: readonly LabAccountInput[] | undefined): StoredLabAccount[];
1132
+ labAccounts(): StoredLabAccount[];
1133
+ corpusInfo(): CorpusInfo | undefined;
1134
+ get geoMode(): GeoMode;
1135
+ set geoMode(mode: GeoMode);
1136
+ order(id: string): OrderRecord | undefined;
1137
+ orders(): OrderRecord[];
1138
+ /**
1139
+ * Put an order straight into a status (a full `<status>.<method>.<event>` value) and
1140
+ * publish the webhook a real transition would. `flags` takes the same keys as
1141
+ * `simulate_order`'s body: `interpretation`, `result_types`, `has_missing_results`.
1142
+ */
1143
+ transitionOrder(id: string, status: string, options: {
1144
+ now: () => number;
1145
+ flags?: Record<string, unknown> | null;
1146
+ }): OrderRecord | undefined;
1147
+ /** Serve `fixture` as this order's results, replacing the generated ones. */
1148
+ installResultFixture(orderId: string, fixture: ResultFixture): boolean;
1149
+ resultFixture(orderId: string): ResultFixture | undefined;
42
1150
  seedFrom(source: SeedSource, observations?: SeedObservations): Promise<SeedReport>;
43
1151
  ensureLabTests(source: SeedSource, ids: readonly string[]): Promise<number>;
44
1152
  ensureOrders(source: SeedSource, ids: readonly string[]): Promise<number>;
45
1153
  markUserDeleted(userId: string): void;
46
1154
  webhookEvents(): JunctionWebhookEvent[];
47
- webhookDeliveryAttempts(): import("./state.js").WebhookDeliveryAttempt[];
1155
+ webhookDeliveryAttempts(): WebhookDeliveryAttempt[];
48
1156
  }
49
- //# sourceMappingURL=index.d.ts.map
1157
+
1158
+ export { AVAILABILITY_ADDRESS, AVAILABILITY_START_DATE, COVERAGE_ZIPS, DEFAULT_JUNCTION_BASE_URL, FAULT_PRESETS, JUNCTION_NAMESPACE, JunctionAPI, ORDER_STATUSES_BY_METHOD, PHLEBOTOMY_AVAILABILITY_ZIPS, PSC_AVAILABILITY_ZIPS, PSC_LAB_IDS, RESULT_FIXTURES, SANDBOX_KEY_PREFIXES, SEALED_CORPUS_VERSION, TEAM_LAB_ACCOUNTS, TRANSITION_ALIASES, UNKNOWN_ZIP_ERROR_TYPE, UNKNOWN_ZIP_STATUS, US_STATES, corpusLabel, createRuntime, createWebhookDispatcher, diffCorpus, document, fingerprintCorpus, isSandboxKey, labAccountFromInput, observationCacheKey, operationIds, parseSealedCorpus, prefetchCoverageObservations, pullCorpus, reshapeCoverageGeoCommand, resolveTransition, signSvix, supportedOperationIds, verifyAgainstReal, verifySvix };
1159
+ export type { CorpusDiff, CorpusInfo, Divergence, FetchAPI, GeoMode, GetCacheEntry, JunctionAPIOptions, JunctionAdminOptions, JunctionRuntime, JunctionRuntimeOptions, JunctionWebhookEvent, JunctionWebhookOptions, LabAccountDelegatedFlow, LabAccountInput, LabAccountRecord, LabAccountStatus, OperationId, OrderRecord, PullCorpusOptions, ResultFixture, SealedCorpus, SeedObservations, SeedReport, SeedSource, SetDiff, SqliteClient, StoredLabAccount, SupportedOperationId, VerifyOptions, VerifyReport, WebhookAttempt, WebhookDelivery, WebhookDispatcher, WebhookEndpoint, WebhookPublisher };