@haste-health/client 0.15.3

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/src/index.ts ADDED
@@ -0,0 +1,555 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { code, id } from "@haste-health/fhir-types/r4/types";
3
+ import {
4
+ AllResourceTypes,
5
+ FHIR_VERSION,
6
+ Resource,
7
+ ResourceType,
8
+ } from "@haste-health/fhir-types/versions";
9
+ import type { IOperation } from "@haste-health/operation-execution";
10
+ import { OperationError, outcomeError } from "@haste-health/operation-outcomes";
11
+
12
+ import type { FHIRClientAsync, InvokeParameter } from "./interface.js";
13
+ import { MiddlewareAsync } from "./middleware/index.js";
14
+ import type {
15
+ AllInteractions,
16
+ FHIRRequest,
17
+ FHIRResponse,
18
+ } from "./types/index.js";
19
+ import type { ParsedParameter } from "./url.js";
20
+ import { parseQuery } from "./url.js";
21
+
22
+ export class AsynchronousClient<CTX> implements FHIRClientAsync<CTX> {
23
+ private readonly _middleware: MiddlewareAsync<CTX>;
24
+ constructor(middleware: MiddlewareAsync<CTX>) {
25
+ this._middleware = middleware;
26
+ }
27
+
28
+ get middleware(): MiddlewareAsync<CTX> {
29
+ return this._middleware;
30
+ }
31
+
32
+ async request<Version extends FHIR_VERSION, I extends AllInteractions>(
33
+ ctx: CTX,
34
+ request: FHIRRequest<Version, I>
35
+ ): Promise<FHIRResponse<Version, I | "error">> {
36
+ const res = await this._middleware({ ctx, request });
37
+
38
+ if (!res.response) throw new Error("No Response was returned.");
39
+ if (res.response.fhirVersion !== res.request.fhirVersion)
40
+ throw new Error(
41
+ `FHIR Version mismatch '${res.response.fhirVersion}' !== '${res.request.fhirVersion}'`
42
+ );
43
+
44
+ return res.response as FHIRResponse<Version, I | "error">;
45
+ }
46
+ async capabilities<FHIRVersion extends FHIR_VERSION>(
47
+ ctx: CTX,
48
+ fhirVersion: FHIRVersion
49
+ ): Promise<Resource<FHIRVersion, "CapabilityStatement">> {
50
+ const response = await this.request<typeof fhirVersion, "capabilities">(
51
+ ctx,
52
+ {
53
+ fhirVersion,
54
+ type: "capabilities-request",
55
+ level: "system",
56
+ }
57
+ );
58
+ if (response.type !== "capabilities-response")
59
+ throw new Error("Unexpected response type");
60
+ return response.body as Resource<FHIRVersion, "CapabilityStatement">;
61
+ }
62
+ async search_system<FHIRVersion extends FHIR_VERSION>(
63
+ ctx: CTX,
64
+ fhirVersion: FHIRVersion,
65
+ parameters: ParsedParameter<string | number>[] | string
66
+ ): Promise<{
67
+ total?: number;
68
+ resources: Resource<FHIRVersion, ResourceType<FHIRVersion>>[];
69
+ }> {
70
+ const parsedParameters: ParsedParameter<string | number>[] =
71
+ typeof parameters === "string" || parameters === undefined
72
+ ? parseQuery(parameters)
73
+ : parameters;
74
+ const response = await this.request(ctx, {
75
+ fhirVersion,
76
+ type: "search-request",
77
+ level: "system",
78
+ parameters: parsedParameters,
79
+ });
80
+ if (response.type !== "search-response")
81
+ throw new Error("Unexpected response type");
82
+ return {
83
+ total: response.body.total,
84
+ resources: (response.body.entry ?? []).map((e) => e.resource) as Resource<
85
+ FHIRVersion,
86
+ ResourceType<FHIRVersion>
87
+ >[],
88
+ };
89
+ }
90
+ async search_type<
91
+ Version extends FHIR_VERSION,
92
+ T extends ResourceType<Version>
93
+ >(
94
+ ctx: CTX,
95
+ fhirVersion: Version,
96
+ type: T,
97
+ parameters: ParsedParameter<string | number>[] | string
98
+ ): Promise<{
99
+ total?: number;
100
+ resources: Resource<Version, T>[];
101
+ }> {
102
+ const parsedParameters: ParsedParameter<string | number>[] =
103
+ typeof parameters === "string" || parameters === undefined
104
+ ? parseQuery(parameters)
105
+ : parameters;
106
+ const response = await this.request<typeof fhirVersion, "search">(ctx, {
107
+ fhirVersion,
108
+ type: "search-request",
109
+ level: "type",
110
+ resource: type,
111
+ parameters: parsedParameters,
112
+ });
113
+ if (response.type !== "search-response")
114
+ throw new Error(`Unexpected response type '${response.type}'`);
115
+ return {
116
+ total: response.body.total,
117
+ resources: (response.body.entry ?? []).map((e) => e.resource) as Resource<
118
+ Version,
119
+ T
120
+ >[],
121
+ };
122
+ }
123
+ async create<
124
+ FHIRVersion extends FHIR_VERSION,
125
+ T extends Resource<FHIRVersion, AllResourceTypes>
126
+ >(ctx: CTX, fhirVersion: FHIRVersion, resource: T): Promise<T> {
127
+ const response = await this.request<typeof fhirVersion, "create">(ctx, {
128
+ fhirVersion,
129
+ type: "create-request",
130
+ level: "type",
131
+ resource: resource.resourceType as ResourceType<FHIRVersion>,
132
+ body: resource as any,
133
+ });
134
+ if (response.type !== "create-response")
135
+ throw new Error("Unexpected response type");
136
+ return response.body as any as T;
137
+ }
138
+ async conditionalUpdate<
139
+ FHIRVersion extends FHIR_VERSION,
140
+ T extends ResourceType<FHIRVersion>
141
+ >(
142
+ ctx: CTX,
143
+ fhirVersion: FHIRVersion,
144
+ resourceType: T,
145
+ parameters: ParsedParameter<string | number>[] | string,
146
+ resource: Resource<FHIRVersion, T>
147
+ ): Promise<Resource<FHIRVersion, T>> {
148
+ const parsedParameters: ParsedParameter<string | number>[] =
149
+ typeof parameters === "string"
150
+ ? parseQuery(parameters)
151
+ : parameters
152
+ ? parameters
153
+ : [];
154
+ const response = await this.request<typeof fhirVersion, "update">(ctx, {
155
+ fhirVersion,
156
+ type: "update-request",
157
+ level: "type",
158
+ resource: resourceType,
159
+ parameters: parsedParameters,
160
+ body: resource,
161
+ });
162
+ if (response.type !== "update-response")
163
+ throw new Error("Unexpected response type");
164
+ return response.body as Resource<FHIRVersion, T>;
165
+ }
166
+ async update<
167
+ FHIRVersion extends FHIR_VERSION,
168
+ T extends ResourceType<FHIRVersion>
169
+ >(
170
+ ctx: CTX,
171
+ fhirVersion: FHIRVersion,
172
+ resourceType: T,
173
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>,
174
+ resource: Resource<FHIRVersion, T>
175
+ ): Promise<Resource<FHIRVersion, T>> {
176
+ if (resource.id === undefined)
177
+ throw new Error("Cannot update resource without id");
178
+ const response = await this.request<typeof fhirVersion, "update">(ctx, {
179
+ fhirVersion,
180
+ type: "update-request",
181
+ level: "instance",
182
+ resource: resourceType,
183
+ id,
184
+ body: resource,
185
+ });
186
+ if (response.type !== "update-response")
187
+ throw new Error("Unexpected response type");
188
+ return response.body as Resource<FHIRVersion, T>;
189
+ }
190
+ async patch<
191
+ FHIRVersion extends FHIR_VERSION,
192
+ T extends ResourceType<FHIRVersion>
193
+ >(
194
+ ctx: CTX,
195
+ fhirVersion: FHIRVersion,
196
+ resourceType: T,
197
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>,
198
+ patches: any
199
+ ): Promise<Resource<FHIRVersion, T>> {
200
+ const response = await this.request<typeof fhirVersion, "patch">(ctx, {
201
+ fhirVersion,
202
+ type: "patch-request",
203
+ level: "instance",
204
+ resource: resourceType,
205
+ id: id,
206
+ body: patches,
207
+ });
208
+ if (response.type !== "patch-response")
209
+ throw new Error("Unexpected response type");
210
+ return response.body as Resource<FHIRVersion, T>;
211
+ }
212
+ async read<
213
+ FHIRVersion extends FHIR_VERSION,
214
+ T extends ResourceType<FHIRVersion>
215
+ >(
216
+ ctx: CTX,
217
+ fhirVersion: FHIRVersion,
218
+ resourceType: T,
219
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>
220
+ ): Promise<Resource<FHIRVersion, T> | undefined> {
221
+ const response = await this.request<typeof fhirVersion, "read">(ctx, {
222
+ fhirVersion,
223
+ type: "read-request",
224
+ level: "instance",
225
+ resource: resourceType,
226
+ id: id,
227
+ });
228
+ if (response.type !== "read-response")
229
+ throw new Error("Unexpected response type");
230
+ return response.body as Resource<FHIRVersion, T> | undefined;
231
+ }
232
+ async vread<
233
+ FHIRVersion extends FHIR_VERSION,
234
+ T extends ResourceType<FHIRVersion>
235
+ >(
236
+ ctx: CTX,
237
+ fhirVersion: FHIRVersion,
238
+ resourceType: T,
239
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>,
240
+ versionId: NonNullable<
241
+ Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]
242
+ >
243
+ ): Promise<Resource<FHIRVersion, T> | undefined> {
244
+ const response = await this.request<typeof fhirVersion, "vread">(ctx, {
245
+ fhirVersion,
246
+ type: "vread-request",
247
+ level: "instance",
248
+ resource: resourceType,
249
+ id: id,
250
+ versionId: versionId,
251
+ });
252
+ if (response.type !== "vread-response")
253
+ throw new Error("Unexpected response type");
254
+ return response.body as Resource<FHIRVersion, T> | undefined;
255
+ }
256
+ async delete_instance<
257
+ FHIRVersion extends FHIR_VERSION,
258
+ T extends ResourceType<FHIRVersion>
259
+ >(
260
+ ctx: CTX,
261
+ fhirVersion: FHIRVersion,
262
+ resourceType: T,
263
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>
264
+ ): Promise<void> {
265
+ const response = await this.request<typeof fhirVersion, "delete">(ctx, {
266
+ fhirVersion,
267
+ type: "delete-request",
268
+ level: "instance",
269
+ resource: resourceType,
270
+ id: id,
271
+ });
272
+ if (response.type !== "delete-response")
273
+ throw new Error("Unexpected response type");
274
+ }
275
+ async delete_type<
276
+ FHIRVersion extends FHIR_VERSION,
277
+ T extends ResourceType<FHIRVersion>
278
+ >(
279
+ ctx: CTX,
280
+ fhirVersion: FHIRVersion,
281
+ resourceType: T,
282
+ parameters?: ParsedParameter<string | number>[] | string
283
+ ): Promise<void> {
284
+ const parsedParameters: ParsedParameter<string | number>[] =
285
+ typeof parameters === "string"
286
+ ? parseQuery(parameters)
287
+ : parameters
288
+ ? parameters
289
+ : [];
290
+ const response = await this.request<typeof fhirVersion, "delete">(ctx, {
291
+ fhirVersion,
292
+ type: "delete-request",
293
+ level: "type",
294
+ resource: resourceType,
295
+ parameters: parsedParameters,
296
+ });
297
+ if (response.type !== "delete-response")
298
+ throw new Error("Unexpected response type");
299
+ }
300
+ async delete_system<FHIRVersion extends FHIR_VERSION>(
301
+ ctx: CTX,
302
+ fhirVersion: FHIRVersion,
303
+ parameters?: ParsedParameter<string | number>[] | string
304
+ ): Promise<void> {
305
+ const parsedParameters: ParsedParameter<string | number>[] =
306
+ typeof parameters === "string"
307
+ ? parseQuery(parameters)
308
+ : parameters
309
+ ? parameters
310
+ : [];
311
+ const response = await this.request<typeof fhirVersion, "delete">(ctx, {
312
+ fhirVersion,
313
+ type: "delete-request",
314
+ level: "system",
315
+ parameters: parsedParameters,
316
+ });
317
+ if (response.type !== "delete-response")
318
+ throw new Error("Unexpected response type");
319
+ }
320
+ async history_system<FHIRVersion extends FHIR_VERSION>(
321
+ ctx: CTX,
322
+ fhirVersion: FHIRVersion,
323
+ parameters?: ParsedParameter<string | number>[] | string
324
+ ): Promise<NonNullable<Resource<FHIRVersion, "Bundle">["entry"]>> {
325
+ const parsedParameters: ParsedParameter<string | number>[] =
326
+ typeof parameters === "string"
327
+ ? parseQuery(parameters)
328
+ : parameters
329
+ ? parameters
330
+ : [];
331
+
332
+ const response = await this.request(ctx, {
333
+ fhirVersion,
334
+ type: "history-request",
335
+ level: "system",
336
+ parameters: parsedParameters,
337
+ });
338
+ if (response.type !== "history-response")
339
+ throw new Error("Unexpected response type");
340
+ return response.body.entry ?? [];
341
+ }
342
+ async history_type<
343
+ FHIRVersion extends FHIR_VERSION,
344
+ T extends ResourceType<FHIRVersion>
345
+ >(
346
+ ctx: CTX,
347
+ fhirVersion: FHIRVersion,
348
+ resourceType: T,
349
+ parameters?: ParsedParameter<string | number>[] | string
350
+ ): Promise<NonNullable<Resource<FHIRVersion, "Bundle">["entry"]>> {
351
+ const parsedParameters: ParsedParameter<string | number>[] =
352
+ typeof parameters === "string"
353
+ ? parseQuery(parameters)
354
+ : parameters
355
+ ? parameters
356
+ : [];
357
+ const response = await this.request<typeof fhirVersion, "history">(ctx, {
358
+ fhirVersion,
359
+ type: "history-request",
360
+ level: "type",
361
+ resource: resourceType,
362
+ parameters: parsedParameters,
363
+ });
364
+ if (response.type !== "history-response")
365
+ throw new Error("Unexpected response type");
366
+ return response.body.entry ?? [];
367
+ }
368
+ async history_instance<
369
+ FHIRVersion extends FHIR_VERSION,
370
+ T extends ResourceType<FHIRVersion>
371
+ >(
372
+ ctx: CTX,
373
+ fhirVersion: FHIRVersion,
374
+ resourceType: T,
375
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>,
376
+ parameters?: ParsedParameter<string | number>[] | string
377
+ ): Promise<NonNullable<Resource<FHIRVersion, "Bundle">["entry"]>> {
378
+ const parsedParameters: ParsedParameter<string | number>[] =
379
+ typeof parameters === "string"
380
+ ? parseQuery(parameters)
381
+ : parameters
382
+ ? parameters
383
+ : [];
384
+ const response = await this.request<typeof fhirVersion, "history">(ctx, {
385
+ fhirVersion,
386
+ type: "history-request",
387
+ level: "instance",
388
+ resource: resourceType,
389
+ id: id as id,
390
+ parameters: parsedParameters,
391
+ });
392
+ if (response.type !== "history-response")
393
+ throw new Error("Unexpected response type");
394
+ return response.body.entry ?? [];
395
+ }
396
+ async invoke_system<
397
+ FHIRVersion extends FHIR_VERSION,
398
+ Op extends IOperation<any, any> | code,
399
+ Input extends InvokeParameter<FHIRVersion, Op, "Input">,
400
+ Output extends InvokeParameter<FHIRVersion, Op, "Output">
401
+ >(op: Op, ctx: CTX, fhirVersion: FHIRVersion, input: Input): Promise<Output> {
402
+ if (typeof op === "string") {
403
+ const response = await this.request<typeof fhirVersion, "invoke">(ctx, {
404
+ fhirVersion,
405
+ type: "invoke-request",
406
+ level: "system",
407
+ operation: op,
408
+ body: input,
409
+ });
410
+
411
+ return (response as unknown as Record<string, unknown>).body as Output;
412
+ }
413
+
414
+ const response = await this.request<typeof fhirVersion, "invoke">(ctx, {
415
+ fhirVersion,
416
+ type: "invoke-request",
417
+ level: "system",
418
+ operation: op.code,
419
+ body: op.parseToParameters("in", input) as Resource<
420
+ FHIRVersion,
421
+ "Parameters"
422
+ >,
423
+ });
424
+
425
+ if (response.type !== "invoke-response")
426
+ throw new Error("Unexpected response type");
427
+ return op.parseToObject("out", response.body);
428
+ }
429
+ async invoke_type<
430
+ FHIRVersion extends FHIR_VERSION,
431
+ Op extends IOperation<any, any> | code,
432
+ Input extends InvokeParameter<FHIRVersion, Op, "Input">,
433
+ Output extends InvokeParameter<FHIRVersion, Op, "Output">,
434
+ T extends ResourceType<FHIRVersion>
435
+ >(
436
+ op: Op,
437
+ ctx: CTX,
438
+ fhirVersion: FHIRVersion,
439
+ resourceType: T,
440
+ input: Input
441
+ ): Promise<Output> {
442
+ if (typeof op === "string") {
443
+ const response = await this.request<typeof fhirVersion, "invoke">(ctx, {
444
+ fhirVersion,
445
+ type: "invoke-request",
446
+ level: "type",
447
+ resource: resourceType,
448
+ operation: op,
449
+ body: input,
450
+ });
451
+
452
+ return (response as unknown as Record<string, unknown>).body as Output;
453
+ }
454
+
455
+ const response = await this.request<typeof fhirVersion, "invoke">(ctx, {
456
+ fhirVersion,
457
+ type: "invoke-request",
458
+ level: "type",
459
+ operation: op.code,
460
+ resource: resourceType,
461
+ body: op.parseToParameters("in", input),
462
+ });
463
+
464
+ if (response.type !== "invoke-response")
465
+ throw new Error("Unexpected response type");
466
+ return op.parseToObject("out", response.body);
467
+ }
468
+ async invoke_instance<
469
+ FHIRVersion extends FHIR_VERSION,
470
+ Op extends IOperation<any, any> | code,
471
+ Input extends InvokeParameter<FHIRVersion, Op, "Input">,
472
+ Output extends InvokeParameter<FHIRVersion, Op, "Output">,
473
+ T extends ResourceType<FHIRVersion>
474
+ >(
475
+ op: Op,
476
+ ctx: CTX,
477
+ fhirVersion: FHIRVersion,
478
+ resourceType: T,
479
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>,
480
+ input: Input
481
+ ): Promise<Output> {
482
+ if (typeof op === "string") {
483
+ const response = await this.request<typeof fhirVersion, "invoke">(ctx, {
484
+ fhirVersion,
485
+ type: "invoke-request",
486
+ level: "instance",
487
+ operation: op,
488
+ resource: resourceType,
489
+ id,
490
+ body: input,
491
+ });
492
+
493
+ return (response as unknown as Record<string, unknown>).body as Output;
494
+ }
495
+
496
+ const response = await this.request<typeof fhirVersion, "invoke">(ctx, {
497
+ fhirVersion,
498
+ type: "invoke-request",
499
+ level: "instance",
500
+ operation: op.code,
501
+ resource: resourceType,
502
+ id,
503
+ body: op.parseToParameters("in", input),
504
+ });
505
+ if (response.type !== "invoke-response")
506
+ throw new Error("Unexpected response type");
507
+ return op.parseToObject("out", response.body);
508
+ }
509
+ async transaction<FHIRVersion extends FHIR_VERSION>(
510
+ ctx: CTX,
511
+ fhirVersion: FHIRVersion,
512
+ bundle: Resource<FHIRVersion, "Bundle">
513
+ ): Promise<Resource<FHIRVersion, "Bundle">> {
514
+ if (bundle.type !== "transaction")
515
+ throw new OperationError(
516
+ outcomeError("invalid", "Bundle must be of type 'transaction'")
517
+ );
518
+ const response = await this.request(ctx, {
519
+ fhirVersion,
520
+ type: "transaction-request",
521
+ level: "system",
522
+ body: bundle,
523
+ });
524
+
525
+ if (response.type !== "transaction-response") {
526
+ throw new OperationError(
527
+ outcomeError("invalid", "response type must be 'transaction-response'")
528
+ );
529
+ }
530
+ return response.body as Resource<FHIRVersion, "Bundle">;
531
+ }
532
+ async batch<FHIRVersion extends FHIR_VERSION>(
533
+ ctx: CTX,
534
+ fhirVersion: FHIRVersion,
535
+ bundle: Resource<FHIRVersion, "Bundle">
536
+ ): Promise<Resource<FHIRVersion, "Bundle">> {
537
+ if (bundle.type !== "batch")
538
+ throw new OperationError(
539
+ outcomeError("invalid", "Bundle must be of type 'batch'")
540
+ );
541
+ const response = await this.request(ctx, {
542
+ fhirVersion,
543
+ type: "batch-request",
544
+ level: "system",
545
+ body: bundle,
546
+ });
547
+
548
+ if (response.type !== "batch-response") {
549
+ throw new OperationError(
550
+ outcomeError("invalid", "response type must be 'batch-response'")
551
+ );
552
+ }
553
+ return response.body as Resource<FHIRVersion, "Bundle">;
554
+ }
555
+ }