@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.
- package/README.md +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
- package/src/index.ts +16 -11
- package/src/shell/api-client/create-client-middleware.ts +179 -0
- package/src/shell/api-client/create-client-response.ts +115 -0
- package/src/shell/api-client/create-client-runtime-helpers.ts +142 -0
- package/src/shell/api-client/create-client-runtime-types.ts +97 -0
- package/src/shell/api-client/create-client-runtime.ts +316 -0
- package/src/shell/api-client/create-client-types.ts +278 -286
- package/src/shell/api-client/create-client.ts +89 -496
- package/src/shell/api-client/index.ts +28 -1
- package/src/shell/api-client/openapi-compat-path.ts +82 -0
- package/src/shell/api-client/openapi-compat-request.ts +153 -0
- package/src/shell/api-client/openapi-compat-serializers.ts +277 -0
- package/src/shell/api-client/openapi-compat-utils.ts +10 -0
- package/src/shell/api-client/openapi-compat-value-guards.ts +9 -0
package/src/index.ts
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
|
-
// CHANGE:
|
|
2
|
-
// WHY:
|
|
3
|
-
// QUOTE(ТЗ): "openapi-effect должен почти 1 в 1 заменяться с openapi-fetch" / "Просто добавлять effect поведение"
|
|
1
|
+
// CHANGE: Expose Effect-only public API
|
|
2
|
+
// WHY: Enforce Effect-first paradigm and remove Promise-based client surface
|
|
4
3
|
// SOURCE: n/a
|
|
5
4
|
// PURITY: SHELL (re-exports)
|
|
6
5
|
// COMPLEXITY: O(1)
|
|
7
6
|
|
|
8
|
-
// Promise-based client (openapi-fetch compatible)
|
|
9
|
-
export { default } from "openapi-fetch"
|
|
10
|
-
export { default as createClient } from "openapi-fetch"
|
|
11
|
-
export * from "openapi-fetch"
|
|
12
|
-
|
|
13
|
-
// Effect-based client (opt-in)
|
|
14
7
|
export * as FetchHttpClient from "@effect/platform/FetchHttpClient"
|
|
15
8
|
|
|
16
|
-
// Strict Effect client (advanced)
|
|
17
9
|
export type * from "./core/api-client/index.js"
|
|
18
10
|
export { assertNever } from "./core/api-client/index.js"
|
|
19
11
|
|
|
20
12
|
export type {
|
|
13
|
+
ClientOptions,
|
|
21
14
|
DispatchersFor,
|
|
22
15
|
StrictApiClient,
|
|
23
16
|
StrictApiClientWithDispatchers
|
|
@@ -26,17 +19,29 @@ export type {
|
|
|
26
19
|
export type { Decoder, Dispatcher, RawResponse, StrictClient, StrictRequestInit } from "./shell/api-client/index.js"
|
|
27
20
|
|
|
28
21
|
export {
|
|
29
|
-
createClient
|
|
22
|
+
createClient,
|
|
30
23
|
createClientEffect,
|
|
31
24
|
createDispatcher,
|
|
25
|
+
createFinalURL,
|
|
26
|
+
createPathBasedClient,
|
|
27
|
+
createQuerySerializer,
|
|
32
28
|
createStrictClient,
|
|
33
29
|
createUniversalDispatcher,
|
|
30
|
+
defaultBodySerializer,
|
|
31
|
+
defaultPathSerializer,
|
|
34
32
|
executeRequest,
|
|
33
|
+
mergeHeaders,
|
|
35
34
|
parseJSON,
|
|
36
35
|
registerDefaultDispatchers,
|
|
36
|
+
removeTrailingSlash,
|
|
37
|
+
serializeArrayParam,
|
|
38
|
+
serializeObjectParam,
|
|
39
|
+
serializePrimitiveParam,
|
|
37
40
|
unexpectedContentType,
|
|
38
41
|
unexpectedStatus
|
|
39
42
|
} from "./shell/api-client/index.js"
|
|
40
43
|
|
|
44
|
+
export { createClient as default } from "./shell/api-client/index.js"
|
|
45
|
+
|
|
41
46
|
// Generated dispatchers (auto-generated from OpenAPI schema)
|
|
42
47
|
export * from "./generated/index.js"
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { Effect } from "effect"
|
|
2
|
+
|
|
3
|
+
import { toError } from "./create-client-response.js"
|
|
4
|
+
import type { AsyncValue, MergedOptions, Middleware, MiddlewareRequestParams, Thenable } from "./create-client-types.js"
|
|
5
|
+
|
|
6
|
+
const isThenable = <T>(value: unknown): value is Thenable<T> => (
|
|
7
|
+
typeof value === "object"
|
|
8
|
+
&& value !== null
|
|
9
|
+
&& "then" in value
|
|
10
|
+
&& typeof Reflect.get(value, "then") === "function"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
export const toPromiseEffect = <T>(value: AsyncValue<T>): Effect.Effect<T, Error> => (
|
|
14
|
+
isThenable(value)
|
|
15
|
+
? Effect.async<T, Error>((resume) => {
|
|
16
|
+
value.then(
|
|
17
|
+
(result) => {
|
|
18
|
+
resume(Effect.succeed(result))
|
|
19
|
+
},
|
|
20
|
+
(error) => {
|
|
21
|
+
resume(Effect.fail(toError(error)))
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
})
|
|
25
|
+
: Effect.succeed(value)
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
export type MiddlewareContext = {
|
|
29
|
+
schemaPath: string
|
|
30
|
+
params: MiddlewareRequestParams
|
|
31
|
+
options: MergedOptions
|
|
32
|
+
id: string
|
|
33
|
+
middleware: Array<Middleware>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const reverseMiddleware = (middleware: Array<Middleware>): Array<Middleware> => {
|
|
37
|
+
const output: Array<Middleware> = []
|
|
38
|
+
|
|
39
|
+
for (let index = middleware.length - 1; index >= 0; index -= 1) {
|
|
40
|
+
const item = middleware[index]
|
|
41
|
+
if (item !== undefined) {
|
|
42
|
+
output.push(item)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return output
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type RequestMiddlewareResult = {
|
|
50
|
+
request: Request
|
|
51
|
+
response?: Response
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const createMiddlewareParams = (
|
|
55
|
+
request: Request,
|
|
56
|
+
context: MiddlewareContext
|
|
57
|
+
): {
|
|
58
|
+
request: Request
|
|
59
|
+
schemaPath: string
|
|
60
|
+
params: MiddlewareRequestParams
|
|
61
|
+
options: MergedOptions
|
|
62
|
+
id: string
|
|
63
|
+
} => ({
|
|
64
|
+
request,
|
|
65
|
+
schemaPath: context.schemaPath,
|
|
66
|
+
params: context.params,
|
|
67
|
+
options: context.options,
|
|
68
|
+
id: context.id
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
export const applyRequestMiddleware = (
|
|
72
|
+
request: Request,
|
|
73
|
+
context: MiddlewareContext
|
|
74
|
+
): Effect.Effect<RequestMiddlewareResult, Error> =>
|
|
75
|
+
Effect.gen(function*() {
|
|
76
|
+
let nextRequest = request
|
|
77
|
+
|
|
78
|
+
for (const item of context.middleware) {
|
|
79
|
+
if (typeof item.onRequest !== "function") {
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const result = yield* toPromiseEffect(item.onRequest(createMiddlewareParams(nextRequest, context)))
|
|
84
|
+
|
|
85
|
+
if (result === undefined) {
|
|
86
|
+
continue
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (result instanceof Request) {
|
|
90
|
+
nextRequest = result
|
|
91
|
+
continue
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (result instanceof Response) {
|
|
95
|
+
return { request: nextRequest, response: result }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return yield* Effect.fail(
|
|
99
|
+
new Error("onRequest: must return new Request() or Response() when modifying the request")
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { request: nextRequest }
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
export const applyResponseMiddleware = (
|
|
107
|
+
request: Request,
|
|
108
|
+
response: Response,
|
|
109
|
+
context: MiddlewareContext
|
|
110
|
+
): Effect.Effect<Response, Error> =>
|
|
111
|
+
Effect.gen(function*() {
|
|
112
|
+
let nextResponse = response
|
|
113
|
+
|
|
114
|
+
for (const item of reverseMiddleware(context.middleware)) {
|
|
115
|
+
if (typeof item.onResponse !== "function") {
|
|
116
|
+
continue
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const result = yield* toPromiseEffect(item.onResponse({
|
|
120
|
+
...createMiddlewareParams(request, context),
|
|
121
|
+
response: nextResponse
|
|
122
|
+
}))
|
|
123
|
+
|
|
124
|
+
if (result === undefined) {
|
|
125
|
+
continue
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!(result instanceof Response)) {
|
|
129
|
+
return yield* Effect.fail(
|
|
130
|
+
new Error("onResponse: must return new Response() when modifying the response")
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
nextResponse = result
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return nextResponse
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
const normalizeErrorResult = (
|
|
141
|
+
result: Response | Error | undefined
|
|
142
|
+
): Effect.Effect<Response | Error | undefined, Error> => {
|
|
143
|
+
if (result === undefined || result instanceof Response || result instanceof Error) {
|
|
144
|
+
return Effect.succeed(result)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return Effect.fail(new Error("onError: must return new Response() or instance of Error"))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const applyErrorMiddleware = (
|
|
151
|
+
request: Request,
|
|
152
|
+
fetchError: Error,
|
|
153
|
+
context: MiddlewareContext
|
|
154
|
+
): Effect.Effect<Response, Error> =>
|
|
155
|
+
Effect.gen(function*() {
|
|
156
|
+
let nextError: Error = fetchError
|
|
157
|
+
|
|
158
|
+
for (const item of reverseMiddleware(context.middleware)) {
|
|
159
|
+
if (typeof item.onError !== "function") {
|
|
160
|
+
continue
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const rawResult = yield* toPromiseEffect(item.onError({
|
|
164
|
+
...createMiddlewareParams(request, context),
|
|
165
|
+
error: nextError
|
|
166
|
+
}))
|
|
167
|
+
|
|
168
|
+
const result = yield* normalizeErrorResult(rawResult)
|
|
169
|
+
if (result instanceof Response) {
|
|
170
|
+
return result
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (result instanceof Error) {
|
|
174
|
+
nextError = result
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return yield* Effect.fail(nextError)
|
|
179
|
+
})
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Effect } from "effect"
|
|
2
|
+
|
|
3
|
+
import { asJson } from "../../core/axioms.js"
|
|
4
|
+
import type { ParseAs } from "./create-client-types.js"
|
|
5
|
+
|
|
6
|
+
type RuntimeFetchResponse = {
|
|
7
|
+
data?: unknown
|
|
8
|
+
error?: unknown
|
|
9
|
+
response: Response
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const toError = (error: unknown): Error => (
|
|
13
|
+
error instanceof Error ? error : new Error(String(error))
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
const parseJsonText = (rawText: string): Effect.Effect<unknown, Error> => (
|
|
17
|
+
rawText.length === 0
|
|
18
|
+
? Effect.void
|
|
19
|
+
: Effect.try({
|
|
20
|
+
try: () => asJson(JSON.parse(rawText)),
|
|
21
|
+
catch: toError
|
|
22
|
+
})
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
const readResponseText = (response: Response): Effect.Effect<string, Error> => (
|
|
26
|
+
Effect.tryPromise({
|
|
27
|
+
try: () => response.text(),
|
|
28
|
+
catch: toError
|
|
29
|
+
})
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
const parseSuccessData = (
|
|
33
|
+
response: Response,
|
|
34
|
+
parseAs: ParseAs,
|
|
35
|
+
contentLength: string | null
|
|
36
|
+
): Effect.Effect<unknown, Error> => {
|
|
37
|
+
if (parseAs === "stream") {
|
|
38
|
+
return Effect.succeed(response.body)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (parseAs === "text") {
|
|
42
|
+
return Effect.tryPromise({ try: () => response.text(), catch: toError })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (parseAs === "blob") {
|
|
46
|
+
return Effect.tryPromise({ try: () => response.blob(), catch: toError })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (parseAs === "arrayBuffer") {
|
|
50
|
+
return Effect.tryPromise({ try: () => response.arrayBuffer(), catch: toError })
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (contentLength === null) {
|
|
54
|
+
return readResponseText(response).pipe(
|
|
55
|
+
Effect.flatMap((rawText) => parseJsonText(rawText))
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return Effect.tryPromise({ try: () => response.json(), catch: toError })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const parseErrorData = (response: Response): Effect.Effect<unknown, Error> => (
|
|
63
|
+
readResponseText(response).pipe(
|
|
64
|
+
Effect.flatMap((rawText) =>
|
|
65
|
+
Effect.match(
|
|
66
|
+
Effect.try({
|
|
67
|
+
try: () => asJson(JSON.parse(rawText)),
|
|
68
|
+
catch: toError
|
|
69
|
+
}),
|
|
70
|
+
{
|
|
71
|
+
onFailure: () => rawText,
|
|
72
|
+
onSuccess: (parsed) => parsed
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
const hasChunkedTransferEncoding = (response: Response): boolean => (
|
|
80
|
+
response.headers.get("Transfer-Encoding")?.includes("chunked") === true
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
const isEmptyResponse = (
|
|
84
|
+
request: Request,
|
|
85
|
+
response: Response,
|
|
86
|
+
contentLength: string | null
|
|
87
|
+
): boolean => (
|
|
88
|
+
response.status === 204
|
|
89
|
+
|| request.method === "HEAD"
|
|
90
|
+
|| (contentLength === "0" && !hasChunkedTransferEncoding(response))
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
export const createResponseEnvelope = (
|
|
94
|
+
request: Request,
|
|
95
|
+
response: Response,
|
|
96
|
+
parseAs: ParseAs
|
|
97
|
+
): Effect.Effect<RuntimeFetchResponse, Error> => {
|
|
98
|
+
const contentLength = response.headers.get("Content-Length")
|
|
99
|
+
|
|
100
|
+
if (isEmptyResponse(request, response, contentLength)) {
|
|
101
|
+
return response.ok
|
|
102
|
+
? Effect.succeed({ data: undefined, response })
|
|
103
|
+
: Effect.succeed({ error: undefined, response })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (response.ok) {
|
|
107
|
+
return parseSuccessData(response, parseAs, contentLength).pipe(
|
|
108
|
+
Effect.map((data) => ({ data, response }))
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return parseErrorData(response).pipe(
|
|
113
|
+
Effect.map((error) => ({ error, response }))
|
|
114
|
+
)
|
|
115
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Effect } from "effect"
|
|
2
|
+
|
|
3
|
+
import { asStrictApiClient } from "../../core/axioms.js"
|
|
4
|
+
import { toError } from "./create-client-response.js"
|
|
5
|
+
import type { FetchWithRequestInitExt, HeaderRecord } from "./create-client-runtime-types.js"
|
|
6
|
+
import type {
|
|
7
|
+
BodySerializer,
|
|
8
|
+
ClientOptions,
|
|
9
|
+
MergedOptions,
|
|
10
|
+
MiddlewareRequestParams,
|
|
11
|
+
ParseAs,
|
|
12
|
+
PathSerializer,
|
|
13
|
+
QuerySerializer,
|
|
14
|
+
QuerySerializerOptions
|
|
15
|
+
} from "./create-client-types.js"
|
|
16
|
+
import { createQuerySerializer } from "./openapi-compat-utils.js"
|
|
17
|
+
|
|
18
|
+
export const supportsRequestInitExt = (): boolean => (
|
|
19
|
+
typeof process === "object"
|
|
20
|
+
&& Number.parseInt(process.versions.node.slice(0, 2), 10) >= 18
|
|
21
|
+
&& typeof process.versions["undici"] === "string"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
export const randomID = (): string => (
|
|
25
|
+
globalThis.crypto.randomUUID().replaceAll("-", "").slice(0, 9)
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const isQuerySerializerOptions = (
|
|
29
|
+
value: QuerySerializer<unknown> | QuerySerializerOptions | undefined
|
|
30
|
+
): value is QuerySerializerOptions => (
|
|
31
|
+
value !== undefined && typeof value === "object"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
export const resolveQuerySerializer = (
|
|
35
|
+
globalQuerySerializer: ClientOptions["querySerializer"],
|
|
36
|
+
requestQuerySerializer: QuerySerializer<unknown> | QuerySerializerOptions | undefined
|
|
37
|
+
): QuerySerializer<unknown> => {
|
|
38
|
+
let serializer = typeof globalQuerySerializer === "function"
|
|
39
|
+
? globalQuerySerializer
|
|
40
|
+
: createQuerySerializer(globalQuerySerializer)
|
|
41
|
+
|
|
42
|
+
if (requestQuerySerializer) {
|
|
43
|
+
serializer = typeof requestQuerySerializer === "function"
|
|
44
|
+
? requestQuerySerializer
|
|
45
|
+
: createQuerySerializer({
|
|
46
|
+
...(isQuerySerializerOptions(globalQuerySerializer) ? globalQuerySerializer : {}),
|
|
47
|
+
...requestQuerySerializer
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return serializer
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const isHeaderPrimitive = (value: unknown): value is string | number | boolean => (
|
|
55
|
+
typeof value === "string" || typeof value === "number" || typeof value === "boolean"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
export const toHeaderOverrides = (headers: MiddlewareRequestParams["header"]): HeaderRecord => {
|
|
59
|
+
if (headers === undefined) {
|
|
60
|
+
return {}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const normalized: HeaderRecord = {}
|
|
64
|
+
for (const [key, rawValue] of Object.entries(headers)) {
|
|
65
|
+
if (rawValue === undefined || rawValue === null || isHeaderPrimitive(rawValue)) {
|
|
66
|
+
normalized[key] = rawValue
|
|
67
|
+
continue
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (Array.isArray(rawValue)) {
|
|
71
|
+
normalized[key] = rawValue.filter((item) => isHeaderPrimitive(item))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return normalized
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const isBodyInit = (value: BodyInit | object): value is BodyInit => (
|
|
79
|
+
typeof value === "string"
|
|
80
|
+
|| value instanceof Blob
|
|
81
|
+
|| value instanceof URLSearchParams
|
|
82
|
+
|| value instanceof ArrayBuffer
|
|
83
|
+
|| value instanceof FormData
|
|
84
|
+
|| value instanceof ReadableStream
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
export type SerializedBody =
|
|
88
|
+
| { hasBody: false }
|
|
89
|
+
| { hasBody: true; value: BodyInit }
|
|
90
|
+
|
|
91
|
+
export const serializeBody = (
|
|
92
|
+
body: BodyInit | object | undefined,
|
|
93
|
+
serializer: BodySerializer<unknown>,
|
|
94
|
+
headers: Headers
|
|
95
|
+
): SerializedBody => {
|
|
96
|
+
if (body === undefined) {
|
|
97
|
+
return { hasBody: false }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (isBodyInit(body)) {
|
|
101
|
+
return { hasBody: true, value: body }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { hasBody: true, value: serializer(body, headers) }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const setCustomRequestFields = (request: Request, init: Record<string, unknown>): void => {
|
|
108
|
+
for (const key in init) {
|
|
109
|
+
if (!(key in request)) {
|
|
110
|
+
Reflect.set(request, key, init[key])
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const invokeFetch = (
|
|
116
|
+
fetch: NonNullable<ClientOptions["fetch"]>,
|
|
117
|
+
request: Request,
|
|
118
|
+
requestInitExt?: Record<string, unknown>
|
|
119
|
+
): Effect.Effect<Response, Error> => {
|
|
120
|
+
const fetchWithExt = asStrictApiClient<FetchWithRequestInitExt>(fetch)
|
|
121
|
+
return Effect.tryPromise({
|
|
122
|
+
try: () => fetchWithExt(request, requestInitExt),
|
|
123
|
+
catch: toError
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const createMergedOptions = (options: {
|
|
128
|
+
baseUrl: string
|
|
129
|
+
parseAs: ParseAs
|
|
130
|
+
querySerializer: QuerySerializer<unknown>
|
|
131
|
+
bodySerializer: BodySerializer<unknown>
|
|
132
|
+
pathSerializer: PathSerializer
|
|
133
|
+
fetch: NonNullable<ClientOptions["fetch"]>
|
|
134
|
+
}): MergedOptions =>
|
|
135
|
+
Object.freeze<MergedOptions>({
|
|
136
|
+
baseUrl: options.baseUrl,
|
|
137
|
+
parseAs: options.parseAs,
|
|
138
|
+
querySerializer: options.querySerializer,
|
|
139
|
+
bodySerializer: options.bodySerializer,
|
|
140
|
+
pathSerializer: options.pathSerializer,
|
|
141
|
+
fetch: asStrictApiClient<typeof globalThis.fetch>(options.fetch)
|
|
142
|
+
})
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Effect } from "effect"
|
|
2
|
+
|
|
3
|
+
import type { MiddlewareContext } from "./create-client-middleware.js"
|
|
4
|
+
import type {
|
|
5
|
+
BodySerializer,
|
|
6
|
+
ClientOptions,
|
|
7
|
+
HeadersOptions,
|
|
8
|
+
Middleware,
|
|
9
|
+
MiddlewareRequestParams,
|
|
10
|
+
ParseAs,
|
|
11
|
+
PathSerializer,
|
|
12
|
+
QuerySerializer,
|
|
13
|
+
QuerySerializerOptions
|
|
14
|
+
} from "./create-client-types.js"
|
|
15
|
+
|
|
16
|
+
export type RuntimeFetchResponse = {
|
|
17
|
+
data?: unknown
|
|
18
|
+
error?: unknown
|
|
19
|
+
response: Response
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type RuntimeFetchOptions = Omit<RequestInit, "body" | "headers" | "method"> & {
|
|
23
|
+
baseUrl?: string
|
|
24
|
+
fetch?: NonNullable<ClientOptions["fetch"]>
|
|
25
|
+
Request?: ClientOptions["Request"]
|
|
26
|
+
headers?: HeadersOptions
|
|
27
|
+
params?: MiddlewareRequestParams
|
|
28
|
+
parseAs?: ParseAs
|
|
29
|
+
querySerializer?: QuerySerializer<unknown> | QuerySerializerOptions
|
|
30
|
+
pathSerializer?: PathSerializer
|
|
31
|
+
bodySerializer?: BodySerializer<unknown>
|
|
32
|
+
body?: BodyInit | object
|
|
33
|
+
middleware?: Array<Middleware>
|
|
34
|
+
method?: string
|
|
35
|
+
[key: string]: unknown
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type RuntimeClient = {
|
|
39
|
+
request: (method: string, url: string, init?: RuntimeFetchOptions) => Effect.Effect<RuntimeFetchResponse, Error>
|
|
40
|
+
GET: (url: string, init?: RuntimeFetchOptions) => Effect.Effect<RuntimeFetchResponse, Error>
|
|
41
|
+
PUT: (url: string, init?: RuntimeFetchOptions) => Effect.Effect<RuntimeFetchResponse, Error>
|
|
42
|
+
POST: (url: string, init?: RuntimeFetchOptions) => Effect.Effect<RuntimeFetchResponse, Error>
|
|
43
|
+
DELETE: (url: string, init?: RuntimeFetchOptions) => Effect.Effect<RuntimeFetchResponse, Error>
|
|
44
|
+
OPTIONS: (url: string, init?: RuntimeFetchOptions) => Effect.Effect<RuntimeFetchResponse, Error>
|
|
45
|
+
HEAD: (url: string, init?: RuntimeFetchOptions) => Effect.Effect<RuntimeFetchResponse, Error>
|
|
46
|
+
PATCH: (url: string, init?: RuntimeFetchOptions) => Effect.Effect<RuntimeFetchResponse, Error>
|
|
47
|
+
TRACE: (url: string, init?: RuntimeFetchOptions) => Effect.Effect<RuntimeFetchResponse, Error>
|
|
48
|
+
use: (...middleware: Array<Middleware>) => void
|
|
49
|
+
eject: (...middleware: Array<Middleware>) => void
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type HeaderValue =
|
|
53
|
+
| string
|
|
54
|
+
| number
|
|
55
|
+
| boolean
|
|
56
|
+
| Array<string | number | boolean>
|
|
57
|
+
| null
|
|
58
|
+
| undefined
|
|
59
|
+
|
|
60
|
+
export type HeaderRecord = Record<string, HeaderValue>
|
|
61
|
+
|
|
62
|
+
export type BaseRuntimeConfig = {
|
|
63
|
+
Request: typeof Request
|
|
64
|
+
baseUrl: string
|
|
65
|
+
bodySerializer: BodySerializer<unknown> | undefined
|
|
66
|
+
fetch: NonNullable<ClientOptions["fetch"]>
|
|
67
|
+
pathSerializer: PathSerializer | undefined
|
|
68
|
+
headers: HeadersOptions | undefined
|
|
69
|
+
querySerializer: QuerySerializer<unknown> | QuerySerializerOptions | undefined
|
|
70
|
+
requestInitExt: Record<string, unknown> | undefined
|
|
71
|
+
baseOptions: Omit<
|
|
72
|
+
ClientOptions,
|
|
73
|
+
| "Request"
|
|
74
|
+
| "baseUrl"
|
|
75
|
+
| "bodySerializer"
|
|
76
|
+
| "fetch"
|
|
77
|
+
| "headers"
|
|
78
|
+
| "querySerializer"
|
|
79
|
+
| "pathSerializer"
|
|
80
|
+
| "requestInitExt"
|
|
81
|
+
>
|
|
82
|
+
globalMiddlewares: Array<Middleware>
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type PreparedRequest = {
|
|
86
|
+
request: Request
|
|
87
|
+
fetch: NonNullable<ClientOptions["fetch"]>
|
|
88
|
+
parseAs: ParseAs
|
|
89
|
+
context: MiddlewareContext
|
|
90
|
+
middleware: Array<Middleware>
|
|
91
|
+
requestInitExt: Record<string, unknown> | undefined
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type FetchWithRequestInitExt = (
|
|
95
|
+
input: Request,
|
|
96
|
+
requestInitExt?: Record<string, unknown>
|
|
97
|
+
) => ReturnType<typeof globalThis.fetch>
|