@crvouga/mockingbird-service-junction 0.2.0 → 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.js CHANGED
@@ -3,11 +3,23 @@ import {
3
3
  AVAILABILITY_START_DATE,
4
4
  COVERAGE_ZIPS,
5
5
  DEFAULT_JUNCTION_BASE_URL,
6
+ DEFAULT_LIMITS,
7
+ DELEGATED_ACCOUNT_STATES,
6
8
  FAULT_PRESETS,
9
+ FixtureError,
10
+ IDENTITY_MODES,
7
11
  JUNCTION_NAMESPACE,
8
12
  JunctionAPI,
13
+ KNOWN_HEADER,
14
+ LAB_ACCOUNT_PRESETS,
15
+ LabAccountConflict,
16
+ MISS_HEADER,
17
+ MOCK_TEAM_ID,
18
+ ORDER_NOT_FOUND,
9
19
  ORDER_STATUSES_BY_METHOD,
20
+ OTHER_TEAM_ID,
10
21
  PHLEBOTOMY_AVAILABILITY_ZIPS,
22
+ PLATFORM_ACCOUNT_STATES,
11
23
  PSC_AVAILABILITY_ZIPS,
12
24
  PSC_LAB_IDS,
13
25
  RESULT_FIXTURES,
@@ -23,38 +35,55 @@ import {
23
35
  diffCorpus,
24
36
  document,
25
37
  fingerprintCorpus,
38
+ isLinkedToTeam,
26
39
  isSandboxKey,
27
40
  labAccountFromInput,
28
41
  observationCacheKey,
29
42
  operationIds,
30
43
  prefetchCoverageObservations,
44
+ presetAccountId,
31
45
  pullCorpus,
32
46
  reshapeCoverageGeoCommand,
33
47
  resolveTransition,
48
+ sandboxUserQuotaBody,
34
49
  signSvix,
35
50
  supportedOperationIds,
36
51
  verifyAgainstReal,
37
52
  verifySvix
38
- } from "./chunk-MA3HSUVD.js";
53
+ } from "./chunk-NFWUEPW4.js";
39
54
  import {
40
55
  SEALED_CORPUS_VERSION,
56
+ SUPPORTED_SEALED_CORPUS_VERSIONS,
41
57
  parseSealedCorpus
42
- } from "./chunk-4C6VYRJN.js";
58
+ } from "./chunk-ISDNA2TU.js";
43
59
  export {
44
60
  AVAILABILITY_ADDRESS,
45
61
  AVAILABILITY_START_DATE,
46
62
  COVERAGE_ZIPS,
47
63
  DEFAULT_JUNCTION_BASE_URL,
64
+ DEFAULT_LIMITS,
65
+ DELEGATED_ACCOUNT_STATES,
48
66
  FAULT_PRESETS,
67
+ FixtureError,
68
+ IDENTITY_MODES,
49
69
  JUNCTION_NAMESPACE,
50
70
  JunctionAPI,
71
+ KNOWN_HEADER,
72
+ LAB_ACCOUNT_PRESETS,
73
+ LabAccountConflict,
74
+ MISS_HEADER,
75
+ MOCK_TEAM_ID,
76
+ ORDER_NOT_FOUND,
51
77
  ORDER_STATUSES_BY_METHOD,
78
+ OTHER_TEAM_ID,
52
79
  PHLEBOTOMY_AVAILABILITY_ZIPS,
80
+ PLATFORM_ACCOUNT_STATES,
53
81
  PSC_AVAILABILITY_ZIPS,
54
82
  PSC_LAB_IDS,
55
83
  RESULT_FIXTURES,
56
84
  SANDBOX_KEY_PREFIXES,
57
85
  SEALED_CORPUS_VERSION,
86
+ SUPPORTED_SEALED_CORPUS_VERSIONS,
58
87
  TEAM_LAB_ACCOUNTS,
59
88
  TRANSITION_ALIASES,
60
89
  UNKNOWN_ZIP_ERROR_TYPE,
@@ -66,15 +95,18 @@ export {
66
95
  diffCorpus,
67
96
  document,
68
97
  fingerprintCorpus,
98
+ isLinkedToTeam,
69
99
  isSandboxKey,
70
100
  labAccountFromInput,
71
101
  observationCacheKey,
72
102
  operationIds,
73
103
  parseSealedCorpus,
74
104
  prefetchCoverageObservations,
105
+ presetAccountId,
75
106
  pullCorpus,
76
107
  reshapeCoverageGeoCommand,
77
108
  resolveTransition,
109
+ sandboxUserQuotaBody,
78
110
  signSvix,
79
111
  supportedOperationIds,
80
112
  verifyAgainstReal,
package/dist/server.d.ts CHANGED
@@ -148,6 +148,10 @@ type RequestLog$1 = {
148
148
  unmatched: boolean;
149
149
  /** Set when a fault rule produced the response. */
150
150
  faultId?: string;
151
+ /** Resource ids the handler touched (`userId`, `orderId`, …), when the service reports them. */
152
+ ids?: Record<string, string>;
153
+ /** Set when the service created a resource the request referred to but that did not exist. */
154
+ adopted?: boolean;
151
155
  };
152
156
  type MetricsReport$1 = {
153
157
  requests: number;
@@ -173,6 +177,28 @@ type Metrics$1 = {
173
177
  reset(): void;
174
178
  };
175
179
 
180
+ /** One journal entry: a request log stamped with when (on the mock clock) it was handled. */
181
+ type JournalEntry$1 = RequestLog$1 & {
182
+ at: string;
183
+ };
184
+ type JournalQuery$1 = {
185
+ /** Only this namespace. Omit for every namespace, oldest first across all of them. */
186
+ namespace?: string;
187
+ operationId?: string;
188
+ status?: number;
189
+ /** Only entries at or after this instant (epoch ms). */
190
+ since?: number;
191
+ /** At most this many, the most recent kept. */
192
+ limit?: number;
193
+ };
194
+ type Journal$1 = {
195
+ readonly size: number;
196
+ record(entry: JournalEntry$1): void;
197
+ list(query?: JournalQuery$1): JournalEntry$1[];
198
+ /** Forget one namespace's entries, or every namespace's. */
199
+ clear(namespace?: string): void;
200
+ };
201
+
176
202
  /**
177
203
  * A point-in-time copy of everything a service namespace holds.
178
204
  *
@@ -215,6 +241,7 @@ type ServiceRuntime$1<T extends ServiceInstance$1> = FetchAPI$2 & {
215
241
  readonly clock: Clock$1;
216
242
  readonly faults: FaultRegistry$1;
217
243
  readonly metrics: Metrics$1;
244
+ readonly journal: Journal$1;
218
245
  readonly rng: Rng$1;
219
246
  /** The instance behind `namespace` (the default one when omitted), created on first use. */
220
247
  instance(namespace?: string): T;
@@ -410,6 +437,10 @@ type RequestLog = {
410
437
  unmatched: boolean;
411
438
  /** Set when a fault rule produced the response. */
412
439
  faultId?: string;
440
+ /** Resource ids the handler touched (`userId`, `orderId`, …), when the service reports them. */
441
+ ids?: Record<string, string>;
442
+ /** Set when the service created a resource the request referred to but that did not exist. */
443
+ adopted?: boolean;
413
444
  };
414
445
  type MetricsReport = {
415
446
  requests: number;
@@ -435,6 +466,28 @@ type Metrics = {
435
466
  reset(): void;
436
467
  };
437
468
 
469
+ /** One journal entry: a request log stamped with when (on the mock clock) it was handled. */
470
+ type JournalEntry = RequestLog & {
471
+ at: string;
472
+ };
473
+ type JournalQuery = {
474
+ /** Only this namespace. Omit for every namespace, oldest first across all of them. */
475
+ namespace?: string;
476
+ operationId?: string;
477
+ status?: number;
478
+ /** Only entries at or after this instant (epoch ms). */
479
+ since?: number;
480
+ /** At most this many, the most recent kept. */
481
+ limit?: number;
482
+ };
483
+ type Journal = {
484
+ readonly size: number;
485
+ record(entry: JournalEntry): void;
486
+ list(query?: JournalQuery): JournalEntry[];
487
+ /** Forget one namespace's entries, or every namespace's. */
488
+ clear(namespace?: string): void;
489
+ };
490
+
438
491
  /**
439
492
  * A point-in-time copy of everything a service namespace holds.
440
493
  *
@@ -477,6 +530,7 @@ type ServiceRuntime<T extends ServiceInstance> = FetchAPI$1 & {
477
530
  readonly clock: Clock;
478
531
  readonly faults: FaultRegistry;
479
532
  readonly metrics: Metrics;
533
+ readonly journal: Journal;
480
534
  readonly rng: Rng;
481
535
  /** The instance behind `namespace` (the default one when omitted), created on first use. */
482
536
  instance(namespace?: string): T;
@@ -582,6 +636,21 @@ type LabTestRecord = {
582
636
  worst_case_tat_days: number | null;
583
637
  };
584
638
 
639
+ type JunctionLimits = {
640
+ /**
641
+ * Live users a team may hold; creating one more answers the sandbox's
642
+ * "maximum of 50 Sandbox users" 400. Deleted users free their slot. `null`: unlimited.
643
+ */
644
+ maxUsers: number | null;
645
+ /** Refuse `POST /v3/order/{id}/test` unless the API key is a sandbox key (`sk_us_…`/`sk_eu_…`). */
646
+ simulateRequiresSandbox: boolean;
647
+ /** Vendor requests per second per namespace before a 429. `null`: unlimited. */
648
+ rateLimitPerSecond: number | null;
649
+ };
650
+ type JunctionLimitsInput = {
651
+ [K in keyof JunctionLimits]?: JunctionLimits[K] | undefined;
652
+ };
653
+
585
654
  type GetCacheEntry = {
586
655
  status: number;
587
656
  headers: Record<string, string>;
@@ -602,6 +671,25 @@ type WebhookDeliveryAttempt = {
602
671
  timeout_ms: number;
603
672
  acknowledged: boolean;
604
673
  };
674
+ type UserRecord = {
675
+ user_id: string;
676
+ team_id: string;
677
+ client_user_id: string;
678
+ created_on: string;
679
+ connected_sources: unknown[];
680
+ fallback_time_zone: {
681
+ id: string;
682
+ source_slug: string;
683
+ updated_at: string;
684
+ } | null;
685
+ fallback_birth_date: {
686
+ value: string;
687
+ source_slug: string;
688
+ updated_at: string;
689
+ } | null;
690
+ ingestion_start: string | null;
691
+ ingestion_end: string | null;
692
+ };
605
693
  type OrderEventRecord = {
606
694
  id: number;
607
695
  created_at: string;
@@ -707,6 +795,62 @@ type JunctionWebhookOptions = {
707
795
  timeoutMs?: number;
708
796
  };
709
797
 
798
+ /**
799
+ * The test backdoor: users and orders put straight into a namespace, with ids a suite
800
+ * chooses — the Junction `user_id`s a database fixture already carries, or an order
801
+ * already in some status — plus the lenient identity mode that adopts unknown users.
802
+ *
803
+ * Everything inserted here goes through the records `create_user` and `create_order`
804
+ * build, so no read path can tell a fixture from an API-created resource.
805
+ */
806
+
807
+ /** How requests referring to a user the namespace does not hold are answered. */
808
+ type IdentityMode = "strict" | "adopt-users";
809
+ type UserFixture = {
810
+ /** Default: a fresh id, as `create_user` would mint. */
811
+ user_id?: string;
812
+ client_user_id: string;
813
+ /** ISO-8601. Default: the mock clock's now. */
814
+ created_on?: string;
815
+ /** An IANA zone, or the full `{ id, source_slug, updated_at }` object. */
816
+ fallback_time_zone?: string | UserRecord["fallback_time_zone"];
817
+ /** `YYYY-MM-DD`, or the full `{ value, source_slug, updated_at }` object. */
818
+ fallback_birth_date?: string | UserRecord["fallback_birth_date"];
819
+ ingestion_start?: string | null;
820
+ ingestion_end?: string | null;
821
+ };
822
+ type OrderFixture = {
823
+ /** Default: a fresh id, as `create_order` would mint. */
824
+ order_id?: string;
825
+ user_id: string;
826
+ lab_test_id: string;
827
+ lab_account_id?: string;
828
+ /** A transition target (`completed`, `at_lab`, or a full status). Default: just ordered. */
829
+ status?: string;
830
+ /** Default: the lab test's own method. */
831
+ collection_method?: string;
832
+ patient_details?: Record<string, unknown>;
833
+ patient_address?: Record<string, unknown>;
834
+ billing_type?: string;
835
+ icd_codes?: string[] | null;
836
+ passthrough?: string | null;
837
+ clinical_notes?: string | null;
838
+ /** ISO-8601. Default: the mock clock's now. */
839
+ created_at?: string;
840
+ /** Results served for the order, as `PUT /__admin/results/{id}` takes them. */
841
+ result_fixture?: Omit<ResultFixture, "name"> & {
842
+ name?: string;
843
+ };
844
+ };
845
+ type JunctionFixtures = {
846
+ users?: readonly UserFixture[];
847
+ orders?: readonly OrderFixture[];
848
+ };
849
+ type InsertOptions = {
850
+ /** Publish the webhooks the equivalent API calls would. Default `false`. */
851
+ emitWebhooks?: boolean;
852
+ };
853
+
710
854
  /**
711
855
  * Anything that can answer a Fetch `Request` with a `Response`.
712
856
  *
@@ -739,15 +883,39 @@ type LabAccountInput = {
739
883
  allowed_billing?: Record<string, readonly string[]>;
740
884
  /** Shorthand for `allowed_billing: { client_bill: states }`. Default: every state. */
741
885
  states?: readonly string[];
742
- /** Teams the account is linked to. Default: the mock team, so it is selectable. */
886
+ /** Teams the account is linked to. Default: the configured team, so it is selectable. */
743
887
  team_id_allowlist?: readonly string[];
744
888
  };
745
889
 
890
+ /**
891
+ * Named lab accounts a suite can add in one call, modelled on a real team's recorded
892
+ * listing (a BioReference-linked team, recorded 2026-09-21 from the Junction sandbox).
893
+ *
894
+ * Where a preset copies a recorded account, its states and billing types are the
895
+ * recording's. `bioreference_ny_nj_delegated` is not a recorded account: it is the
896
+ * NY/NJ client-bill shape a consumer asked for, kept because suites depend on it.
897
+ */
898
+
899
+ /**
900
+ * A lab-account layout as a file or option declares it: a bare list of accounts, or
901
+ * presets (by name, or `{ name, id }`) plus accounts, added in that order.
902
+ */
903
+ type LabAccountLayout = readonly LabAccountInput[] | {
904
+ presets?: readonly (string | {
905
+ name: string;
906
+ id?: string;
907
+ })[];
908
+ accounts?: readonly LabAccountInput[];
909
+ };
910
+
746
911
  /**
747
912
  * Version of the sealed-corpus on-disk format. Bumped whenever the shape changes so a
748
913
  * stale recording is rejected instead of half-applied.
914
+ *
915
+ * - 1: observations, catalog and lab accounts.
916
+ * - 2: adds `teamId`, the recording team, so lab-account allowlists load verbatim.
749
917
  */
750
- declare const SEALED_CORPUS_VERSION = 1;
918
+ declare const SEALED_CORPUS_VERSION = 2;
751
919
  /**
752
920
  * Exact sandbox recording of the provider-owned reads a drop-in consumer depends on:
753
921
  * parameter-stable GET observations (area/PSC inventory), the lab catalog, team labs, and
@@ -755,9 +923,14 @@ declare const SEALED_CORPUS_VERSION = 1;
755
923
  * `booking_key`s can never match a live cache key, so it stays with the generator.
756
924
  */
757
925
  type SealedCorpus = {
758
- version: typeof SEALED_CORPUS_VERSION;
926
+ version: 1 | typeof SEALED_CORPUS_VERSION;
759
927
  recordedAt: string;
760
928
  source: string;
929
+ /**
930
+ * The recording team's id (version 2+). When present it is the mock's default team,
931
+ * and each lab account's `team_id_allowlist` is kept exactly as recorded.
932
+ */
933
+ teamId?: string;
761
934
  /** SHA-256 of the recording's content, written by `corpus pull`. Identifies it in logs. */
762
935
  fingerprint?: string;
763
936
  observations: Record<string, GetCacheEntry>;
@@ -856,10 +1029,22 @@ type JunctionAPIOptions = APIOptions & {
856
1029
  */
857
1030
  geo?: GeoMode;
858
1031
  /**
859
- * The team's lab accounts, replacing the built-in fixtures (and any from the corpus).
860
- * `create_order` routes, accepts and rejects `lab_account_id` against these.
1032
+ * The team's lab accounts, replacing the built-in fixtures (and any from the corpus):
1033
+ * a list of accounts, or `{ presets: [...], accounts: [...] }`. `create_order` routes,
1034
+ * accepts and rejects `lab_account_id` against these.
1035
+ */
1036
+ labAccounts?: LabAccountLayout;
1037
+ /**
1038
+ * The team the mock answers as: `team_id` on users and orders, and the team lab-account
1039
+ * allowlists are checked against. Default: the corpus's recorded team, else a fixed id.
861
1040
  */
862
- labAccounts?: readonly LabAccountInput[];
1041
+ teamId?: string;
1042
+ /** Sandbox-only restrictions to enforce. Every one is off unless set here. */
1043
+ limits?: JunctionLimitsInput;
1044
+ /** `adopt-users` creates an unknown (well-formed) `user_id` on first use. Default `strict`. */
1045
+ identity?: IdentityMode;
1046
+ /** Users and orders to load into every namespace, re-applied on every `reset()`. */
1047
+ fixtures?: JunctionFixtures;
863
1048
  };
864
1049
  /** What a loaded corpus covers, for `/health` and startup logs. */
865
1050
  type CorpusInfo = {
@@ -870,12 +1055,18 @@ type CorpusInfo = {
870
1055
  labTests: number;
871
1056
  labAccounts: number;
872
1057
  zips: number;
1058
+ /** The recording team, when the corpus recorded one (version 2+). */
1059
+ teamId?: string;
873
1060
  };
874
1061
  declare class JunctionAPI implements FetchAPI {
875
1062
  readonly app: Hono;
876
1063
  readonly sqlite: SqliteClient;
877
1064
  private readonly service;
878
1065
  private readonly state;
1066
+ private readonly now;
1067
+ private readonly explicitTeamId;
1068
+ private readonly fixtures;
1069
+ private readonly rateWindow;
879
1070
  private corpus;
880
1071
  private labAccountConfig;
881
1072
  constructor(options?: JunctionAPIOptions);
@@ -892,9 +1083,40 @@ declare class JunctionAPI implements FetchAPI {
892
1083
  * recorded from a team with no accounts leaves in place).
893
1084
  */
894
1085
  private seedLabAccounts;
895
- /** Replace the team's lab accounts; `undefined` restores the default. Kept across resets. */
896
- configureLabAccounts(accounts: readonly LabAccountInput[] | undefined): StoredLabAccount[];
1086
+ /** The team this namespace answers as. */
1087
+ get teamId(): string;
1088
+ /**
1089
+ * Replace the team's lab accounts (a list, or `{ presets, accounts }`); `undefined`
1090
+ * restores the default. Kept across `reset()`, per namespace.
1091
+ */
1092
+ configureLabAccounts(accounts: LabAccountLayout | undefined): StoredLabAccount[];
897
1093
  labAccounts(): StoredLabAccount[];
1094
+ /** Apply one change to the effective account list and keep it as this namespace's config. */
1095
+ private editLabAccounts;
1096
+ /** Add one account. Throws `LabAccountConflict` when the id is taken. Kept across resets. */
1097
+ addLabAccount(input: LabAccountInput | StoredLabAccount): StoredLabAccount;
1098
+ /** Add a named preset (see `LAB_ACCOUNT_PRESETS`); `undefined` for an unknown name. */
1099
+ addLabAccountPreset(name: string, id?: string): StoredLabAccount | undefined;
1100
+ /** Merge fields into an account; `undefined` when there is none with that id. */
1101
+ patchLabAccount(id: string, patch: Partial<LabAccountInput>): StoredLabAccount | undefined;
1102
+ removeLabAccount(id: string): boolean;
1103
+ /** The sandbox limits this namespace enforces. Every one is off unless configured. */
1104
+ get limits(): JunctionLimits;
1105
+ /** Change some limits (`null` lifts a numeric one); kept across `reset()`. */
1106
+ configureLimits(input: JunctionLimitsInput): JunctionLimits;
1107
+ get identity(): IdentityMode;
1108
+ set identity(mode: IdentityMode);
1109
+ /** Insert users with chosen ids, all or none. Throws `FixtureError` (400/404/409). */
1110
+ insertUsers(users: readonly UserFixture[]): UserRecord[];
1111
+ /** Remove a user outright, leaving no deletion tombstone. */
1112
+ hardDeleteUser(userId: string): boolean;
1113
+ /** Insert orders, optionally already in a status. No webhook fires unless asked. */
1114
+ insertOrders(orders: readonly OrderFixture[], options?: InsertOptions): OrderRecord[];
1115
+ /** Load users, then orders, in one call. */
1116
+ importFixtures(fixtures: JunctionFixtures, options?: InsertOptions): {
1117
+ users: UserRecord[];
1118
+ orders: OrderRecord[];
1119
+ };
898
1120
  corpusInfo(): CorpusInfo | undefined;
899
1121
  get geoMode(): GeoMode;
900
1122
  set geoMode(mode: GeoMode);
@@ -928,8 +1150,18 @@ type JunctionRuntimeOptions = {
928
1150
  corpus?: SealedCorpus;
929
1151
  /** Default: `corpus` when a corpus is loaded, otherwise `synthetic`. */
930
1152
  geo?: GeoMode;
931
- /** The team's lab accounts; see {@link JunctionAPIOptions.labAccounts}. */
932
- labAccounts?: readonly LabAccountInput[];
1153
+ /** The team's lab accounts, or `{ presets, accounts }`; see {@link JunctionAPIOptions.labAccounts}. */
1154
+ labAccounts?: LabAccountLayout;
1155
+ /** The team the mock answers as. Default: the corpus's recorded team, else a fixed id. */
1156
+ teamId?: string;
1157
+ /** Sandbox-only restrictions, every one off by default; per namespace via `PUT /__admin/limits`. */
1158
+ limits?: JunctionLimitsInput;
1159
+ /** `adopt-users` creates unknown user ids on first use. Default `strict`. */
1160
+ identity?: IdentityMode;
1161
+ /** Users and orders every namespace starts with, re-applied on each reset. */
1162
+ fixtures?: JunctionFixtures;
1163
+ /** Requests each namespace's journal keeps (`GET /__admin/requests`). Default 1000. */
1164
+ journalSize?: number;
933
1165
  /** Deliver signed webhooks here, with Svix's retry schedule. */
934
1166
  webhooks?: WebhookEndpoint;
935
1167
  /** Called in-process for every webhook event, delivered or not. */
package/dist/server.js CHANGED
@@ -3,10 +3,10 @@ import {
3
3
  createServer,
4
4
  loadCorpus,
5
5
  serveTarget
6
- } from "./chunk-KKNULV7X.js";
7
- import "./chunk-MA3HSUVD.js";
8
- import "./chunk-MD4KYEIM.js";
9
- import "./chunk-4C6VYRJN.js";
6
+ } from "./chunk-KLBGX23P.js";
7
+ import "./chunk-NFWUEPW4.js";
8
+ import "./chunk-C2UGIPPM.js";
9
+ import "./chunk-ISDNA2TU.js";
10
10
  export {
11
11
  DEFAULT_PORT,
12
12
  createServer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crvouga/mockingbird-service-junction",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Stateful mock of the Junction (Vital) user API driven by its OpenAPI contract and verified by differential property tests against the Junction sandbox.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/sealed-corpus.ts"],
4
- "sourcesContent": ["import type { ExpectedResult, LabTestRecord } from \"./catalog.js\"\nimport type { GetCacheEntry } from \"./state.js\"\n\n/**\n * Version of the sealed-corpus on-disk format. Bumped whenever the shape changes so a\n * stale recording is rejected instead of half-applied.\n */\nexport const SEALED_CORPUS_VERSION = 1\n\n/**\n * Exact sandbox recording of the provider-owned reads a drop-in consumer depends on:\n * parameter-stable GET observations (area/PSC inventory), the lab catalog, team labs, and\n * lab accounts. Availability is intentionally absent \u2014 its slot dates and single-use\n * `booking_key`s can never match a live cache key, so it stays with the generator.\n */\nexport type SealedCorpus = {\n version: typeof SEALED_CORPUS_VERSION\n recordedAt: string\n source: string\n /** SHA-256 of the recording's content, written by `corpus pull`. Identifies it in logs. */\n fingerprint?: string\n observations: Record<string, GetCacheEntry>\n catalog: {\n labTests: LabTestRecord[]\n labs: Record<string, unknown>[]\n expectedResults: Record<string, ExpectedResult[]>\n }\n labAccounts: Record<string, unknown>[]\n}\n\n/** Throws on a non-object or an unrecognized `version`. */\nexport const parseSealedCorpus = (value: unknown): SealedCorpus => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(\"sealed corpus must be an object\")\n }\n const record = value as Record<string, unknown>\n if (record.version !== SEALED_CORPUS_VERSION) {\n throw new Error(`unsupported sealed corpus version: ${String(record.version)}`)\n }\n return record as unknown as SealedCorpus\n}\n"],
5
- "mappings": ";AAOO,IAAM,wBAAwB;AAwB9B,IAAM,oBAAoB,CAAC,UAAiC;AACjE,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACvE,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AACA,QAAM,SAAS;AACf,MAAI,OAAO,YAAY,uBAAuB;AAC5C,UAAM,IAAI,MAAM,sCAAsC,OAAO,OAAO,OAAO,CAAC,EAAE;AAAA,EAChF;AACA,SAAO;AACT;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/server.ts", "../../../adapters/node/src/cli.ts", "../../../adapters/node/src/serve.ts", "../../../adapters/node/src/listen.ts"],
4
- "sourcesContent": ["/// <reference types=\"node\" />\nimport { readFile } from \"node:fs/promises\"\nimport {\n type CliValues,\n type Listening,\n listen,\n type ServeTarget,\n} from \"@crvouga/mockingbird-adapter-node\"\nimport { defaultCorpus } from \"./corpus.js\"\nimport { pullCorpus } from \"./corpus-tools.js\"\nimport type { LabAccountInput } from \"./lab-accounts.js\"\nimport { createRuntime, type JunctionRuntime, type JunctionRuntimeOptions } from \"./runtime.js\"\nimport { parseSealedCorpus, type SealedCorpus } from \"./sealed-corpus.js\"\nimport type { GeoMode } from \"./state.js\"\n\n/** Port `mockingbird-junction serve` listens on when none is given. */\nexport const DEFAULT_PORT = 8787\n\n/** A corpus to serve: the shipped one, none (synthetic), a file path, or a loaded corpus. */\nexport type CorpusSource = \"default\" | \"none\" | (string & {}) | SealedCorpus\n\nexport type JunctionServerOptions = Omit<JunctionRuntimeOptions, \"corpus\"> & {\n /** Default `0`: the OS picks a free port (read it from `url` / `port`). */\n port?: number\n /** Default `127.0.0.1`. */\n host?: string\n /** Default `\"default\"`: the corpus shipped with this package. */\n corpus?: CorpusSource\n}\n\nexport type JunctionServer = Listening & { runtime: JunctionRuntime }\n\n/** Resolve a {@link CorpusSource} to a corpus, or `undefined` for synthetic data. */\nexport const loadCorpus = async (source: CorpusSource): Promise<SealedCorpus | undefined> => {\n if (typeof source !== \"string\") return source\n if (source === \"none\") return undefined\n if (source === \"default\") return defaultCorpus\n const raw = await readFile(source, \"utf8\").catch(() => undefined)\n if (raw === undefined) throw new Error(`junction mock corpus not found: ${source}`)\n return parseSealedCorpus(JSON.parse(raw))\n}\n\n/**\n * Serve the Junction mock over `node:http`. Resolves once it is listening.\n *\n * const server = await createServer()\n * // point the Vital SDK at server.url; await server.close() when done\n */\nexport const createServer = async (\n options: JunctionServerOptions = {},\n): Promise<JunctionServer> => {\n const { port, host, corpus: source, ...rest } = options\n const corpus = await loadCorpus(source ?? \"default\")\n const runtime = createRuntime({ ...rest, ...(corpus ? { corpus } : {}) })\n const listening = await listen(runtime, {\n port: port ?? 0,\n ...(host !== undefined ? { host } : {}),\n })\n return { ...listening, runtime }\n}\n\nconst asString = (value: string | boolean | undefined): string | undefined =>\n typeof value === \"string\" && value !== \"\" ? value : undefined\n\nconst retryDelays = (value: string | undefined): number[] | undefined => {\n if (value === undefined) return undefined\n const delays = value.split(\",\").map((part) => Number.parseInt(part.trim(), 10))\n if (delays.some((delay) => !Number.isFinite(delay) || delay < 0)) {\n throw new Error(`--webhook-retry-delays must be comma-separated milliseconds (got ${value})`)\n }\n return delays\n}\n\n/** How `serve` (and `serve --config`) builds the Junction mock from flags. */\nexport const serveTarget: ServeTarget = {\n name: \"junction\",\n defaultPort: DEFAULT_PORT,\n options: {\n corpus: {\n type: \"string\",\n value: \"<default|none|file>\",\n description: \"Corpus to serve: the shipped one, none (synthetic), or a pulled file\",\n default: \"default\",\n },\n geo: {\n type: \"string\",\n value: \"<corpus|synthetic>\",\n description: \"Unknown ZIPs: refuse (corpus) or invent coverage (synthetic)\",\n },\n \"lab-accounts\": {\n type: \"string\",\n value: \"<file>\",\n description: \"JSON array of lab accounts to route orders through\",\n },\n \"seed-url\": {\n type: \"string\",\n value: \"<url>\",\n description: \"Pull a corpus from this Junction team at boot (prefer a committed corpus)\",\n },\n \"seed-key\": {\n type: \"string\",\n value: \"<key>\",\n description: \"API key for --seed-url (env MOCKINGBIRD_JUNCTION_SEED_KEY)\",\n },\n \"webhook-url\": { type: \"string\", value: \"<url>\", description: \"Deliver signed webhooks here\" },\n \"webhook-secret\": {\n type: \"string\",\n value: \"<whsec_\u2026>\",\n description: \"Svix signing secret (env MOCKINGBIRD_JUNCTION_WEBHOOK_SECRET)\",\n },\n \"webhook-retry-delays\": {\n type: \"string\",\n value: \"<ms,ms,\u2026>\",\n description: \"Delay before each delivery attempt (default: Svix's schedule)\",\n },\n \"webhook-scope\": {\n type: \"string\",\n value: \"<scope>\",\n description: \"Sent as x-mockingbird-scope on every delivery\",\n },\n },\n async create(values: CliValues, common) {\n const seedUrl = asString(values[\"seed-url\"])\n const seedKey = asString(values[\"seed-key\"]) ?? process.env.MOCKINGBIRD_JUNCTION_SEED_KEY\n let corpus: SealedCorpus | undefined\n if (seedUrl !== undefined) {\n if (seedKey === undefined) throw new Error(\"--seed-url needs --seed-key\")\n console.log(\n `junction pulling a corpus from ${seedUrl} (commit one with \\`corpus pull\\` to skip this)`,\n )\n corpus = await pullCorpus({ baseUrl: seedUrl, apiKey: seedKey })\n } else {\n corpus = await loadCorpus(asString(values.corpus) ?? \"default\")\n }\n const geo = asString(values.geo)\n if (geo !== undefined && geo !== \"corpus\" && geo !== \"synthetic\") {\n throw new Error(`--geo must be corpus or synthetic (got ${geo})`)\n }\n const accountsFile = asString(values[\"lab-accounts\"])\n const labAccounts =\n accountsFile === undefined\n ? undefined\n : (JSON.parse(await readFile(accountsFile, \"utf8\")) as LabAccountInput[])\n const webhookUrl = asString(values[\"webhook-url\"])\n const webhookSecret =\n asString(values[\"webhook-secret\"]) ?? process.env.MOCKINGBIRD_JUNCTION_WEBHOOK_SECRET\n if (webhookUrl !== undefined && webhookSecret === undefined) {\n throw new Error(\"--webhook-url needs --webhook-secret (the whsec_\u2026 your receiver verifies)\")\n }\n const delays = retryDelays(asString(values[\"webhook-retry-delays\"]))\n const scope = asString(values[\"webhook-scope\"])\n return createRuntime({\n ...(corpus ? { corpus } : {}),\n ...(geo ? { geo: geo as GeoMode } : {}),\n ...(labAccounts ? { labAccounts } : {}),\n ...(webhookUrl !== undefined && webhookSecret !== undefined\n ? {\n webhooks: {\n url: webhookUrl,\n secret: webhookSecret,\n ...(delays ? { retryDelaysMs: delays } : {}),\n ...(scope ? { headers: { \"x-mockingbird-scope\": scope } } : {}),\n },\n }\n : {}),\n ...(common.adminKey !== undefined ? { adminKey: common.adminKey } : {}),\n ...(common.seed !== undefined ? { seed: common.seed } : {}),\n ...(common.onLog ? { onLog: common.onLog } : {}),\n })\n },\n banner(runtime) {\n const junction = runtime as JunctionRuntime\n const api = junction.instance()\n const info = api.corpusInfo()\n return [\n info\n ? `corpus ${info.label}: ${info.observations} observations, ${info.zips} ZIPs, ${info.labTests} lab tests, ${info.labAccounts} lab accounts (recorded ${info.recordedAt} from ${info.source})`\n : \"corpus none: synthetic catalog and coverage\",\n `geo ${api.geoMode}${api.geoMode === \"corpus\" ? \" (unknown ZIPs answer 424 MOCKINGBIRD_UNKNOWN_ZIP)\" : \"\"}`,\n `lab accounts: ${api.labAccounts().length}`,\n `webhooks: ${junction.webhooks ? \"signed delivery on\" : \"off\"}`,\n \"auth: any x-vital-api-key value (e.g. sk_us_mockingbird)\",\n ]\n },\n}\n", "import { readFile } from \"node:fs/promises\"\nimport { type ParseArgsConfig, parseArgs } from \"node:util\"\nimport type { RequestLog, ServiceInstance, ServiceRuntime } from \"@crvouga/mockingbird-service\"\nimport { type Listening, listen } from \"./listen.js\"\n\nexport type CliOption = {\n type: \"string\" | \"boolean\"\n description: string\n /** Shown in help; the value placeholder, e.g. `<port>`. */\n value?: string\n default?: string | boolean\n}\n\nexport type CliValues = Record<string, string | boolean | undefined>\n\nexport type CliCommand = {\n summary: string\n usage?: string\n options?: Record<string, CliOption>\n /** Resolves to an exit code; a server command resolves only when it stops. */\n run(values: CliValues, positionals: string[]): Promise<number>\n}\n\nexport type CliSpec = {\n bin: string\n description: string\n commands: Record<string, CliCommand>\n}\n\nconst optionHelp = (options: Record<string, CliOption>): string[] =>\n Object.entries(options).map(([name, option]) => {\n const flag = `--${name}${option.type === \"string\" ? ` ${option.value ?? \"<value>\"}` : \"\"}`\n const fallback = option.default !== undefined ? ` (default: ${String(option.default)})` : \"\"\n return ` ${flag.padEnd(30)} ${option.description}${fallback}`\n })\n\nconst help = (spec: CliSpec): string =>\n [\n `${spec.bin} \u2014 ${spec.description}`,\n \"\",\n \"Usage:\",\n ` ${spec.bin} <command> [options]`,\n \"\",\n \"Commands:\",\n ...Object.entries(spec.commands).map(([name, c]) => ` ${name.padEnd(30)} ${c.summary}`),\n \"\",\n `Run \\`${spec.bin} <command> --help\\` for a command's options.`,\n ].join(\"\\n\")\n\nconst commandHelp = (spec: CliSpec, name: string, command: CliCommand): string =>\n [\n `${spec.bin} ${name} \u2014 ${command.summary}`,\n \"\",\n \"Usage:\",\n ` ${command.usage ?? `${spec.bin} ${name} [options]`}`,\n ...(command.options ? [\"\", \"Options:\", ...optionHelp(command.options)] : []),\n ].join(\"\\n\")\n\n/** Parse `argv` against `spec` and run the chosen command. Resolves to an exit code. */\nexport const runCli = async (spec: CliSpec, argv: string[]): Promise<number> => {\n // Two-word commands (`corpus pull`) win over one-word ones.\n const pair = argv.length >= 2 ? `${argv[0]} ${argv[1]}` : undefined\n const words = pair !== undefined && spec.commands[pair] ? 2 : 1\n const name = words === 2 ? pair : argv[0]\n const rest = argv.slice(words)\n if (name === undefined || name === \"--help\" || name === \"-h\" || name === \"help\") {\n console.log(help(spec))\n return 0\n }\n const command = spec.commands[name]\n if (!command) {\n console.error(`${spec.bin}: unknown command ${JSON.stringify(name)}\\n\\n${help(spec)}`)\n return 2\n }\n if (rest.includes(\"--help\") || rest.includes(\"-h\")) {\n console.log(commandHelp(spec, name, command))\n return 0\n }\n let parsed: { values: CliValues; positionals: string[] }\n try {\n parsed = parseArgs({\n args: rest,\n allowPositionals: true,\n strict: true,\n options: Object.fromEntries(\n Object.entries(command.options ?? {}).map(([key, option]) => [\n key,\n {\n type: option.type,\n ...(option.default !== undefined ? { default: option.default } : {}),\n },\n ]),\n ) as ParseArgsConfig[\"options\"],\n }) as { values: CliValues; positionals: string[] }\n } catch (error) {\n console.error(\n `${spec.bin} ${name}: ${error instanceof Error ? error.message : String(error)}\\n\\n${commandHelp(spec, name, command)}`,\n )\n return 2\n }\n return command.run(parsed.values, parsed.positionals)\n}\n\n// \u2500\u2500 serve \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type LogFormat = \"pretty\" | \"json\" | \"off\"\n\nexport type CommonServeOptions = {\n adminKey: string | undefined\n seed: string | undefined\n onLog: ((entry: RequestLog) => void) | undefined\n}\n\n/**\n * What a service contributes to `serve`: how to build its runtime from CLI flags,\n * and what to say at startup. Every service's `./server` entry exports one as\n * `serveTarget`, which is also how `serve --config` finds services by name.\n */\nexport type ServeTarget = {\n name: string\n defaultPort: number\n /** Serve flags beyond the common ones. */\n options?: Record<string, CliOption>\n create(\n values: CliValues,\n common: CommonServeOptions,\n ): Promise<ServiceRuntime<ServiceInstance>> | ServiceRuntime<ServiceInstance>\n /** Startup lines after the listen address, e.g. the loaded corpus version. */\n banner?(runtime: ServiceRuntime<ServiceInstance>): string[]\n}\n\nconst COMMON_SERVE_OPTIONS: Record<string, CliOption> = {\n port: { type: \"string\", value: \"<port>\", description: \"Port to listen on\" },\n host: { type: \"string\", value: \"<host>\", description: \"Interface to bind\", default: \"127.0.0.1\" },\n \"admin-key\": {\n type: \"string\",\n value: \"<key>\",\n description: \"Require x-mockingbird-admin-key on /__admin/* (env MOCKINGBIRD_ADMIN_KEY)\",\n },\n seed: { type: \"string\", value: \"<seed>\", description: \"Seed for every random choice\" },\n log: {\n type: \"string\",\n value: \"<pretty|json|off>\",\n description: \"Request log format\",\n default: \"pretty\",\n },\n config: {\n type: \"string\",\n value: \"<file>\",\n description: \"Serve every service in a mockingbird.json config instead\",\n },\n}\n\nconst formatLog = (format: LogFormat) => {\n if (format === \"off\") return undefined\n if (format === \"json\") return (entry: RequestLog) => console.log(JSON.stringify(entry))\n return (entry: RequestLog) => {\n const op = entry.operationId ?? (entry.unmatched ? \"UNMATCHED\" : \"-\")\n const ns = entry.namespace === \"default\" ? \"\" : ` [${entry.namespace}]`\n const fault = entry.faultId ? ` fault=${entry.faultId}` : \"\"\n console.log(\n `${entry.service} ${entry.method} ${entry.path} ${entry.status} ${op} ${entry.durationMs}ms${ns}${fault}`,\n )\n }\n}\n\nconst asString = (value: string | boolean | undefined): string | undefined =>\n typeof value === \"string\" ? value : undefined\n\n/** A service entry in `mockingbird.json`. */\nexport type ConfigService = {\n port?: number\n host?: string\n adminKey?: string\n seed?: string\n /** Service-specific serve flags, by long name: `{ \"webhook-url\": \"\u2026\" }`. */\n options?: Record<string, string | boolean>\n}\n\nexport type MockingbirdConfig = {\n /** Keyed by service name: `junction` loads `@crvouga/mockingbird-service-junction`. */\n services: Record<string, ConfigService>\n log?: LogFormat\n}\n\nconst loadTarget = async (name: string, own: ServeTarget): Promise<ServeTarget> => {\n if (name === own.name) return own\n const specifier = `@crvouga/mockingbird-service-${name}/server`\n try {\n const mod = (await import(specifier)) as { serveTarget?: ServeTarget }\n if (!mod.serveTarget) throw new Error(`${specifier} exports no serveTarget`)\n return mod.serveTarget\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n throw new Error(\n `cannot load service \"${name}\": ${reason}. Install @crvouga/mockingbird-service-${name}.`,\n )\n }\n}\n\nconst start = async (\n target: ServeTarget,\n values: CliValues,\n config: { port: number; host: string; adminKey?: string; seed?: string; log: LogFormat },\n): Promise<Listening> => {\n const runtime = await target.create(values, {\n adminKey: config.adminKey,\n seed: config.seed,\n onLog: formatLog(config.log),\n })\n const listening = await listen(runtime, { port: config.port, host: config.host })\n console.log(`${target.name} mock listening on ${listening.url}`)\n console.log(`${target.name} health: GET ${listening.url}/health`)\n console.log(\n `${target.name} admin: ${listening.url}/__admin (${config.adminKey ? \"x-mockingbird-admin-key required\" : \"open \u2014 pass --admin-key to lock\"})`,\n )\n for (const line of target.banner?.(runtime) ?? []) console.log(`${target.name} ${line}`)\n return listening\n}\n\nconst untilSignal = async (servers: Listening[]): Promise<number> =>\n new Promise((resolve) => {\n const stop = async () => {\n await Promise.allSettled(servers.map((s) => s.close()))\n resolve(0)\n }\n process.once(\"SIGINT\", stop)\n process.once(\"SIGTERM\", stop)\n })\n\n/** The standard `serve` command for a service, including multi-service `--config`. */\nexport const serveCommand = (target: ServeTarget): CliCommand => ({\n summary: `Serve the ${target.name} mock over HTTP`,\n options: { ...COMMON_SERVE_OPTIONS, ...target.options },\n async run(values) {\n const log = (asString(values.log) ?? \"pretty\") as LogFormat\n if (![\"pretty\", \"json\", \"off\"].includes(log)) {\n console.error(`--log must be pretty, json or off (got ${log})`)\n return 2\n }\n const configPath = asString(values.config)\n if (configPath !== undefined) {\n const config = JSON.parse(await readFile(configPath, \"utf8\")) as MockingbirdConfig\n const servers: Listening[] = []\n try {\n for (const [name, entry] of Object.entries(config.services ?? {})) {\n const each = await loadTarget(name, target)\n servers.push(\n await start(each, entry.options ?? {}, {\n port: entry.port ?? each.defaultPort,\n host: entry.host ?? \"127.0.0.1\",\n ...(entry.adminKey !== undefined ? { adminKey: entry.adminKey } : {}),\n ...(entry.seed !== undefined ? { seed: entry.seed } : {}),\n log: config.log ?? log,\n }),\n )\n }\n } catch (error) {\n await Promise.allSettled(servers.map((s) => s.close()))\n console.error(error instanceof Error ? error.message : String(error))\n return 1\n }\n return untilSignal(servers)\n }\n const port = asString(values.port)\n const adminKey = asString(values[\"admin-key\"]) ?? process.env.MOCKINGBIRD_ADMIN_KEY\n const seed = asString(values.seed)\n let listening: Listening\n try {\n listening = await start(target, values, {\n port: port === undefined ? target.defaultPort : Number.parseInt(port, 10),\n host: asString(values.host) ?? \"127.0.0.1\",\n ...(adminKey !== undefined ? { adminKey } : {}),\n ...(seed !== undefined ? { seed } : {}),\n log,\n })\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n return 1\n }\n return untilSignal([listening])\n },\n})\n", "import { createServer } from \"node:http\"\nimport type { FetchAPI } from \"@crvouga/mockingbird-core\"\n\n/** Options for {@link serve}. */\nexport type NodeServeOptions = {\n port?: number\n host?: string\n}\n\n/**\n * Serve any Mockingbird {@link FetchAPI} over `node:http`.\n * Port defaults to `0`, so the OS assigns an ephemeral port (read from `server.address()`).\n */\nexport const serve = async (api: FetchAPI, options: NodeServeOptions = {}) => {\n const server = createServer(async (req, res) => {\n const chunks: Buffer[] = []\n for await (const chunk of req) {\n chunks.push(Buffer.from(chunk))\n }\n const body = Buffer.concat(chunks)\n const address = server.address()\n const port = typeof address === \"object\" && address !== null ? address.port : undefined\n const base = `http://${req.headers.host ?? `localhost:${port ?? 80}`}`\n const raw = req.url ?? \"/\"\n const url = new URL(raw.replace(/^\\/+/, \"/\"), base)\n const method = req.method ?? \"GET\"\n const init: RequestInit = { method, headers: req.headers as Record<string, string> }\n if (method !== \"GET\" && method !== \"HEAD\" && body.length > 0) {\n init.body = body\n }\n const request = new Request(url, init)\n const response = await api.fetch(request)\n // Headers#entries() joins repeated headers; Set-Cookie must stay one header per cookie.\n const headers: Record<string, string | string[]> = Object.fromEntries(response.headers)\n const cookies = response.headers.getSetCookie()\n if (cookies.length > 0) headers[\"set-cookie\"] = cookies\n res.writeHead(response.status, headers)\n res.end(Buffer.from(await response.arrayBuffer()))\n })\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject)\n server.listen(options.port ?? 0, options.host, resolve)\n })\n return server\n}\n", "import type { Server } from \"node:http\"\nimport type { FetchAPI } from \"@crvouga/mockingbird-core\"\nimport { serve } from \"./serve.js\"\n\nexport type ListenOptions = {\n /** Default `0`: the OS picks a free port. */\n port?: number\n /** Default `127.0.0.1`: a mock should not be reachable off the machine by accident. */\n host?: string\n}\n\n/** A running server, with the address it actually bound. */\nexport type Listening = {\n url: string\n port: number\n host: string\n server: Server\n close(): Promise<void>\n}\n\nexport const listen = async (api: FetchAPI, options: ListenOptions = {}): Promise<Listening> => {\n const host = options.host ?? \"127.0.0.1\"\n const server = await serve(api, { port: options.port ?? 0, host })\n const address = server.address()\n const port = typeof address === \"object\" && address !== null ? address.port : (options.port ?? 0)\n const shown = host.includes(\":\") ? `[${host}]` : host\n return {\n url: `http://${shown}:${port}`,\n port,\n host,\n server,\n close: () =>\n new Promise<void>((resolve, reject) => {\n // Stop accepting first, then drop keep-alive sockets so close() can finish.\n server.close((error) => {\n if (error && (error as { code?: string }).code !== \"ERR_SERVER_NOT_RUNNING\") reject(error)\n else resolve()\n })\n server.closeAllConnections?.()\n }),\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["readFile", "readFile", "createServer", "asString"]
7
- }