@prover-coder-ai/openapi-effect 1.0.19

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,310 @@
1
+ // CHANGE: Extract public createClient types into dedicated module
2
+ // WHY: Keep create-client.ts under lint max-lines without weakening type-level invariants
3
+ // QUOTE(ТЗ): "Только прогони по всему проекту линтеры"
4
+ // REF: user-msg-2
5
+ // SOURCE: n/a
6
+ // PURITY: CORE
7
+ // EFFECT: none
8
+ // INVARIANT: All type-level correlations remain unchanged
9
+ // COMPLEXITY: O(1) compile-time / O(0) runtime
10
+
11
+ import type * as HttpClient from "@effect/platform/HttpClient"
12
+ import type { Effect } from "effect"
13
+ import type { ClientOptions as OpenapiFetchClientOptions } from "openapi-fetch"
14
+ import type { HttpMethod } from "openapi-typescript-helpers"
15
+
16
+ import type {
17
+ ApiFailure,
18
+ ApiSuccess,
19
+ HttpError,
20
+ OperationFor,
21
+ PathsForMethod,
22
+ RequestOptionsFor,
23
+ ResponsesFor
24
+ } from "../../core/api-client/strict-types.js"
25
+ import type { Dispatcher } from "../../core/axioms.js"
26
+
27
+ /**
28
+ * Client configuration options
29
+ *
30
+ * @pure - immutable configuration
31
+ */
32
+ export type ClientOptions = OpenapiFetchClientOptions
33
+
34
+ // CHANGE: Add dispatcher map type for auto-dispatching clients
35
+ // WHY: Enable creating clients that infer dispatcher from path+method without per-call parameter
36
+ // QUOTE(ТЗ): "ApiClient и так знает текущие типы. Зачем передавать что либо в GET"
37
+ // REF: user-msg-1
38
+ // SOURCE: n/a
39
+ // FORMAT THEOREM: ∀ path, method: dispatchers[path][method] = Dispatcher<ResponsesFor<OperationFor<Paths, path, method>>>
40
+ // PURITY: CORE
41
+ // EFFECT: none
42
+ // INVARIANT: dispatcher map is total for all operations in Paths
43
+ // COMPLEXITY: O(1) compile-time / O(0) runtime
44
+ export type DispatchersForMethod<
45
+ Paths extends object,
46
+ Method extends HttpMethod
47
+ > = {
48
+ readonly [Path in PathsForMethod<Paths, Method>]: {
49
+ readonly [K in Method]: Dispatcher<ResponsesFor<OperationFor<Paths, Path, Method>>>
50
+ }
51
+ }
52
+
53
+ export type DispatchersFor<Paths extends object> =
54
+ & DispatchersForMethod<Paths, "get">
55
+ & DispatchersForMethod<Paths, "post">
56
+ & DispatchersForMethod<Paths, "put">
57
+ & DispatchersForMethod<Paths, "delete">
58
+ & DispatchersForMethod<Paths, "patch">
59
+ & DispatchersForMethod<Paths, "head">
60
+ & DispatchersForMethod<Paths, "options">
61
+
62
+ type ResponsesForOperation<
63
+ Paths extends object,
64
+ Path extends keyof Paths,
65
+ Method extends HttpMethod
66
+ > = ResponsesFor<OperationFor<Paths, Path, Method>>
67
+
68
+ type RequestEffect<
69
+ Paths extends object,
70
+ Path extends keyof Paths,
71
+ Method extends HttpMethod
72
+ > = Effect.Effect<
73
+ ApiSuccess<ResponsesForOperation<Paths, Path, Method>>,
74
+ ApiFailure<ResponsesForOperation<Paths, Path, Method>>,
75
+ HttpClient.HttpClient
76
+ >
77
+
78
+ type RequestEffectWithHttpErrorsInSuccess<
79
+ Paths extends object,
80
+ Path extends keyof Paths,
81
+ Method extends HttpMethod
82
+ > = Effect.Effect<
83
+ | ApiSuccess<ResponsesForOperation<Paths, Path, Method>>
84
+ | HttpError<ResponsesForOperation<Paths, Path, Method>>,
85
+ Exclude<
86
+ ApiFailure<ResponsesForOperation<Paths, Path, Method>>,
87
+ HttpError<ResponsesForOperation<Paths, Path, Method>>
88
+ >,
89
+ HttpClient.HttpClient
90
+ >
91
+
92
+ type DispatcherFor<
93
+ Paths extends object,
94
+ Path extends keyof Paths,
95
+ Method extends HttpMethod
96
+ > = Dispatcher<ResponsesForOperation<Paths, Path, Method>>
97
+
98
+ type RequestOptionsForOperation<
99
+ Paths extends object,
100
+ Path extends keyof Paths,
101
+ Method extends HttpMethod
102
+ > = RequestOptionsFor<OperationFor<Paths, Path, Method>>
103
+
104
+ /**
105
+ * Type-safe API client with full request-side type enforcement
106
+ *
107
+ * **Key guarantees:**
108
+ * 1. GET only works on paths that have `get` method in schema
109
+ * 2. POST only works on paths that have `post` method in schema
110
+ * 3. Dispatcher type is derived from operation's responses
111
+ * 4. Request options (params/query/body) are derived from operation
112
+ *
113
+ * **Effect Channel Design:**
114
+ * - Success channel: `ApiSuccess<Responses>` - 2xx responses only
115
+ * - Error channel: `ApiFailure<Responses>` - HTTP errors (4xx, 5xx) + boundary errors
116
+ *
117
+ * @typeParam Paths - OpenAPI paths type from openapi-typescript
118
+ *
119
+ * @pure false - operations perform HTTP requests
120
+ * @invariant ∀ call: path ∈ PathsForMethod<Paths, method> ∧ options derived from operation
121
+ */
122
+ export type StrictApiClient<Paths extends object> = {
123
+ /**
124
+ * Execute GET request
125
+ *
126
+ * @typeParam Path - Path that supports GET method (enforced at type level)
127
+ * @param path - API path with GET method
128
+ * @param dispatcher - Response dispatcher (must match operation responses)
129
+ * @param options - Request options (typed from operation)
130
+ * @returns Effect with 2xx in success channel, errors in error channel
131
+ */
132
+ readonly GET: <Path extends PathsForMethod<Paths, "get">>(
133
+ path: Path,
134
+ dispatcher: DispatcherFor<Paths, Path, "get">,
135
+ options?: RequestOptionsForOperation<Paths, Path, "get">
136
+ ) => RequestEffect<Paths, Path, "get">
137
+
138
+ /**
139
+ * Execute POST request
140
+ */
141
+ readonly POST: <Path extends PathsForMethod<Paths, "post">>(
142
+ path: Path,
143
+ dispatcher: DispatcherFor<Paths, Path, "post">,
144
+ options?: RequestOptionsForOperation<Paths, Path, "post">
145
+ ) => RequestEffect<Paths, Path, "post">
146
+
147
+ /**
148
+ * Execute PUT request
149
+ */
150
+ readonly PUT: <Path extends PathsForMethod<Paths, "put">>(
151
+ path: Path,
152
+ dispatcher: DispatcherFor<Paths, Path, "put">,
153
+ options?: RequestOptionsForOperation<Paths, Path, "put">
154
+ ) => RequestEffect<Paths, Path, "put">
155
+
156
+ /**
157
+ * Execute DELETE request
158
+ */
159
+ readonly DELETE: <Path extends PathsForMethod<Paths, "delete">>(
160
+ path: Path,
161
+ dispatcher: DispatcherFor<Paths, Path, "delete">,
162
+ options?: RequestOptionsForOperation<Paths, Path, "delete">
163
+ ) => RequestEffect<Paths, Path, "delete">
164
+
165
+ /**
166
+ * Execute PATCH request
167
+ */
168
+ readonly PATCH: <Path extends PathsForMethod<Paths, "patch">>(
169
+ path: Path,
170
+ dispatcher: DispatcherFor<Paths, Path, "patch">,
171
+ options?: RequestOptionsForOperation<Paths, Path, "patch">
172
+ ) => RequestEffect<Paths, Path, "patch">
173
+
174
+ /**
175
+ * Execute HEAD request
176
+ */
177
+ readonly HEAD: <Path extends PathsForMethod<Paths, "head">>(
178
+ path: Path,
179
+ dispatcher: DispatcherFor<Paths, Path, "head">,
180
+ options?: RequestOptionsForOperation<Paths, Path, "head">
181
+ ) => RequestEffect<Paths, Path, "head">
182
+
183
+ /**
184
+ * Execute OPTIONS request
185
+ */
186
+ readonly OPTIONS: <Path extends PathsForMethod<Paths, "options">>(
187
+ path: Path,
188
+ dispatcher: DispatcherFor<Paths, Path, "options">,
189
+ options?: RequestOptionsForOperation<Paths, Path, "options">
190
+ ) => RequestEffect<Paths, Path, "options">
191
+ }
192
+
193
+ /**
194
+ * Type-safe API client with auto-dispatching (dispatcher is derived from path+method)
195
+ *
196
+ * **Key guarantees:**
197
+ * 1. GET only works on paths that have `get` method in schema
198
+ * 2. Dispatcher is looked up from provided dispatcher map by path+method
199
+ * 3. Request options (params/query/body) are derived from operation
200
+ *
201
+ * **Effect Channel Design:**
202
+ * - Success channel: `ApiSuccess<Responses>` - 2xx responses only
203
+ * - Error channel: `ApiFailure<Responses>` - HTTP errors (4xx, 5xx) + boundary errors
204
+ *
205
+ * @typeParam Paths - OpenAPI paths type from openapi-typescript
206
+ *
207
+ * @pure false - operations perform HTTP requests
208
+ * @invariant ∀ call: path ∈ PathsForMethod<Paths, method> ∧ dispatcherMap[path][method] defined
209
+ */
210
+ export type StrictApiClientWithDispatchers<Paths extends object> = {
211
+ /**
212
+ * Execute GET request (dispatcher is inferred)
213
+ */
214
+ readonly GET: <Path extends PathsForMethod<Paths, "get">>(
215
+ path: Path,
216
+ options?: RequestOptionsForOperation<Paths, Path, "get">
217
+ ) => RequestEffect<Paths, Path, "get">
218
+
219
+ /**
220
+ * Execute POST request (dispatcher is inferred)
221
+ */
222
+ readonly POST: <Path extends PathsForMethod<Paths, "post">>(
223
+ path: Path,
224
+ options?: RequestOptionsForOperation<Paths, Path, "post">
225
+ ) => RequestEffect<Paths, Path, "post">
226
+
227
+ /**
228
+ * Execute PUT request (dispatcher is inferred)
229
+ */
230
+ readonly PUT: <Path extends PathsForMethod<Paths, "put">>(
231
+ path: Path,
232
+ options?: RequestOptionsForOperation<Paths, Path, "put">
233
+ ) => RequestEffect<Paths, Path, "put">
234
+
235
+ /**
236
+ * Execute DELETE request (dispatcher is inferred)
237
+ */
238
+ readonly DELETE: <Path extends PathsForMethod<Paths, "delete">>(
239
+ path: Path,
240
+ options?: RequestOptionsForOperation<Paths, Path, "delete">
241
+ ) => RequestEffect<Paths, Path, "delete">
242
+
243
+ /**
244
+ * Execute PATCH request (dispatcher is inferred)
245
+ */
246
+ readonly PATCH: <Path extends PathsForMethod<Paths, "patch">>(
247
+ path: Path,
248
+ options?: RequestOptionsForOperation<Paths, Path, "patch">
249
+ ) => RequestEffect<Paths, Path, "patch">
250
+
251
+ /**
252
+ * Execute HEAD request (dispatcher is inferred)
253
+ */
254
+ readonly HEAD: <Path extends PathsForMethod<Paths, "head">>(
255
+ path: Path,
256
+ options?: RequestOptionsForOperation<Paths, Path, "head">
257
+ ) => RequestEffect<Paths, Path, "head">
258
+
259
+ /**
260
+ * Execute OPTIONS request (dispatcher is inferred)
261
+ */
262
+ readonly OPTIONS: <Path extends PathsForMethod<Paths, "options">>(
263
+ path: Path,
264
+ options?: RequestOptionsForOperation<Paths, Path, "options">
265
+ ) => RequestEffect<Paths, Path, "options">
266
+ }
267
+
268
+ /**
269
+ * Ergonomic API client where HTTP statuses (2xx + 4xx/5xx from schema)
270
+ * are returned in the success value channel.
271
+ *
272
+ * Boundary/protocol errors remain in the error channel.
273
+ * This removes the need for `Effect.either` when handling normal HTTP statuses.
274
+ */
275
+ export type ClientEffect<Paths extends object> = {
276
+ readonly GET: <Path extends PathsForMethod<Paths, "get">>(
277
+ path: Path,
278
+ options?: RequestOptionsForOperation<Paths, Path, "get">
279
+ ) => RequestEffectWithHttpErrorsInSuccess<Paths, Path, "get">
280
+
281
+ readonly POST: <Path extends PathsForMethod<Paths, "post">>(
282
+ path: Path,
283
+ options?: RequestOptionsForOperation<Paths, Path, "post">
284
+ ) => RequestEffectWithHttpErrorsInSuccess<Paths, Path, "post">
285
+
286
+ readonly PUT: <Path extends PathsForMethod<Paths, "put">>(
287
+ path: Path,
288
+ options?: RequestOptionsForOperation<Paths, Path, "put">
289
+ ) => RequestEffectWithHttpErrorsInSuccess<Paths, Path, "put">
290
+
291
+ readonly DELETE: <Path extends PathsForMethod<Paths, "delete">>(
292
+ path: Path,
293
+ options?: RequestOptionsForOperation<Paths, Path, "delete">
294
+ ) => RequestEffectWithHttpErrorsInSuccess<Paths, Path, "delete">
295
+
296
+ readonly PATCH: <Path extends PathsForMethod<Paths, "patch">>(
297
+ path: Path,
298
+ options?: RequestOptionsForOperation<Paths, Path, "patch">
299
+ ) => RequestEffectWithHttpErrorsInSuccess<Paths, Path, "patch">
300
+
301
+ readonly HEAD: <Path extends PathsForMethod<Paths, "head">>(
302
+ path: Path,
303
+ options?: RequestOptionsForOperation<Paths, Path, "head">
304
+ ) => RequestEffectWithHttpErrorsInSuccess<Paths, Path, "head">
305
+
306
+ readonly OPTIONS: <Path extends PathsForMethod<Paths, "options">>(
307
+ path: Path,
308
+ options?: RequestOptionsForOperation<Paths, Path, "options">
309
+ ) => RequestEffectWithHttpErrorsInSuccess<Paths, Path, "options">
310
+ }