@crvouga/mockingbird-service-junction 0.1.2 → 0.3.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/dist/index.d.ts CHANGED
@@ -1,5 +1,35 @@
1
1
  import { Hono } from 'hono';
2
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
+
3
33
  /** Bind values accepted by Mockingbird's SQLite port (matches sqlite-mem / better-sqlite3). */
4
34
  type SqliteValue$1 = null | number | bigint | string | Uint8Array | boolean;
5
35
  /** Mutation counters returned by {@link SqliteStatement.run}. */
@@ -29,12 +59,210 @@ interface SqliteClient$1 {
29
59
  transaction<T>(fn: () => T): T;
30
60
  }
31
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
+ /** Resource ids the handler touched (`userId`, `orderId`, …), when the service reports them. */
151
+ ids?: Record<string, string>;
152
+ /** Set when the service created a resource the request referred to but that did not exist. */
153
+ adopted?: boolean;
154
+ };
155
+ type MetricsReport = {
156
+ requests: number;
157
+ /** Counts keyed `<operationId> <status>`. */
158
+ byOperation: Record<string, number>;
159
+ /**
160
+ * Paths that matched no operation, most frequent first.
161
+ *
162
+ * This is the early-warning signal: a consumer calling something the mock does
163
+ * not implement shows up here as a count, before it fails a suite as a 404.
164
+ */
165
+ unmatched: {
166
+ method: string;
167
+ path: string;
168
+ count: number;
169
+ }[];
170
+ faults: number;
171
+ totalDurationMs: number;
172
+ };
173
+ type Metrics = {
174
+ record(entry: RequestLog): void;
175
+ report(): MetricsReport;
176
+ reset(): void;
177
+ };
178
+
179
+ /** One journal entry: a request log stamped with when (on the mock clock) it was handled. */
180
+ type JournalEntry = RequestLog & {
181
+ at: string;
182
+ };
183
+ type JournalQuery = {
184
+ /** Only this namespace. Omit for every namespace, oldest first across all of them. */
185
+ namespace?: string;
186
+ operationId?: string;
187
+ status?: number;
188
+ /** Only entries at or after this instant (epoch ms). */
189
+ since?: number;
190
+ /** At most this many, the most recent kept. */
191
+ limit?: number;
192
+ };
193
+ type Journal = {
194
+ readonly size: number;
195
+ record(entry: JournalEntry): void;
196
+ list(query?: JournalQuery): JournalEntry[];
197
+ /** Forget one namespace's entries, or every namespace's. */
198
+ clear(namespace?: string): void;
199
+ };
200
+
201
+ /**
202
+ * A point-in-time copy of everything a service namespace holds.
203
+ *
204
+ * All service state lives in the two core tables keyed by namespace, so a snapshot
205
+ * is generic: any service gets per-test rollback without knowing its own schema.
206
+ * Restoring is much cheaper than rebuilding a namespace from a corpus.
207
+ */
208
+ type NamespaceSnapshot = {
209
+ namespace: string;
210
+ records: {
211
+ collection: string;
212
+ id: string;
213
+ seq: number;
214
+ value: string;
215
+ }[];
216
+ sequences: {
217
+ name: string;
218
+ kind: string;
219
+ value: number;
220
+ }[];
221
+ };
222
+
223
+ /**
224
+ * Anything that can answer a Fetch `Request` with a `Response`.
225
+ *
226
+ * Every Mockingbird service implements this, and every runtime adapter consumes it.
227
+ * It is the only contract shared across the whole graph.
228
+ */
229
+ interface FetchAPI$1 {
230
+ fetch(request: Request): Promise<Response>;
231
+ }
232
+
233
+ /** What the runtime needs from a service: a Fetch handler it can reset. */
234
+ type ServiceInstance = FetchAPI$1 & {
235
+ reset(): Promise<void>;
236
+ };
237
+ type ServiceRuntime<T extends ServiceInstance> = FetchAPI$1 & {
238
+ readonly name: string;
239
+ readonly sqlite: SqliteClient$1;
240
+ readonly clock: Clock;
241
+ readonly faults: FaultRegistry;
242
+ readonly metrics: Metrics;
243
+ readonly journal: Journal;
244
+ readonly rng: Rng;
245
+ /** The instance behind `namespace` (the default one when omitted), created on first use. */
246
+ instance(namespace?: string): T;
247
+ /** Public names of every namespace created so far. */
248
+ namespaces(): string[];
249
+ /** Reset one namespace, or every namespace with `"*"`. */
250
+ reset(namespace?: string): Promise<void>;
251
+ snapshot(namespace?: string): NamespaceSnapshot;
252
+ restore(snapshot: NamespaceSnapshot, namespace?: string): void;
253
+ };
254
+
32
255
  /** Options every provider constructor accepts. */
33
256
  type APIOptions = {
34
257
  /** Sync SQLite client. Defaults to `@crvouga/mockingbird-service-sqlite`. */
35
258
  sqlite?: SqliteClient$1;
36
259
  /** Clock used for `created`-style fields. Default `Date.now`. */
37
260
  now?: () => number;
261
+ /**
262
+ * Storage namespace for this instance's records. Instances sharing one SQLite
263
+ * client stay isolated when their namespaces differ. Defaults to the service name.
264
+ */
265
+ namespace?: string;
38
266
  };
39
267
 
40
268
  /** Bind values accepted by Mockingbird's SQLite port (matches sqlite-mem / better-sqlite3). */
@@ -118,6 +346,95 @@ type LabTestRecord = {
118
346
  worst_case_tat_days: number | null;
119
347
  };
120
348
 
349
+ /**
350
+ * The test backdoor: users and orders put straight into a namespace, with ids a suite
351
+ * chooses — the Junction `user_id`s a database fixture already carries, or an order
352
+ * already in some status — plus the lenient identity mode that adopts unknown users.
353
+ *
354
+ * Everything inserted here goes through the records `create_user` and `create_order`
355
+ * build, so no read path can tell a fixture from an API-created resource.
356
+ */
357
+
358
+ /** How requests referring to a user the namespace does not hold are answered. */
359
+ type IdentityMode = "strict" | "adopt-users";
360
+ declare const IDENTITY_MODES: readonly IdentityMode[];
361
+ type UserFixture = {
362
+ /** Default: a fresh id, as `create_user` would mint. */
363
+ user_id?: string;
364
+ client_user_id: string;
365
+ /** ISO-8601. Default: the mock clock's now. */
366
+ created_on?: string;
367
+ /** An IANA zone, or the full `{ id, source_slug, updated_at }` object. */
368
+ fallback_time_zone?: string | UserRecord["fallback_time_zone"];
369
+ /** `YYYY-MM-DD`, or the full `{ value, source_slug, updated_at }` object. */
370
+ fallback_birth_date?: string | UserRecord["fallback_birth_date"];
371
+ ingestion_start?: string | null;
372
+ ingestion_end?: string | null;
373
+ };
374
+ type OrderFixture = {
375
+ /** Default: a fresh id, as `create_order` would mint. */
376
+ order_id?: string;
377
+ user_id: string;
378
+ lab_test_id: string;
379
+ lab_account_id?: string;
380
+ /** A transition target (`completed`, `at_lab`, or a full status). Default: just ordered. */
381
+ status?: string;
382
+ /** Default: the lab test's own method. */
383
+ collection_method?: string;
384
+ patient_details?: Record<string, unknown>;
385
+ patient_address?: Record<string, unknown>;
386
+ billing_type?: string;
387
+ icd_codes?: string[] | null;
388
+ passthrough?: string | null;
389
+ clinical_notes?: string | null;
390
+ /** ISO-8601. Default: the mock clock's now. */
391
+ created_at?: string;
392
+ /** Results served for the order, as `PUT /__admin/results/{id}` takes them. */
393
+ result_fixture?: Omit<ResultFixture, "name"> & {
394
+ name?: string;
395
+ };
396
+ };
397
+ type JunctionFixtures = {
398
+ users?: readonly UserFixture[];
399
+ orders?: readonly OrderFixture[];
400
+ };
401
+ type InsertOptions = {
402
+ /** Publish the webhooks the equivalent API calls would. Default `false`. */
403
+ emitWebhooks?: boolean;
404
+ };
405
+ /** A backdoor failure: `status` 400 for a bad fixture, 404 for a missing reference, 409 for a clash. */
406
+ declare class FixtureError extends Error {
407
+ readonly status: 400 | 404 | 409;
408
+ constructor(status: 400 | 404 | 409, message: string);
409
+ }
410
+
411
+ /** Key prefixes of sandbox team keys. `corpus pull` and `verify` refuse any other key. */
412
+ declare const SANDBOX_KEY_PREFIXES: readonly ["sk_us_", "sk_eu_"];
413
+ declare const isSandboxKey: (key: string) => boolean;
414
+ type JunctionLimits = {
415
+ /**
416
+ * Live users a team may hold; creating one more answers the sandbox's
417
+ * "maximum of 50 Sandbox users" 400. Deleted users free their slot. `null`: unlimited.
418
+ */
419
+ maxUsers: number | null;
420
+ /** Refuse `POST /v3/order/{id}/test` unless the API key is a sandbox key (`sk_us_…`/`sk_eu_…`). */
421
+ simulateRequiresSandbox: boolean;
422
+ /** Vendor requests per second per namespace before a 429. `null`: unlimited. */
423
+ rateLimitPerSecond: number | null;
424
+ };
425
+ type JunctionLimitsInput = {
426
+ [K in keyof JunctionLimits]?: JunctionLimits[K] | undefined;
427
+ };
428
+ /** Every limit off: what a default instance enforces. */
429
+ declare const DEFAULT_LIMITS: Readonly<JunctionLimits>;
430
+ /** The 400 the sandbox answers once a team holds its 50th user, byte for byte. */
431
+ declare const sandboxUserQuotaBody: (max: number) => {
432
+ detail: {
433
+ error_type: string;
434
+ error_message: string;
435
+ };
436
+ };
437
+
121
438
  type GetCacheEntry = {
122
439
  status: number;
123
440
  headers: Record<string, string>;
@@ -139,6 +456,125 @@ type WebhookDeliveryAttempt = {
139
456
  timeout_ms: number;
140
457
  acknowledged: boolean;
141
458
  };
459
+ type UserRecord = {
460
+ user_id: string;
461
+ team_id: string;
462
+ client_user_id: string;
463
+ created_on: string;
464
+ connected_sources: unknown[];
465
+ fallback_time_zone: {
466
+ id: string;
467
+ source_slug: string;
468
+ updated_at: string;
469
+ } | null;
470
+ fallback_birth_date: {
471
+ value: string;
472
+ source_slug: string;
473
+ updated_at: string;
474
+ } | null;
475
+ ingestion_start: string | null;
476
+ ingestion_end: string | null;
477
+ };
478
+ /** Fixed mock team id — volatile on the real side, stable in the mock. */
479
+ declare const MOCK_TEAM_ID = "11111111-1111-4111-8111-111111111111";
480
+ type OrderEventRecord = {
481
+ id: number;
482
+ created_at: string;
483
+ status: string;
484
+ status_detail: null;
485
+ };
486
+ type OrderTransactionEmbed = {
487
+ id: string;
488
+ status: "active" | "completed" | "cancelled";
489
+ orders: Array<{
490
+ id: string;
491
+ low_level_status: string;
492
+ low_level_status_created_at: string;
493
+ origin: string;
494
+ parent_id: null;
495
+ created_at: string;
496
+ updated_at: string;
497
+ }>;
498
+ };
499
+ type OrderRecord = {
500
+ id: string;
501
+ user_id: string;
502
+ team_id: string;
503
+ patient_details: Record<string, unknown>;
504
+ patient_address: Record<string, unknown>;
505
+ lab_test: LabTestRecord;
506
+ details: Record<string, unknown>;
507
+ sample_id: string | null;
508
+ notes: null;
509
+ clinical_notes: string | null;
510
+ passthrough: string | null;
511
+ /** Echoed from the request's opaque provider account id; absent when the order had none. */
512
+ lab_account_id?: string;
513
+ created_at: string;
514
+ updated_at: string;
515
+ events: OrderEventRecord[];
516
+ status: string;
517
+ last_event: OrderEventRecord;
518
+ physician: {
519
+ first_name: string;
520
+ last_name: string;
521
+ npi: string;
522
+ };
523
+ health_insurance_id: null;
524
+ requisition_form_url: string | null;
525
+ shipping_details: null;
526
+ has_abn: boolean;
527
+ billing_type: string;
528
+ priority: boolean;
529
+ activate_by: null;
530
+ icd_codes: string[] | null;
531
+ interpretation: string | null;
532
+ has_missing_results: boolean | null;
533
+ result_types: string[] | null;
534
+ expected_result_by_date: string | null;
535
+ worst_case_result_by_date: string | null;
536
+ origin: string;
537
+ order_transaction: OrderTransactionEmbed;
538
+ };
539
+ /**
540
+ * A lab account as the ordering rules read it. Structurally `LabAccountRecord` from
541
+ * lab-accounts.ts, restated here because that module imports this one.
542
+ */
543
+ type StoredLabAccount = {
544
+ id: string;
545
+ lab: string;
546
+ org_id: string | null;
547
+ status: "active" | "pending" | "suspended" | "ready_to_launch";
548
+ delegated_flow: "order_delegated" | "result_delegated" | "fully_delegated" | "not_delegated";
549
+ provider_account_id: string;
550
+ account_name: string | null;
551
+ default_clinical_notes: string | null;
552
+ business_units: string[] | null;
553
+ allowed_billing: Record<string, readonly string[]>;
554
+ team_id_allowlist: readonly string[];
555
+ };
556
+ /**
557
+ * A chosen result for one order, installed through the admin API. Replaces the
558
+ * generated biomarker lines, so a suite can assert on an exact payload — an abnormal
559
+ * panel, a critical value, a missing marker — instead of whatever the generator made.
560
+ */
561
+ type ResultFixture = {
562
+ /** The fixture's name, for logs and `GET /__admin/results`. */
563
+ name: string;
564
+ results?: unknown[];
565
+ missing_results?: unknown[] | null;
566
+ interpretation?: string;
567
+ /** Served from `GET /v3/order/{id}/result/pdf` instead of the generated PDF. */
568
+ pdf_base64?: string;
569
+ };
570
+ /**
571
+ * How serviceability reads answer a ZIP the loaded corpus has no record for.
572
+ *
573
+ * - `synthetic`: invent plausible coverage. Right for property tests that walk random ZIPs.
574
+ * - `corpus`: refuse with a documented Mockingbird error. Right for a mock standing in for
575
+ * the vendor, where invented coverage would silently disagree with production.
576
+ */
577
+ type GeoMode = "synthetic" | "corpus";
142
578
  type WebhookPublisher = (event: JunctionWebhookEvent) => void;
143
579
  type JunctionWebhookOptions = {
144
580
  seed?: number;
@@ -156,11 +592,105 @@ interface FetchAPI {
156
592
  fetch(request: Request): Promise<Response>;
157
593
  }
158
594
 
595
+ type LabAccountStatus = "active" | "pending" | "suspended" | "ready_to_launch";
596
+ type LabAccountDelegatedFlow = "order_delegated" | "result_delegated" | "fully_delegated" | "not_delegated";
597
+ type LabAccountRecord = {
598
+ id: string;
599
+ lab: string;
600
+ org_id: string | null;
601
+ status: LabAccountStatus;
602
+ delegated_flow: LabAccountDelegatedFlow;
603
+ provider_account_id: string;
604
+ account_name: string | null;
605
+ default_clinical_notes: string | null;
606
+ business_units: string[] | null;
607
+ allowed_billing: Record<string, readonly string[]>;
608
+ team_id_allowlist: readonly string[];
609
+ };
610
+ /** The 50 `USState` values from the docs, alphabetical by state name. */
611
+ declare const US_STATES: readonly string[];
612
+ /** A team the mock is not, for fixtures that model another team's account. */
613
+ declare const OTHER_TEAM_ID: string;
614
+ declare const TEAM_LAB_ACCOUNTS: readonly LabAccountRecord[];
615
+ /**
616
+ * Whether `teamId` may use an account. An empty `team_id_allowlist` counts as linked:
617
+ * a real team's listing returns such accounts (observed — Junction's own Quest, Labcorp
618
+ * and BioReference accounts carry `[]`), so the mock treats them as the team's. Whether
619
+ * ordering through one by explicit id behaves the same is unverified; SUPPORT.md tracks it.
620
+ */
621
+ declare const isLinkedToTeam: (entry: LabAccountRecord, teamId: string) => boolean;
622
+ /**
623
+ * A lab account as a consumer configures it. Mirrors `ClientFacingLabAccount`, with
624
+ * every field but `id` and `lab` optional, plus a `states` shorthand for the common
625
+ * case of an account that client-bills in a fixed set of states.
626
+ */
627
+ type LabAccountInput = {
628
+ id: string;
629
+ /** Lab slug, e.g. `"quest"`, `"labcorp"`, `"bioreference"`. */
630
+ lab: string;
631
+ status?: LabAccountStatus;
632
+ delegated_flow?: LabAccountDelegatedFlow;
633
+ account_name?: string | null;
634
+ provider_account_id?: string;
635
+ org_id?: string | null;
636
+ business_units?: string[] | null;
637
+ default_clinical_notes?: string | null;
638
+ /** Billing type → states it is allowed in. Takes precedence over `states`. */
639
+ allowed_billing?: Record<string, readonly string[]>;
640
+ /** Shorthand for `allowed_billing: { client_bill: states }`. Default: every state. */
641
+ states?: readonly string[];
642
+ /** Teams the account is linked to. Default: the configured team, so it is selectable. */
643
+ team_id_allowlist?: readonly string[];
644
+ };
645
+ /**
646
+ * Normalize a consumer's configuration into the record the ordering rules read.
647
+ * `teamId` is the default allowlist, so an account is selectable unless told otherwise.
648
+ */
649
+ declare const labAccountFromInput: (input: LabAccountInput, teamId?: string) => LabAccountRecord;
650
+
651
+ /**
652
+ * Named lab accounts a suite can add in one call, modelled on a real team's recorded
653
+ * listing (a BioReference-linked team, recorded 2026-09-21 from the Junction sandbox).
654
+ *
655
+ * Where a preset copies a recorded account, its states and billing types are the
656
+ * recording's. `bioreference_ny_nj_delegated` is not a recorded account: it is the
657
+ * NY/NJ client-bill shape a consumer asked for, kept because suites depend on it.
658
+ */
659
+
660
+ /** Junction's own Quest, Labcorp and BioReference accounts client-bill in these 47 states. */
661
+ declare const PLATFORM_ACCOUNT_STATES: readonly string[];
662
+ /** The recorded order-delegated BioReference account: every state but New York and New Jersey. */
663
+ declare const DELEGATED_ACCOUNT_STATES: readonly string[];
664
+ type Preset = Omit<LabAccountInput, "id">;
665
+ /**
666
+ * Every preset, without its id. A preset with no `team_id_allowlist` is linked to the
667
+ * configured team; the platform presets carry `[]`, as Junction's own accounts do.
668
+ */
669
+ declare const LAB_ACCOUNT_PRESETS: Readonly<Record<string, Preset>>;
670
+ /** A preset's id when none is given: stable across runs and namespaces. */
671
+ declare const presetAccountId: (name: string) => string;
672
+ /**
673
+ * A lab-account layout as a file or option declares it: a bare list of accounts, or
674
+ * presets (by name, or `{ name, id }`) plus accounts, added in that order.
675
+ */
676
+ type LabAccountLayout = readonly LabAccountInput[] | {
677
+ presets?: readonly (string | {
678
+ name: string;
679
+ id?: string;
680
+ })[];
681
+ accounts?: readonly LabAccountInput[];
682
+ };
683
+
159
684
  /**
160
685
  * Version of the sealed-corpus on-disk format. Bumped whenever the shape changes so a
161
686
  * stale recording is rejected instead of half-applied.
687
+ *
688
+ * - 1: observations, catalog and lab accounts.
689
+ * - 2: adds `teamId`, the recording team, so lab-account allowlists load verbatim.
162
690
  */
163
- declare const SEALED_CORPUS_VERSION = 1;
691
+ declare const SEALED_CORPUS_VERSION = 2;
692
+ /** Versions `parseSealedCorpus` still loads. A version-1 corpus behaves as it did in 0.2.0. */
693
+ declare const SUPPORTED_SEALED_CORPUS_VERSIONS: readonly number[];
164
694
  /**
165
695
  * Exact sandbox recording of the provider-owned reads a drop-in consumer depends on:
166
696
  * parameter-stable GET observations (area/PSC inventory), the lab catalog, team labs, and
@@ -168,9 +698,16 @@ declare const SEALED_CORPUS_VERSION = 1;
168
698
  * `booking_key`s can never match a live cache key, so it stays with the generator.
169
699
  */
170
700
  type SealedCorpus = {
171
- version: typeof SEALED_CORPUS_VERSION;
701
+ version: 1 | typeof SEALED_CORPUS_VERSION;
172
702
  recordedAt: string;
173
703
  source: string;
704
+ /**
705
+ * The recording team's id (version 2+). When present it is the mock's default team,
706
+ * and each lab account's `team_id_allowlist` is kept exactly as recorded.
707
+ */
708
+ teamId?: string;
709
+ /** SHA-256 of the recording's content, written by `corpus pull`. Identifies it in logs. */
710
+ fingerprint?: string;
174
711
  observations: Record<string, GetCacheEntry>;
175
712
  catalog: {
176
713
  labTests: LabTestRecord[];
@@ -199,6 +736,183 @@ type SeedReport = {
199
736
  cacheEntries: number;
200
737
  };
201
738
 
739
+ /**
740
+ * Signed webhook delivery, the way Junction delivers through Svix.
741
+ *
742
+ * Signing lives here, next to the events, so a consumer never hand-rolls it. The
743
+ * signature is standard Svix — `v1,` + base64 HMAC-SHA256, keyed by the base64 part of
744
+ * `whsec_…`, over `"<svix-id>.<svix-timestamp>.<body>"` — so the official `svix`
745
+ * verifier, and a receiver written for real Junction webhooks, accept it unchanged.
746
+ */
747
+ type WebhookEndpoint = {
748
+ url: string;
749
+ /** Svix signing secret, `whsec_<base64>`. A bare base64 secret is accepted too. */
750
+ secret: string;
751
+ /**
752
+ * Delay before each attempt, in ms; the first entry delays the first attempt. Default:
753
+ * Svix's schedule (immediately, 5 s, 5 min, 30 min, 2 h, 5 h, 10 h, 10 h).
754
+ */
755
+ retryDelaysMs?: readonly number[];
756
+ /** Abort an attempt that takes longer than this. Default 15 s, like Svix. */
757
+ timeoutMs?: number;
758
+ /** Added to every delivery, e.g. a routing scope for a shared receiver. */
759
+ headers?: Record<string, string>;
760
+ fetch?: (request: Request) => Promise<Response>;
761
+ };
762
+ type WebhookAttempt = {
763
+ attempt: number;
764
+ /** Wall-clock time of the attempt, ISO-8601. */
765
+ at: string;
766
+ /** Receiver's status code, or `null` when the request never completed. */
767
+ status: number | null;
768
+ error: string | null;
769
+ durationMs: number;
770
+ };
771
+ type WebhookDelivery = {
772
+ messageId: string;
773
+ namespace: string;
774
+ event: JunctionWebhookEvent;
775
+ state: "pending" | "delivered" | "failed";
776
+ attempts: WebhookAttempt[];
777
+ };
778
+ type WebhookDispatcher = {
779
+ /** Queue an event for delivery. Returns immediately; delivery runs in the background. */
780
+ publish(event: JunctionWebhookEvent, namespace: string): WebhookDelivery;
781
+ deliveries(namespace?: string): WebhookDelivery[];
782
+ /** Deliver a message again now, as a new attempt, whatever its state. */
783
+ replay(messageId: string): Promise<WebhookDelivery | undefined>;
784
+ /** Run every pending retry now instead of waiting out its delay. */
785
+ flush(): Promise<void>;
786
+ /** Resolve once no delivery is in flight. */
787
+ idle(): Promise<void>;
788
+ /** Cancel pending retries and forget every delivery. */
789
+ clear(namespace?: string): void;
790
+ };
791
+ /** The `svix-signature` value for one message: `v1,<base64 HMAC-SHA256>`. */
792
+ declare const signSvix: (secret: string, messageId: string, timestampSeconds: number, body: string) => Promise<string>;
793
+ /**
794
+ * Check a delivery's signature the way the `svix` verifier does: any `v1,` entry in the
795
+ * space-separated header matches, and the timestamp is within `toleranceSeconds`.
796
+ */
797
+ declare const verifySvix: (secret: string, headers: {
798
+ "svix-id": string;
799
+ "svix-timestamp": string;
800
+ "svix-signature": string;
801
+ }, body: string, options?: {
802
+ toleranceSeconds?: number;
803
+ now?: () => number;
804
+ }) => Promise<boolean>;
805
+ declare const createWebhookDispatcher: (endpoint: WebhookEndpoint) => WebhookDispatcher;
806
+
807
+ /**
808
+ * Every order status the contract defines, by collection method (from the vendored
809
+ * OpenAPI enum). An admin transition may only target a status real Junction can emit.
810
+ */
811
+ declare const ORDER_STATUSES_BY_METHOD: Readonly<Record<string, readonly string[]>>;
812
+ /**
813
+ * Friendly transition targets, per method, mapped onto the contract's own statuses.
814
+ * A name is absent for a method when no real status means it — walk-in tests have no
815
+ * draw event, so `collected` is refused there rather than approximated.
816
+ */
817
+ declare const TRANSITION_ALIASES: Readonly<Record<string, Readonly<Record<string, string>>>>;
818
+ /**
819
+ * Named results, as the flags `simulate_order` accepts. They steer the generator, so
820
+ * the payload keeps Junction's exact shape; `PUT /__admin/results/{order}` installs an
821
+ * exact payload instead.
822
+ */
823
+ declare const RESULT_FIXTURES: Readonly<Record<string, Record<string, unknown>>>;
824
+ /**
825
+ * Junction failure modes a suite can switch on by name. `sandbox_user_quota` reproduces
826
+ * the shared-sandbox error byte for byte (message as observed from a real sandbox team);
827
+ * the others are shape-plausible, not verified against Junction.
828
+ */
829
+ declare const FAULT_PRESETS: Readonly<Record<string, Omit<FaultRule, "id">>>;
830
+ /** Resolve a target (`completed`, `completed.completed`, or a full status) for a method. */
831
+ declare const resolveTransition: (method: string, target: string) => {
832
+ status: string;
833
+ } | {
834
+ error: string;
835
+ };
836
+ type JunctionAdminOptions = {
837
+ webhooks: WebhookDispatcher | undefined;
838
+ };
839
+
840
+ type SeedCacheEntry = {
841
+ status: number;
842
+ headers: Record<string, string>;
843
+ body: unknown;
844
+ };
845
+
846
+ type PrefetchTarget = {
847
+ baseUrl: string;
848
+ fetch: (request: Request) => Promise<Response>;
849
+ headers: () => Promise<Record<string, string>> | Record<string, string>;
850
+ };
851
+ /**
852
+ * Seal coverage-corpus routing ZIPs + availability POSTs into the seed observation cache.
853
+ * Call after warmup, before `seedFrom`, so compare walks can forceInclude geo/scheduling reads.
854
+ */
855
+ declare const prefetchCoverageObservations: (args: {
856
+ real: PrefetchTarget;
857
+ getCache: Map<string, SeedCacheEntry>;
858
+ zips?: readonly string[];
859
+ /** ZIPs that also get PSC availability POST seals. Defaults to scheduling corpus. */
860
+ schedulingZips?: readonly string[];
861
+ /** ZIPs for phlebotomy availability (narrow served set). Defaults to PHLEBOTOMY_ZIPS. */
862
+ phlebotomyZips?: readonly string[];
863
+ labIds?: readonly number[];
864
+ /** Also prefetch phlebotomy + PSC availability. Default true. */
865
+ includeAvailability?: boolean;
866
+ sleep?: (ms: number) => Promise<void>;
867
+ minIntervalMs?: number;
868
+ }) => Promise<void>;
869
+
870
+ declare const DEFAULT_JUNCTION_BASE_URL = "https://api.sandbox.tryvital.io";
871
+
872
+ type PullCorpusOptions = {
873
+ apiKey: string;
874
+ baseUrl?: string;
875
+ /** ZIPs to record area and PSC coverage for. Default: the shipped coverage set. */
876
+ zips?: readonly string[];
877
+ /**
878
+ * Refresh only `zips` on top of this corpus, keeping everything else. How a single
879
+ * ZIP is added to a committed corpus without re-recording the whole thing.
880
+ */
881
+ base?: SealedCorpus;
882
+ /** Skip the team's catalog and lab accounts (coverage only). */
883
+ coverageOnly?: boolean;
884
+ fetch?: (request: Request) => Promise<Response>;
885
+ /** Pause between vendor calls, to stay under rate limits. Default 50 ms. */
886
+ minIntervalMs?: number;
887
+ onProgress?: (message: string) => void;
888
+ };
889
+ /** SHA-256 over a corpus's content — not its recording time — so a re-pull of unchanged data matches. */
890
+ declare const fingerprintCorpus: (corpus: SealedCorpus) => Promise<string>;
891
+ /** Record a sealed corpus from a real Junction team. Read-only: it only issues GETs. */
892
+ declare const pullCorpus: (options: PullCorpusOptions) => Promise<SealedCorpus>;
893
+ type SetDiff = {
894
+ added: string[];
895
+ removed: string[];
896
+ changed: string[];
897
+ };
898
+ type CorpusDiff = {
899
+ identical: boolean;
900
+ /** Set when the recorded team differs (or one side recorded none). */
901
+ teamId?: {
902
+ before: string | null;
903
+ after: string | null;
904
+ };
905
+ observations: SetDiff;
906
+ labTests: SetDiff;
907
+ labAccounts: SetDiff;
908
+ zips: {
909
+ added: string[];
910
+ removed: string[];
911
+ };
912
+ };
913
+ /** What changed between two recordings: coverage, catalog, accounts and raw observations. */
914
+ declare const diffCorpus: (before: SealedCorpus, after: SealedCorpus) => CorpusDiff;
915
+
202
916
  /** High-signal ZIPs for availability prefetch (full routing corpus still used for area/psc). */
203
917
  declare const PSC_AVAILABILITY_ZIPS: readonly ["85004", "85234", "11050", "10006", "90012", "96101", "92101", "60601", "33130", "98104", "75034", "07030"];
204
918
  /**
@@ -381,40 +1095,20 @@ type OpenAPIDocument = {
381
1095
  };
382
1096
 
383
1097
  declare const document: OpenAPIDocument;
384
- 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";
385
- 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";
386
- 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"];
387
- 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"];
1098
+ 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_tests_for_team_v3_lab_tests_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";
1099
+ 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_tests_for_team_v3_lab_tests_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";
1100
+ 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_tests_for_team_v3_lab_tests_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"];
1101
+ 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_tests_for_team_v3_lab_tests_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"];
388
1102
 
389
- type SeedCacheEntry = {
390
- status: number;
391
- headers: Record<string, string>;
392
- body: unknown;
393
- };
394
-
395
- type PrefetchTarget = {
396
- baseUrl: string;
397
- fetch: (request: Request) => Promise<Response>;
398
- headers: () => Promise<Record<string, string>> | Record<string, string>;
399
- };
1103
+ /** The `detail` of each order-scoped operation's 404 for an order that does not exist. */
1104
+ declare const ORDER_NOT_FOUND: Readonly<Record<string, string>>;
400
1105
  /**
401
- * Seal coverage-corpus routing ZIPs + availability POSTs into the seed observation cache.
402
- * Call after warmup, before `seedFrom`, so compare walks can forceInclude geo/scheduling reads.
1106
+ * Names the missing resource: `order <id>`, `user <id>`, or `user client:<client_user_id>`
1107
+ * (the id percent-encoded). Never part of Junction's answer.
403
1108
  */
404
- declare const prefetchCoverageObservations: (args: {
405
- real: PrefetchTarget;
406
- getCache: Map<string, SeedCacheEntry>;
407
- zips?: readonly string[];
408
- /** ZIPs that also get PSC availability POST seals. Defaults to scheduling corpus. */
409
- schedulingZips?: readonly string[];
410
- /** ZIPs for phlebotomy availability (narrow served set). Defaults to PHLEBOTOMY_ZIPS. */
411
- phlebotomyZips?: readonly string[];
412
- labIds?: readonly number[];
413
- /** Also prefetch phlebotomy + PSC availability. Default true. */
414
- includeAvailability?: boolean;
415
- sleep?: (ms: number) => Promise<void>;
416
- minIntervalMs?: number;
417
- }) => Promise<void>;
1109
+ declare const MISS_HEADER = "x-mockingbird-miss";
1110
+ /** What the namespace does hold: `users=<n> orders=<n>`. */
1111
+ declare const KNOWN_HEADER = "x-mockingbird-known";
418
1112
 
419
1113
  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";
420
1114
  type Mutation = {
@@ -483,6 +1177,119 @@ declare const reshapeCoverageGeoCommand: (command: LogicalCommand, _state: Explo
483
1177
  labIds?: readonly number[];
484
1178
  }) => LogicalCommand;
485
1179
 
1180
+ type JunctionRuntimeOptions = {
1181
+ /**
1182
+ * Recorded vendor data every namespace starts from. The served mock loads the corpus
1183
+ * shipped at `@crvouga/mockingbird-service-junction/corpus` unless told otherwise.
1184
+ */
1185
+ corpus?: SealedCorpus;
1186
+ /** Default: `corpus` when a corpus is loaded, otherwise `synthetic`. */
1187
+ geo?: GeoMode;
1188
+ /** The team's lab accounts, or `{ presets, accounts }`; see {@link JunctionAPIOptions.labAccounts}. */
1189
+ labAccounts?: LabAccountLayout;
1190
+ /** The team the mock answers as. Default: the corpus's recorded team, else a fixed id. */
1191
+ teamId?: string;
1192
+ /** Sandbox-only restrictions, every one off by default; per namespace via `PUT /__admin/limits`. */
1193
+ limits?: JunctionLimitsInput;
1194
+ /** `adopt-users` creates unknown user ids on first use. Default `strict`. */
1195
+ identity?: IdentityMode;
1196
+ /** Users and orders every namespace starts with, re-applied on each reset. */
1197
+ fixtures?: JunctionFixtures;
1198
+ /** Requests each namespace's journal keeps (`GET /__admin/requests`). Default 1000. */
1199
+ journalSize?: number;
1200
+ /** Deliver signed webhooks here, with Svix's retry schedule. */
1201
+ webhooks?: WebhookEndpoint;
1202
+ /** Called in-process for every webhook event, delivered or not. */
1203
+ onWebhook?: (event: Parameters<WebhookPublisher>[0], namespace: string) => void;
1204
+ sqlite?: SqliteClient;
1205
+ clock?: Clock;
1206
+ /** Seeds fault rates and webhook retry jitter. */
1207
+ seed?: number | string;
1208
+ /** Require `x-mockingbird-admin-key` on `/__admin/*`. */
1209
+ adminKey?: string;
1210
+ onLog?: (entry: RequestLog) => void;
1211
+ };
1212
+ type JunctionRuntime = ServiceRuntime<JunctionAPI> & {
1213
+ /** Signed delivery, when `webhooks` was configured. */
1214
+ readonly webhooks: WebhookDispatcher | undefined;
1215
+ };
1216
+ /**
1217
+ * The Junction mock with Mockingbird's full service contract: unauthenticated
1218
+ * `/health`, the `/__admin/*` control plane, per-request namespaces
1219
+ * (`x-mockingbird-namespace`), clock control, fault injection and request metrics.
1220
+ * Runtime-neutral: serve it with any Fetch-native server, or use `./server` for Node.
1221
+ */
1222
+ declare const createRuntime: (options?: JunctionRuntimeOptions) => JunctionRuntime;
1223
+
1224
+ /**
1225
+ * Appointment scheduling surface: phlebotomy + PSC availability/booking lifecycle,
1226
+ * cancellation reasons, area serviceability, and PSC site info.
1227
+ *
1228
+ * Slot data is generated deterministically (seeded by zip/site/date), so walks are
1229
+ * reproducible without any provider-side state.
1230
+ */
1231
+
1232
+ /** Status of the unknown-ZIP error: unused by Junction, and not retried by HTTP clients. */
1233
+ declare const UNKNOWN_ZIP_STATUS = 424;
1234
+ declare const UNKNOWN_ZIP_ERROR_TYPE = "MOCKINGBIRD_UNKNOWN_ZIP";
1235
+
1236
+ /**
1237
+ * Differential verification: the same requests against real Junction and the mock,
1238
+ * with every divergence reported. Two checks:
1239
+ *
1240
+ * - drift: each recorded corpus observation re-fetched from the real API. A stale
1241
+ * entry means the corpus no longer matches production and should be re-pulled.
1242
+ * - scenario: a fixed script of stateful calls (users, catalog, serviceability, and
1243
+ * optionally orders) run against both sides, comparing status codes, response
1244
+ * shapes, and — for the documented error bodies — exact bytes.
1245
+ *
1246
+ * The scenario also replays every order-scoped operation against a random unknown order
1247
+ * (each 404 body compared exactly) and, with `orders` and a corpus holding an account whose
1248
+ * `team_id_allowlist` is empty, orders through that account with and without its id.
1249
+ *
1250
+ * Only a sandbox team should be used: the scenario creates, and always deletes, one
1251
+ * user (and, with `orders`, places and cancels its orders).
1252
+ */
1253
+ type VerifyOptions = {
1254
+ realKey: string;
1255
+ realUrl?: string;
1256
+ /** The mock under test, e.g. `createRuntime({ corpus })`. */
1257
+ mock: {
1258
+ fetch(request: Request): Promise<Response>;
1259
+ };
1260
+ corpus: SealedCorpus;
1261
+ /** Re-fetch at most this many observations (spread evenly). Default: all of them. */
1262
+ sample?: number;
1263
+ /** Skip the drift check. */
1264
+ skipDrift?: boolean;
1265
+ /** Also place, read and cancel an order on the real team. */
1266
+ orders?: boolean;
1267
+ fetch?: (request: Request) => Promise<Response>;
1268
+ minIntervalMs?: number;
1269
+ onProgress?: (message: string) => void;
1270
+ };
1271
+ type Divergence = {
1272
+ check: string;
1273
+ kind: "status" | "shape" | "body" | "error";
1274
+ /** JSON path of the first difference, when there is one. */
1275
+ at?: string;
1276
+ real: unknown;
1277
+ mock: unknown;
1278
+ };
1279
+ type VerifyReport = {
1280
+ ok: boolean;
1281
+ drift: {
1282
+ checked: number;
1283
+ stale: number;
1284
+ };
1285
+ scenario: {
1286
+ steps: number;
1287
+ divergent: number;
1288
+ };
1289
+ divergences: Divergence[];
1290
+ };
1291
+ declare const verifyAgainstReal: (options: VerifyOptions) => Promise<VerifyReport>;
1292
+
486
1293
  declare const JUNCTION_NAMESPACE = "junction";
487
1294
  /**
488
1295
  * Stateful mock of the Junction (Vital) API user and lab-testing surfaces.
@@ -493,13 +1300,56 @@ declare const JUNCTION_NAMESPACE = "junction";
493
1300
  type JunctionAPIOptions = APIOptions & {
494
1301
  onWebhook?: WebhookPublisher;
495
1302
  webhook?: JunctionWebhookOptions;
1303
+ /** Install a recorded corpus at construction; it is re-applied on every `reset()`. */
1304
+ corpus?: SealedCorpus;
1305
+ /**
1306
+ * How serviceability reads answer a ZIP the corpus does not cover. Default: `corpus`
1307
+ * when a corpus is given (refuse, loudly), otherwise `synthetic` (invent coverage).
1308
+ */
1309
+ geo?: GeoMode;
1310
+ /**
1311
+ * The team's lab accounts, replacing the built-in fixtures (and any from the corpus):
1312
+ * a list of accounts, or `{ presets: [...], accounts: [...] }`. `create_order` routes,
1313
+ * accepts and rejects `lab_account_id` against these.
1314
+ */
1315
+ labAccounts?: LabAccountLayout;
1316
+ /**
1317
+ * The team the mock answers as: `team_id` on users and orders, and the team lab-account
1318
+ * allowlists are checked against. Default: the corpus's recorded team, else a fixed id.
1319
+ */
1320
+ teamId?: string;
1321
+ /** Sandbox-only restrictions to enforce. Every one is off unless set here. */
1322
+ limits?: JunctionLimitsInput;
1323
+ /** `adopt-users` creates an unknown (well-formed) `user_id` on first use. Default `strict`. */
1324
+ identity?: IdentityMode;
1325
+ /** Users and orders to load into every namespace, re-applied on every `reset()`. */
1326
+ fixtures?: JunctionFixtures;
1327
+ };
1328
+ /** What a loaded corpus covers, for `/health` and startup logs. */
1329
+ type CorpusInfo = {
1330
+ label: string;
1331
+ recordedAt: string;
1332
+ source: string;
1333
+ observations: number;
1334
+ labTests: number;
1335
+ labAccounts: number;
1336
+ zips: number;
1337
+ /** The recording team, when the corpus recorded one (version 2+). */
1338
+ teamId?: string;
496
1339
  };
1340
+ /** A short, stable name for a corpus: its recording date plus a digest of what it holds. */
1341
+ declare const corpusLabel: (corpus: SealedCorpus) => string;
497
1342
  declare class JunctionAPI implements FetchAPI {
498
1343
  readonly app: Hono;
499
1344
  readonly sqlite: SqliteClient;
500
1345
  private readonly service;
501
1346
  private readonly state;
1347
+ private readonly now;
1348
+ private readonly explicitTeamId;
1349
+ private readonly fixtures;
1350
+ private readonly rateWindow;
502
1351
  private corpus;
1352
+ private labAccountConfig;
503
1353
  constructor(options?: JunctionAPIOptions);
504
1354
  fetch(request: Request): Promise<Response>;
505
1355
  reset(): Promise<void>;
@@ -508,6 +1358,63 @@ declare class JunctionAPI implements FetchAPI {
508
1358
  * labs, and lab accounts with the recording's exact values. Re-applied on `reset()`.
509
1359
  */
510
1360
  installCorpus(corpus: SealedCorpus): void;
1361
+ /**
1362
+ * Which lab accounts the ordering rules see, in order of precedence: the configured
1363
+ * ones, then a corpus's real accounts, then the built-in fixtures (which a corpus
1364
+ * recorded from a team with no accounts leaves in place).
1365
+ */
1366
+ private seedLabAccounts;
1367
+ /** The team this namespace answers as. */
1368
+ get teamId(): string;
1369
+ /**
1370
+ * Replace the team's lab accounts (a list, or `{ presets, accounts }`); `undefined`
1371
+ * restores the default. Kept across `reset()`, per namespace.
1372
+ */
1373
+ configureLabAccounts(accounts: LabAccountLayout | undefined): StoredLabAccount[];
1374
+ labAccounts(): StoredLabAccount[];
1375
+ /** Apply one change to the effective account list and keep it as this namespace's config. */
1376
+ private editLabAccounts;
1377
+ /** Add one account. Throws `LabAccountConflict` when the id is taken. Kept across resets. */
1378
+ addLabAccount(input: LabAccountInput | StoredLabAccount): StoredLabAccount;
1379
+ /** Add a named preset (see `LAB_ACCOUNT_PRESETS`); `undefined` for an unknown name. */
1380
+ addLabAccountPreset(name: string, id?: string): StoredLabAccount | undefined;
1381
+ /** Merge fields into an account; `undefined` when there is none with that id. */
1382
+ patchLabAccount(id: string, patch: Partial<LabAccountInput>): StoredLabAccount | undefined;
1383
+ removeLabAccount(id: string): boolean;
1384
+ /** The sandbox limits this namespace enforces. Every one is off unless configured. */
1385
+ get limits(): JunctionLimits;
1386
+ /** Change some limits (`null` lifts a numeric one); kept across `reset()`. */
1387
+ configureLimits(input: JunctionLimitsInput): JunctionLimits;
1388
+ get identity(): IdentityMode;
1389
+ set identity(mode: IdentityMode);
1390
+ /** Insert users with chosen ids, all or none. Throws `FixtureError` (400/404/409). */
1391
+ insertUsers(users: readonly UserFixture[]): UserRecord[];
1392
+ /** Remove a user outright, leaving no deletion tombstone. */
1393
+ hardDeleteUser(userId: string): boolean;
1394
+ /** Insert orders, optionally already in a status. No webhook fires unless asked. */
1395
+ insertOrders(orders: readonly OrderFixture[], options?: InsertOptions): OrderRecord[];
1396
+ /** Load users, then orders, in one call. */
1397
+ importFixtures(fixtures: JunctionFixtures, options?: InsertOptions): {
1398
+ users: UserRecord[];
1399
+ orders: OrderRecord[];
1400
+ };
1401
+ corpusInfo(): CorpusInfo | undefined;
1402
+ get geoMode(): GeoMode;
1403
+ set geoMode(mode: GeoMode);
1404
+ order(id: string): OrderRecord | undefined;
1405
+ orders(): OrderRecord[];
1406
+ /**
1407
+ * Put an order straight into a status (a full `<status>.<method>.<event>` value) and
1408
+ * publish the webhook a real transition would. `flags` takes the same keys as
1409
+ * `simulate_order`'s body: `interpretation`, `result_types`, `has_missing_results`.
1410
+ */
1411
+ transitionOrder(id: string, status: string, options: {
1412
+ now: () => number;
1413
+ flags?: Record<string, unknown> | null;
1414
+ }): OrderRecord | undefined;
1415
+ /** Serve `fixture` as this order's results, replacing the generated ones. */
1416
+ installResultFixture(orderId: string, fixture: ResultFixture): boolean;
1417
+ resultFixture(orderId: string): ResultFixture | undefined;
511
1418
  seedFrom(source: SeedSource, observations?: SeedObservations): Promise<SeedReport>;
512
1419
  ensureLabTests(source: SeedSource, ids: readonly string[]): Promise<number>;
513
1420
  ensureOrders(source: SeedSource, ids: readonly string[]): Promise<number>;
@@ -515,6 +1422,11 @@ declare class JunctionAPI implements FetchAPI {
515
1422
  webhookEvents(): JunctionWebhookEvent[];
516
1423
  webhookDeliveryAttempts(): WebhookDeliveryAttempt[];
517
1424
  }
1425
+ /** Raised by `addLabAccount` for an id the namespace already has. */
1426
+ declare class LabAccountConflict extends Error {
1427
+ readonly id: string;
1428
+ constructor(id: string);
1429
+ }
518
1430
 
519
- export { AVAILABILITY_ADDRESS, AVAILABILITY_START_DATE, COVERAGE_ZIPS, JUNCTION_NAMESPACE, JunctionAPI, PHLEBOTOMY_AVAILABILITY_ZIPS, PSC_AVAILABILITY_ZIPS, PSC_LAB_IDS, SEALED_CORPUS_VERSION, document, observationCacheKey, operationIds, parseSealedCorpus, prefetchCoverageObservations, reshapeCoverageGeoCommand, supportedOperationIds };
520
- export type { FetchAPI, GetCacheEntry, JunctionAPIOptions, JunctionWebhookEvent, JunctionWebhookOptions, OperationId, SealedCorpus, SeedObservations, SeedReport, SeedSource, SqliteClient, SupportedOperationId, WebhookPublisher };
1431
+ export { AVAILABILITY_ADDRESS, AVAILABILITY_START_DATE, COVERAGE_ZIPS, DEFAULT_JUNCTION_BASE_URL, DEFAULT_LIMITS, DELEGATED_ACCOUNT_STATES, FAULT_PRESETS, FixtureError, IDENTITY_MODES, JUNCTION_NAMESPACE, JunctionAPI, KNOWN_HEADER, LAB_ACCOUNT_PRESETS, LabAccountConflict, MISS_HEADER, MOCK_TEAM_ID, ORDER_NOT_FOUND, ORDER_STATUSES_BY_METHOD, OTHER_TEAM_ID, PHLEBOTOMY_AVAILABILITY_ZIPS, PLATFORM_ACCOUNT_STATES, PSC_AVAILABILITY_ZIPS, PSC_LAB_IDS, RESULT_FIXTURES, SANDBOX_KEY_PREFIXES, SEALED_CORPUS_VERSION, SUPPORTED_SEALED_CORPUS_VERSIONS, TEAM_LAB_ACCOUNTS, TRANSITION_ALIASES, UNKNOWN_ZIP_ERROR_TYPE, UNKNOWN_ZIP_STATUS, US_STATES, corpusLabel, createRuntime, createWebhookDispatcher, diffCorpus, document, fingerprintCorpus, isLinkedToTeam, isSandboxKey, labAccountFromInput, observationCacheKey, operationIds, parseSealedCorpus, prefetchCoverageObservations, presetAccountId, pullCorpus, reshapeCoverageGeoCommand, resolveTransition, sandboxUserQuotaBody, signSvix, supportedOperationIds, verifyAgainstReal, verifySvix };
1432
+ export type { CorpusDiff, CorpusInfo, Divergence, FetchAPI, GeoMode, GetCacheEntry, IdentityMode, InsertOptions, JunctionAPIOptions, JunctionAdminOptions, JunctionFixtures, JunctionLimits, JunctionLimitsInput, JunctionRuntime, JunctionRuntimeOptions, JunctionWebhookEvent, JunctionWebhookOptions, LabAccountDelegatedFlow, LabAccountInput, LabAccountLayout, LabAccountRecord, LabAccountStatus, OperationId, OrderFixture, OrderRecord, PullCorpusOptions, ResultFixture, SealedCorpus, SeedObservations, SeedReport, SeedSource, SetDiff, SqliteClient, StoredLabAccount, SupportedOperationId, UserFixture, UserRecord, VerifyOptions, VerifyReport, WebhookAttempt, WebhookDelivery, WebhookDispatcher, WebhookEndpoint, WebhookPublisher };