@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.
@@ -0,0 +1,203 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { code } from "@haste-health/fhir-types/lib/generated/r4/types";
3
+ import {
4
+ AllResourceTypes,
5
+ FHIR_VERSION,
6
+ Resource,
7
+ ResourceType,
8
+ } from "@haste-health/fhir-types/versions";
9
+ import type { IOperation, OPMetadata } from "@haste-health/operation-execution";
10
+
11
+ import { MiddlewareAsync } from "./middleware/index.js";
12
+ import type {
13
+ AllInteractions,
14
+ FHIRRequest,
15
+ FHIRResponse,
16
+ } from "./types/index.js";
17
+ import type { ParsedParameter } from "./url.js";
18
+
19
+ export type FHIRClient<CTX> = FHIRClientAsync<CTX>;
20
+
21
+ export type InvokeParameter<
22
+ FHIRVersion extends FHIR_VERSION,
23
+ Op,
24
+ direction extends "Output" | "Input"
25
+ > = Op extends IOperation<any, any>
26
+ ? OPMetadata<Op>[direction]
27
+ : Resource<FHIRVersion, "Parameters">;
28
+
29
+ export interface FHIRClientAsync<CTX> {
30
+ request<Version extends FHIR_VERSION, I extends AllInteractions>(
31
+ ctx: CTX,
32
+ request: FHIRRequest<Version, I>
33
+ ): Promise<FHIRResponse<Version, I | "error">>;
34
+ middleware: MiddlewareAsync<CTX>;
35
+ capabilities<FHIRVersion extends FHIR_VERSION>(
36
+ ctx: CTX,
37
+ fhirVersion: FHIRVersion
38
+ ): Promise<Resource<FHIRVersion, "CapabilityStatement">>;
39
+ search_system<FHIRVersion extends FHIR_VERSION>(
40
+ ctx: CTX,
41
+ fhirVersion: FHIRVersion,
42
+ parameters: ParsedParameter<string | number>[] | string
43
+ ): Promise<{
44
+ total?: number;
45
+ resources: Resource<FHIRVersion, ResourceType<FHIRVersion>>[];
46
+ }>;
47
+ search_type<
48
+ FHIRVersion extends FHIR_VERSION,
49
+ T extends ResourceType<FHIRVersion>
50
+ >(
51
+ ctx: CTX,
52
+ fhirVersion: FHIRVersion,
53
+ type: T,
54
+ parameters: ParsedParameter<string | number>[] | string
55
+ ): Promise<{
56
+ total?: number;
57
+ resources: Resource<FHIRVersion, T>[];
58
+ }>;
59
+ create<
60
+ FHIRVersion extends FHIR_VERSION,
61
+ Value extends Resource<FHIRVersion, AllResourceTypes>
62
+ >(
63
+ ctx: CTX,
64
+ fhirVersion: FHIRVersion,
65
+ resource: Value
66
+ ): Promise<Value>;
67
+
68
+ update<FHIRVersion extends FHIR_VERSION, T extends ResourceType<FHIRVersion>>(
69
+ ctx: CTX,
70
+ fhirVersion: FHIRVersion,
71
+ resourceType: T,
72
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>,
73
+ resource: Resource<FHIRVersion, T>
74
+ ): Promise<Resource<FHIRVersion, T>>;
75
+ conditionalUpdate<
76
+ FHIRVersion extends FHIR_VERSION,
77
+ T extends ResourceType<FHIRVersion>
78
+ >(
79
+ ctx: CTX,
80
+ fhirVersion: FHIRVersion,
81
+ resourceType: T,
82
+ parameters: ParsedParameter<string | number>[] | string,
83
+ resource: Resource<FHIRVersion, T>
84
+ ): Promise<Resource<FHIRVersion, T>>;
85
+ patch<FHIRVersion extends FHIR_VERSION, T extends ResourceType<FHIRVersion>>(
86
+ ctx: CTX,
87
+ fhirVersion: FHIRVersion,
88
+ resourceType: T,
89
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>,
90
+ patches: any
91
+ ): Promise<Resource<FHIRVersion, T>>;
92
+ read<FHIRVersion extends FHIR_VERSION, T extends ResourceType<FHIRVersion>>(
93
+ ctx: CTX,
94
+ fhirVersion: FHIRVersion,
95
+ resourceType: T,
96
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>
97
+ ): Promise<Resource<FHIRVersion, T> | undefined>;
98
+ vread<FHIRVersion extends FHIR_VERSION, T extends ResourceType<FHIRVersion>>(
99
+ ctx: CTX,
100
+ fhirVersion: FHIRVersion,
101
+ resourceType: T,
102
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>,
103
+ versionId: NonNullable<
104
+ Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]
105
+ >
106
+ ): Promise<Resource<FHIRVersion, T> | undefined>;
107
+ delete_instance<
108
+ FHIRVersion extends FHIR_VERSION,
109
+ T extends ResourceType<FHIRVersion>
110
+ >(
111
+ ctx: CTX,
112
+ fhirVersion: FHIRVersion,
113
+ resourceType: T,
114
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>
115
+ ): Promise<void>;
116
+ delete_type<
117
+ FHIRVersion extends FHIR_VERSION,
118
+ T extends ResourceType<FHIRVersion>
119
+ >(
120
+ ctx: CTX,
121
+ fhirVersion: FHIRVersion,
122
+ resourceType: T,
123
+ parameters?: ParsedParameter<string | number>[] | string
124
+ ): Promise<void>;
125
+ delete_system<FHIRVersion extends FHIR_VERSION>(
126
+ ctx: CTX,
127
+ fhirVersion: FHIRVersion,
128
+ parameters?: ParsedParameter<string | number>[] | string
129
+ ): Promise<void>;
130
+
131
+ history_system<FHIRVersion extends FHIR_VERSION>(
132
+ ctx: CTX,
133
+ fhirVersion: FHIRVersion,
134
+ parameters?: ParsedParameter<string | number>[] | string
135
+ ): Promise<NonNullable<Resource<FHIRVersion, "Bundle">["entry"]>>;
136
+ history_type<
137
+ FHIRVersion extends FHIR_VERSION,
138
+ T extends ResourceType<FHIRVersion>
139
+ >(
140
+ ctx: CTX,
141
+ fhirVersion: FHIRVersion,
142
+ resourceType: T,
143
+ parameters?: ParsedParameter<string | number>[] | string
144
+ ): Promise<NonNullable<Resource<FHIRVersion, "Bundle">["entry"]>>;
145
+ history_instance<
146
+ FHIRVersion extends FHIR_VERSION,
147
+ T extends ResourceType<FHIRVersion>
148
+ >(
149
+ ctx: CTX,
150
+ fhirVersion: FHIRVersion,
151
+ resourceType: T,
152
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>,
153
+ parameters?: ParsedParameter<string | number>[] | string
154
+ ): Promise<NonNullable<Resource<FHIRVersion, "Bundle">["entry"]>>;
155
+ invoke_system<
156
+ FHIRVersion extends FHIR_VERSION,
157
+ Op extends IOperation<any, any> | code,
158
+ Input extends InvokeParameter<FHIRVersion, Op, "Input">,
159
+ Output extends InvokeParameter<FHIRVersion, Op, "Output">
160
+ >(
161
+ op: Op,
162
+ ctx: CTX,
163
+ fhirVersion: FHIRVersion,
164
+ input: Input
165
+ ): Promise<Output>;
166
+ invoke_type<
167
+ FHIRVersion extends FHIR_VERSION,
168
+ Op extends IOperation<any, any> | code,
169
+ Input extends InvokeParameter<FHIRVersion, Op, "Input">,
170
+ Output extends InvokeParameter<FHIRVersion, Op, "Output">,
171
+ T extends ResourceType<FHIRVersion>
172
+ >(
173
+ op: Op,
174
+ ctx: CTX,
175
+ fhirVersion: FHIRVersion,
176
+ resourceType: T,
177
+ input: Input
178
+ ): Promise<Output>;
179
+ invoke_instance<
180
+ FHIRVersion extends FHIR_VERSION,
181
+ Op extends IOperation<any, any> | code,
182
+ Input extends InvokeParameter<FHIRVersion, Op, "Input">,
183
+ Output extends InvokeParameter<FHIRVersion, Op, "Output">,
184
+ T extends ResourceType<FHIRVersion>
185
+ >(
186
+ op: Op,
187
+ ctx: CTX,
188
+ fhirVersion: FHIRVersion,
189
+ resourceType: T,
190
+ id: NonNullable<Resource<FHIRVersion, ResourceType<FHIRVersion>>["id"]>,
191
+ input: Input
192
+ ): Promise<Output>;
193
+ transaction<FHIRVersion extends FHIR_VERSION>(
194
+ ctx: CTX,
195
+ fhirVersion: FHIRVersion,
196
+ bundle: Resource<FHIRVersion, "Bundle">
197
+ ): Promise<Resource<FHIRVersion, "Bundle">>;
198
+ batch<FHIRVersion extends FHIR_VERSION>(
199
+ ctx: CTX,
200
+ fhirVersion: FHIRVersion,
201
+ bundle: Resource<FHIRVersion, "Bundle">
202
+ ): Promise<Resource<FHIRVersion, "Bundle">>;
203
+ }
@@ -0,0 +1,120 @@
1
+ import { expect, test } from "@jest/globals";
2
+
3
+ import { Bundle, code } from "@haste-health/fhir-types/lib/generated/r4/types";
4
+ import { FHIR_VERSION, R4 } from "@haste-health/fhir-types/lib/versions";
5
+
6
+ import { AllInteractions, FHIRResponse } from "../types/index.js";
7
+ import type { ParsedParameter } from "../url";
8
+ import { createMiddlewareAsync } from "./index.js";
9
+
10
+ test("Test middleware Async", async () => {
11
+ const middleware = createMiddlewareAsync<{}, {}>({}, [
12
+ async (state, context, next) => {
13
+ if (next) {
14
+ const [nextState, res] = await next(state, context);
15
+ return [
16
+ nextState,
17
+ {
18
+ ...res,
19
+ response: {
20
+ ...res.response,
21
+ body: {
22
+ ...((res as any).response?.body as any),
23
+ entry: (res.response as any).body?.entry?.map((e: any) => ({
24
+ ...e,
25
+ resource: {
26
+ id: "123",
27
+ ...e.resource,
28
+ },
29
+ })),
30
+ } as Bundle,
31
+ } as FHIRResponse<FHIR_VERSION, AllInteractions>,
32
+ },
33
+ ];
34
+ }
35
+ return [
36
+ state,
37
+ {
38
+ ...context,
39
+
40
+ response: {
41
+ fhirVersion: R4,
42
+ parameters: (context.request as any).parameters
43
+ ? ((context.request as any).parameters as ParsedParameter<
44
+ string | number
45
+ >[])
46
+ : [],
47
+ type: "search-response",
48
+ level: "system",
49
+ body: {
50
+ resourceType: "Bundle",
51
+ type: "searchset" as code,
52
+ entry: [],
53
+ } as Bundle,
54
+ },
55
+ },
56
+ ];
57
+ },
58
+ async (state, context, next) => {
59
+ return [
60
+ state,
61
+ {
62
+ ...context,
63
+ response: {
64
+ fhirVersion: R4,
65
+ parameters: (context.request as any).parameters
66
+ ? ((context.request as any).parameters as ParsedParameter<
67
+ string | number
68
+ >[])
69
+ : [],
70
+ type: "search-response",
71
+ level: "system",
72
+ body: {
73
+ resourceType: "Bundle",
74
+ type: "searchset" as code,
75
+ entry: [{ resource: { resourceType: "Patient" } }],
76
+ } as Bundle,
77
+ },
78
+ },
79
+ ];
80
+ },
81
+ ]);
82
+
83
+ const result = middleware({
84
+ key: "test",
85
+ ctx: {},
86
+ request: {} as any,
87
+ });
88
+ expect(result).toBeInstanceOf(Promise);
89
+ expect(await result).toEqual({
90
+ key: "test",
91
+ request: {},
92
+ ctx: {},
93
+ response: {
94
+ fhirVersion: R4,
95
+ parameters: [],
96
+ type: "search-response",
97
+ level: "system",
98
+ body: {
99
+ resourceType: "Bundle",
100
+ type: "searchset",
101
+ entry: [{ resource: { id: "123", resourceType: "Patient" } }],
102
+ },
103
+ },
104
+ });
105
+ });
106
+
107
+ test("Test state", async () => {
108
+ const middleware = createMiddlewareAsync<number, {}, number, number>(5, [
109
+ async (state, context, next) => {
110
+ const [nextState, res] = await next(state, context);
111
+ return [
112
+ res.request + nextState,
113
+ { ...res, response: state + context.request },
114
+ ];
115
+ },
116
+ ]);
117
+
118
+ expect((await middleware({ ctx: {}, request: 5 })).response).toEqual(10);
119
+ expect((await middleware({ ctx: {}, request: 5 })).response).toEqual(15);
120
+ });
@@ -0,0 +1,95 @@
1
+ import { FHIR_VERSION } from "@haste-health/fhir-types/versions";
2
+
3
+ import { AllInteractions, FHIRRequest, FHIRResponse } from "../types/index.js";
4
+
5
+ type Context<CTX, Request, Response> = {
6
+ key?: string;
7
+ ctx: CTX;
8
+ request: Request;
9
+ response?: Response;
10
+ };
11
+
12
+ export type MiddlewareAsyncChain<
13
+ State,
14
+ CTX,
15
+ Request = FHIRRequest<FHIR_VERSION, AllInteractions>,
16
+ Response = FHIRResponse<FHIR_VERSION, AllInteractions | "error">
17
+ > = (
18
+ state: State,
19
+ ctx: Context<CTX, Request, Response>,
20
+ next: Next<State, CTX, Request, Response>
21
+ ) => Promise<[State, Context<CTX, Request, Response>]>;
22
+
23
+ type Next<
24
+ State,
25
+ CTX,
26
+ Request = FHIRRequest<FHIR_VERSION, AllInteractions>,
27
+ Response = FHIRResponse<FHIR_VERSION, AllInteractions | "error">
28
+ > = (
29
+ state: State,
30
+ context: Context<CTX, Request, Response>
31
+ ) => Promise<[State, Context<CTX, Request, Response>]>;
32
+
33
+ export type MiddlewareAsync<
34
+ CTX,
35
+ Request = FHIRRequest<FHIR_VERSION, AllInteractions>,
36
+ Response = FHIRResponse<FHIR_VERSION, AllInteractions | "error">
37
+ > = (
38
+ ctx: Context<CTX, Request, Response>
39
+ ) => Promise<Context<CTX, Request, Response>>;
40
+
41
+ function createNext<
42
+ State,
43
+ CTX,
44
+ Request = FHIRRequest<FHIR_VERSION, AllInteractions>,
45
+ Response = FHIRResponse<FHIR_VERSION, AllInteractions | "error">
46
+ >(
47
+ middlewareChain: MiddlewareAsyncChain<State, CTX, Request, Response>[],
48
+ options: { logging?: boolean } = { logging: false }
49
+ ): Next<State, CTX, Request, Response> {
50
+ const [first, ...rest] = middlewareChain;
51
+
52
+ if (first) {
53
+ return async (state, context) => {
54
+ if (options.logging) console.time(`${context.key}:${first.name}`);
55
+ const response = await first(state, context, createNext(rest));
56
+ if (options.logging) console.timeEnd(`${context.key}:${first.name}`);
57
+ return response;
58
+ };
59
+ } else {
60
+ // placeholder.
61
+ return async (state, context) => {
62
+ return [state, context];
63
+ };
64
+ }
65
+ }
66
+
67
+ export function createMiddlewareAsync<
68
+ State,
69
+ CTX,
70
+ Request = FHIRRequest<FHIR_VERSION, AllInteractions>,
71
+ Response = FHIRResponse<FHIR_VERSION, AllInteractions | "error">
72
+ >(
73
+ _state: State,
74
+ middlewareChain: MiddlewareAsyncChain<State, CTX, Request, Response>[],
75
+ options: { logging?: boolean } = { logging: false }
76
+ ): MiddlewareAsync<CTX, Request, Response> {
77
+ let state = _state;
78
+
79
+ return async (context) => {
80
+ const [first, ...rest] = middlewareChain;
81
+ context.key = context.key ?? Math.ceil(Math.random() * 10000).toString();
82
+
83
+ if (options.logging) console.time(`${context.key}:${first.name}`);
84
+
85
+ const [nextState, response] = await first(
86
+ state,
87
+ context,
88
+ createNext(rest, options)
89
+ );
90
+ state = nextState;
91
+
92
+ if (options.logging) console.timeEnd(`${context.key}:${first.name}`);
93
+ return response;
94
+ };
95
+ }