@kubb/plugin-fetch 5.0.0-beta.75 → 5.0.0-beta.76
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/dist/index.cjs +178 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +178 -119
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/generators/clientGenerator.tsx +2 -1
- package/templates/fetch.ts +33 -8
package/templates/fetch.ts
CHANGED
|
@@ -84,7 +84,8 @@ export type BodySerializer = (body: unknown, contentType?: string) => BodyInit |
|
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* Parses a value before it is sent or after it is received, returning the parsed (and optionally
|
|
87
|
-
* transformed) value. Wires zod parsing through the per-call `parser.request` / `parser.response`
|
|
87
|
+
* transformed) value. Wires zod parsing through the per-call `parser.request` / `parser.response` /
|
|
88
|
+
* `parser.error` hooks (`error` runs on the error body when a non-2xx call does not throw).
|
|
88
89
|
*/
|
|
89
90
|
export type Parser<T = unknown> = (value: T) => T | Promise<T>
|
|
90
91
|
|
|
@@ -133,7 +134,7 @@ export type RequestConfig<TBody = unknown, TRequest = Request, TResponse = Respo
|
|
|
133
134
|
transport?: Transport<TRequest, TResponse>
|
|
134
135
|
querySerializer?: QuerySerializer
|
|
135
136
|
bodySerializer?: BodySerializer
|
|
136
|
-
parser?: { request?: Parser; response?: Parser }
|
|
137
|
+
parser?: { request?: Parser; response?: Parser; error?: Parser }
|
|
137
138
|
security?: Array<Auth>
|
|
138
139
|
auth?: AuthResolver
|
|
139
140
|
}
|
|
@@ -283,13 +284,30 @@ function isFormBody(body: unknown): body is BodyInit {
|
|
|
283
284
|
)
|
|
284
285
|
}
|
|
285
286
|
|
|
287
|
+
function appendFormDataValue(formData: FormData, key: string, value: unknown): void {
|
|
288
|
+
if (value === undefined || value === null) return
|
|
289
|
+
if (value instanceof Blob) formData.append(key, value)
|
|
290
|
+
else if (value instanceof Date) formData.append(key, value.toISOString())
|
|
291
|
+
else if (typeof value === 'object') formData.append(key, JSON.stringify(value))
|
|
292
|
+
else formData.append(key, String(value))
|
|
293
|
+
}
|
|
294
|
+
|
|
286
295
|
/**
|
|
287
296
|
* Default body serializer: passes binary/form bodies through and JSON-serializes everything else.
|
|
288
|
-
* For `
|
|
297
|
+
* For `multipart/form-data` plain objects become `FormData` and for
|
|
298
|
+
* `application/x-www-form-urlencoded` they become `URLSearchParams`.
|
|
289
299
|
*/
|
|
290
300
|
export const defaultBodySerializer: BodySerializer = (body, contentType) => {
|
|
291
301
|
if (body === undefined || body === null) return undefined
|
|
292
302
|
if (isFormBody(body)) return body as BodyInit
|
|
303
|
+
if (contentType?.includes('multipart/form-data')) {
|
|
304
|
+
const formData = new FormData()
|
|
305
|
+
for (const [key, value] of Object.entries(body as Record<string, unknown>)) {
|
|
306
|
+
if (Array.isArray(value)) for (const item of value) appendFormDataValue(formData, key, item)
|
|
307
|
+
else appendFormDataValue(formData, key, value)
|
|
308
|
+
}
|
|
309
|
+
return formData
|
|
310
|
+
}
|
|
293
311
|
if (contentType?.includes('application/x-www-form-urlencoded')) {
|
|
294
312
|
return new URLSearchParams(body as Record<string, string>)
|
|
295
313
|
}
|
|
@@ -439,9 +457,7 @@ export function createClientCore<TRequest = Request, TResponse = Response>(
|
|
|
439
457
|
const bodySerializer = requestConfig.bodySerializer ?? config.bodySerializer ?? defaultBodySerializer
|
|
440
458
|
|
|
441
459
|
const headers = mergeHeaders(config.headers, requestConfig.headers)
|
|
442
|
-
|
|
443
|
-
headers['Content-Type'] = requestConfig.contentType
|
|
444
|
-
}
|
|
460
|
+
const requestContentType = requestConfig.contentType ?? headers['Content-Type'] ?? headers['content-type']
|
|
445
461
|
|
|
446
462
|
const query: Record<string, unknown> = { ...((requestConfig.query ?? requestConfig.params) as Record<string, unknown> | undefined) }
|
|
447
463
|
|
|
@@ -454,13 +470,21 @@ export function createClientCore<TRequest = Request, TResponse = Response>(
|
|
|
454
470
|
|
|
455
471
|
const rawBody = requestConfig.body
|
|
456
472
|
const validatedBody = await runParser(requestConfig.parser?.request, rawBody)
|
|
473
|
+
const body = bodySerializer(validatedBody, requestContentType)
|
|
474
|
+
// A FormData body must keep its Content-Type unset so the runtime appends the multipart boundary.
|
|
475
|
+
if (body instanceof FormData) {
|
|
476
|
+
delete headers['Content-Type']
|
|
477
|
+
delete headers['content-type']
|
|
478
|
+
} else if (requestConfig.contentType) {
|
|
479
|
+
headers['Content-Type'] = requestConfig.contentType
|
|
480
|
+
}
|
|
457
481
|
const url = serializeUrl([config.baseURL, requestConfig.baseURL, requestConfig.url], requestConfig.path ?? {}, querySerializer(query))
|
|
458
482
|
|
|
459
483
|
let resolvedRequest: ResolvedRequest = {
|
|
460
484
|
url,
|
|
461
485
|
method: (requestConfig.method ?? 'GET').toUpperCase(),
|
|
462
486
|
headers,
|
|
463
|
-
body
|
|
487
|
+
body,
|
|
464
488
|
signal: requestConfig.signal,
|
|
465
489
|
credentials: requestConfig.credentials,
|
|
466
490
|
responseType: requestConfig.responseType,
|
|
@@ -486,11 +510,12 @@ export function createClientCore<TRequest = Request, TResponse = Response>(
|
|
|
486
510
|
}
|
|
487
511
|
|
|
488
512
|
const data = isSuccess ? await runParser(requestConfig.parser?.response, result.data) : undefined
|
|
513
|
+
const error = isSuccess ? undefined : await runParser(requestConfig.parser?.error, result.data)
|
|
489
514
|
|
|
490
515
|
return {
|
|
491
516
|
status: result.status,
|
|
492
517
|
data,
|
|
493
|
-
error
|
|
518
|
+
error,
|
|
494
519
|
request: result.request,
|
|
495
520
|
response: result.response,
|
|
496
521
|
}
|