@prover-coder-ai/openapi-effect 1.0.19 → 1.0.20

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,316 @@
1
+ import { Effect } from "effect"
2
+
3
+ import { applyErrorMiddleware, applyRequestMiddleware, applyResponseMiddleware } from "./create-client-middleware.js"
4
+ import { createResponseEnvelope } from "./create-client-response.js"
5
+ import {
6
+ createMergedOptions,
7
+ invokeFetch,
8
+ randomID,
9
+ resolveQuerySerializer,
10
+ serializeBody,
11
+ setCustomRequestFields,
12
+ supportsRequestInitExt,
13
+ toHeaderOverrides
14
+ } from "./create-client-runtime-helpers.js"
15
+ import type { SerializedBody } from "./create-client-runtime-helpers.js"
16
+ import type {
17
+ BaseRuntimeConfig,
18
+ PreparedRequest,
19
+ RuntimeClient,
20
+ RuntimeFetchOptions,
21
+ RuntimeFetchResponse
22
+ } from "./create-client-runtime-types.js"
23
+ import type {
24
+ BodySerializer,
25
+ ClientOptions,
26
+ Middleware,
27
+ MiddlewareRequestParams,
28
+ ParseAs,
29
+ PathSerializer,
30
+ QuerySerializer
31
+ } from "./create-client-types.js"
32
+ import {
33
+ createFinalURL,
34
+ defaultBodySerializer,
35
+ defaultPathSerializer,
36
+ mergeHeaders,
37
+ removeTrailingSlash
38
+ } from "./openapi-compat-utils.js"
39
+
40
+ type ResolvedFetchConfig = {
41
+ Request: typeof Request
42
+ fetch: NonNullable<ClientOptions["fetch"]>
43
+ parseAs: ParseAs
44
+ params: MiddlewareRequestParams
45
+ body: BodyInit | object | undefined
46
+ bodySerializer: BodySerializer<unknown>
47
+ headers: ClientOptions["headers"]
48
+ init: Record<string, unknown>
49
+ finalBaseUrl: string
50
+ pathSerializer: PathSerializer
51
+ querySerializer: QuerySerializer<unknown>
52
+ middleware: Array<Middleware>
53
+ }
54
+
55
+ const resolveBaseUrl = (baseUrl: string, localBaseUrl: string | undefined): string => (
56
+ localBaseUrl ? removeTrailingSlash(localBaseUrl) : baseUrl
57
+ )
58
+
59
+ const resolveBodySerializer = (
60
+ globalBodySerializer: BodySerializer<unknown> | undefined,
61
+ requestBodySerializer: BodySerializer<unknown> | undefined
62
+ ): BodySerializer<unknown> => (
63
+ requestBodySerializer ?? globalBodySerializer ?? defaultBodySerializer
64
+ )
65
+
66
+ const resolvePathSerializer = (
67
+ globalPathSerializer: PathSerializer | undefined,
68
+ requestPathSerializer: PathSerializer | undefined
69
+ ): PathSerializer => (
70
+ requestPathSerializer ?? globalPathSerializer ?? defaultPathSerializer
71
+ )
72
+
73
+ const joinMiddleware = (
74
+ globalMiddlewares: Array<Middleware>,
75
+ requestMiddlewares: Array<Middleware>
76
+ ): Array<Middleware> => [...globalMiddlewares, ...requestMiddlewares]
77
+
78
+ const resolveFetchConfig = (
79
+ config: BaseRuntimeConfig,
80
+ fetchOptions?: RuntimeFetchOptions
81
+ ): ResolvedFetchConfig => {
82
+ const {
83
+ Request = config.Request,
84
+ baseUrl: localBaseUrl,
85
+ body,
86
+ bodySerializer: requestBodySerializer,
87
+ fetch = config.fetch,
88
+ headers,
89
+ middleware: requestMiddlewares = [],
90
+ params = {},
91
+ parseAs = "json",
92
+ pathSerializer: requestPathSerializer,
93
+ querySerializer: requestQuerySerializer,
94
+ ...init
95
+ } = fetchOptions ?? {}
96
+
97
+ return {
98
+ Request,
99
+ fetch,
100
+ parseAs,
101
+ params,
102
+ body,
103
+ bodySerializer: resolveBodySerializer(config.bodySerializer, requestBodySerializer),
104
+ headers,
105
+ init,
106
+ finalBaseUrl: resolveBaseUrl(config.baseUrl, localBaseUrl),
107
+ pathSerializer: resolvePathSerializer(config.pathSerializer, requestPathSerializer),
108
+ querySerializer: resolveQuerySerializer(config.querySerializer, requestQuerySerializer),
109
+ middleware: joinMiddleware(config.globalMiddlewares, requestMiddlewares)
110
+ }
111
+ }
112
+
113
+ type ResolvedHeaders = {
114
+ serializedBody: SerializedBody
115
+ finalHeaders: Headers
116
+ }
117
+
118
+ const resolveHeaders = (
119
+ config: BaseRuntimeConfig,
120
+ resolved: ResolvedFetchConfig
121
+ ): ResolvedHeaders => {
122
+ const headerOverrides = toHeaderOverrides(resolved.params.header)
123
+ const serializedBody = serializeBody(
124
+ resolved.body,
125
+ resolved.bodySerializer,
126
+ mergeHeaders(config.headers, resolved.headers, headerOverrides)
127
+ )
128
+
129
+ const finalHeaders = mergeHeaders(
130
+ !serializedBody.hasBody || serializedBody.value instanceof FormData
131
+ ? {}
132
+ : { "Content-Type": "application/json" },
133
+ config.headers,
134
+ resolved.headers,
135
+ headerOverrides
136
+ )
137
+
138
+ return { serializedBody, finalHeaders }
139
+ }
140
+
141
+ const createRequest = (
142
+ config: BaseRuntimeConfig,
143
+ schemaPath: string,
144
+ resolved: ResolvedFetchConfig,
145
+ resolvedHeaders: ResolvedHeaders
146
+ ): Request => {
147
+ const requestInit: RequestInit = {
148
+ redirect: "follow",
149
+ ...config.baseOptions,
150
+ ...resolved.init,
151
+ ...(resolvedHeaders.serializedBody.hasBody
152
+ ? { body: resolvedHeaders.serializedBody.value }
153
+ : {}),
154
+ headers: resolvedHeaders.finalHeaders
155
+ }
156
+
157
+ const request = new resolved.Request(
158
+ createFinalURL(schemaPath, {
159
+ baseUrl: resolved.finalBaseUrl,
160
+ params: resolved.params,
161
+ querySerializer: resolved.querySerializer,
162
+ pathSerializer: resolved.pathSerializer
163
+ }),
164
+ requestInit
165
+ )
166
+
167
+ setCustomRequestFields(request, resolved.init)
168
+ return request
169
+ }
170
+
171
+ const createPreparedContext = (
172
+ schemaPath: string,
173
+ resolved: ResolvedFetchConfig
174
+ ): PreparedRequest["context"] => ({
175
+ schemaPath,
176
+ params: resolved.params,
177
+ id: randomID(),
178
+ options: createMergedOptions({
179
+ baseUrl: resolved.finalBaseUrl,
180
+ parseAs: resolved.parseAs,
181
+ querySerializer: resolved.querySerializer,
182
+ bodySerializer: resolved.bodySerializer,
183
+ pathSerializer: resolved.pathSerializer,
184
+ fetch: resolved.fetch
185
+ }),
186
+ middleware: resolved.middleware
187
+ })
188
+
189
+ const prepareRequest = (
190
+ config: BaseRuntimeConfig,
191
+ schemaPath: string,
192
+ fetchOptions?: RuntimeFetchOptions
193
+ ): PreparedRequest => {
194
+ const resolved = resolveFetchConfig(config, fetchOptions)
195
+ const requestHeaders = resolveHeaders(config, resolved)
196
+ const request = createRequest(config, schemaPath, resolved, requestHeaders)
197
+
198
+ return {
199
+ request,
200
+ fetch: resolved.fetch,
201
+ parseAs: resolved.parseAs,
202
+ middleware: resolved.middleware,
203
+ requestInitExt: config.requestInitExt,
204
+ context: createPreparedContext(schemaPath, resolved)
205
+ }
206
+ }
207
+
208
+ const executeFetch = (
209
+ prepared: PreparedRequest
210
+ ): Effect.Effect<{ request: Request; response: Response }, Error> => {
211
+ if (prepared.middleware.length === 0) {
212
+ return invokeFetch(prepared.fetch, prepared.request, prepared.requestInitExt).pipe(
213
+ Effect.map((response) => ({ request: prepared.request, response }))
214
+ )
215
+ }
216
+
217
+ return Effect.gen(function*() {
218
+ const requestPhase = yield* applyRequestMiddleware(prepared.request, prepared.context)
219
+ const request = requestPhase.request
220
+
221
+ const response = requestPhase.response ?? (
222
+ yield* invokeFetch(prepared.fetch, request, prepared.requestInitExt).pipe(
223
+ Effect.matchEffect({
224
+ onFailure: (fetchError) => applyErrorMiddleware(request, fetchError, prepared.context),
225
+ onSuccess: (response) => Effect.succeed(response)
226
+ })
227
+ )
228
+ )
229
+
230
+ const responseAfterMiddleware = yield* applyResponseMiddleware(request, response, prepared.context)
231
+ return { request, response: responseAfterMiddleware }
232
+ })
233
+ }
234
+
235
+ const createCoreFetch = (config: BaseRuntimeConfig) =>
236
+ (
237
+ schemaPath: string,
238
+ fetchOptions?: RuntimeFetchOptions
239
+ ): Effect.Effect<RuntimeFetchResponse, Error> =>
240
+ Effect.gen(function*() {
241
+ const prepared = prepareRequest(config, schemaPath, fetchOptions)
242
+ const execution = yield* executeFetch(prepared)
243
+ return yield* createResponseEnvelope(execution.request, execution.response, prepared.parseAs)
244
+ })
245
+
246
+ const hasMiddlewareHook = (value: Middleware): boolean => (
247
+ "onRequest" in value || "onResponse" in value || "onError" in value
248
+ )
249
+
250
+ const createBaseRuntimeConfig = (
251
+ clientOptions: ClientOptions | undefined,
252
+ globalMiddlewares: Array<Middleware>
253
+ ): BaseRuntimeConfig => {
254
+ const {
255
+ Request = globalThis.Request,
256
+ baseUrl: rawBaseUrl = "",
257
+ bodySerializer,
258
+ fetch = globalThis.fetch,
259
+ headers,
260
+ pathSerializer,
261
+ querySerializer,
262
+ requestInitExt: rawRequestInitExt,
263
+ ...baseOptions
264
+ } = { ...clientOptions }
265
+
266
+ return {
267
+ Request,
268
+ baseUrl: removeTrailingSlash(rawBaseUrl),
269
+ bodySerializer,
270
+ fetch,
271
+ headers,
272
+ pathSerializer,
273
+ querySerializer,
274
+ requestInitExt: supportsRequestInitExt() ? rawRequestInitExt : undefined,
275
+ baseOptions,
276
+ globalMiddlewares
277
+ }
278
+ }
279
+
280
+ const createClientMethods = (
281
+ coreFetch: ReturnType<typeof createCoreFetch>,
282
+ globalMiddlewares: Array<Middleware>
283
+ ): RuntimeClient => ({
284
+ request: (method, url, init) => coreFetch(url, { ...init, method: method.toUpperCase() }),
285
+ GET: (url, init) => coreFetch(url, { ...init, method: "GET" }),
286
+ PUT: (url, init) => coreFetch(url, { ...init, method: "PUT" }),
287
+ POST: (url, init) => coreFetch(url, { ...init, method: "POST" }),
288
+ DELETE: (url, init) => coreFetch(url, { ...init, method: "DELETE" }),
289
+ OPTIONS: (url, init) => coreFetch(url, { ...init, method: "OPTIONS" }),
290
+ HEAD: (url, init) => coreFetch(url, { ...init, method: "HEAD" }),
291
+ PATCH: (url, init) => coreFetch(url, { ...init, method: "PATCH" }),
292
+ TRACE: (url, init) => coreFetch(url, { ...init, method: "TRACE" }),
293
+ use: (...middleware) => {
294
+ for (const item of middleware) {
295
+ if (!hasMiddlewareHook(item)) {
296
+ throw new Error("Middleware must be an object with one of `onRequest()`, `onResponse() or `onError()`")
297
+ }
298
+ globalMiddlewares.push(item)
299
+ }
300
+ },
301
+ eject: (...middleware) => {
302
+ for (const item of middleware) {
303
+ const index = globalMiddlewares.indexOf(item)
304
+ if (index !== -1) {
305
+ globalMiddlewares.splice(index, 1)
306
+ }
307
+ }
308
+ }
309
+ })
310
+
311
+ export const createRuntimeClient = (clientOptions?: ClientOptions): RuntimeClient => {
312
+ const globalMiddlewares: Array<Middleware> = []
313
+ const config = createBaseRuntimeConfig(clientOptions, globalMiddlewares)
314
+ const coreFetch = createCoreFetch(config)
315
+ return createClientMethods(coreFetch, globalMiddlewares)
316
+ }