@zimic/http 0.4.0-canary.0 → 0.4.0-canary.2

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.
@@ -14,7 +14,7 @@ export namespace HttpSearchParamsSchema {
14
14
  }
15
15
 
16
16
  /** A strict tuple representation of a {@link HttpSearchParamsSchema}. */
17
- export type HttpSearchParamsSchemaTuple<Schema extends HttpSearchParamsSchema = HttpSearchParamsSchema> = {
17
+ export type HttpSearchParamsSchemaTuple<Schema extends HttpSearchParamsSchema.Loose = HttpSearchParamsSchema.Loose> = {
18
18
  [Key in keyof Schema & string]: [Key, ArrayItemIfArray<NonNullable<Schema[Key]>>];
19
19
  }[keyof Schema & string];
20
20
 
@@ -22,7 +22,7 @@ export type HttpSearchParamsSchemaTuple<Schema extends HttpSearchParamsSchema =
22
22
  * An initialization value for
23
23
  * {@link https://github.com/zimicjs/zimic/wiki/api‐zimic‐http#httpsearchparams `HttpSearchParams`}.
24
24
  */
25
- export type HttpSearchParamsInit<Schema extends HttpSearchParamsSchema = HttpSearchParamsSchema> =
25
+ export type HttpSearchParamsInit<Schema extends HttpSearchParamsSchema.Loose = HttpSearchParamsSchema.Loose> =
26
26
  | string
27
27
  | URLSearchParams
28
28
  | Schema
@@ -72,62 +72,56 @@ export type HttpSearchParamsSchemaName<Schema extends HttpSearchParamsSchema> =
72
72
  keyof Schema & string
73
73
  >;
74
74
 
75
- type PrimitiveHttpSearchParamsSerialized<Type> = Type extends HttpSearchParamsSchema[string]
76
- ? Type
77
- : Type extends (infer ArrayItem)[]
78
- ? ArrayItem extends (infer _InternalArrayItem)[]
79
- ? never
80
- : PrimitiveHttpSearchParamsSerialized<ArrayItem>[]
81
- : Type extends number
82
- ? `${number}`
83
- : Type extends boolean
84
- ? `${boolean}`
85
- : Type extends null
86
- ? undefined
87
- : never;
75
+ type PrimitiveHttpSearchParamsSerialized<Type> = [Type] extends [never]
76
+ ? never
77
+ : Type extends number
78
+ ? `${number}`
79
+ : Type extends boolean
80
+ ? `${boolean}`
81
+ : Type extends null
82
+ ? 'null'
83
+ : Type extends symbol
84
+ ? never
85
+ : Type extends HttpSearchParamsSchema[string]
86
+ ? Type
87
+ : Type extends (infer ArrayItem)[]
88
+ ? ArrayItem extends (infer _InternalArrayItem)[]
89
+ ? string
90
+ : PrimitiveHttpSearchParamsSerialized<ArrayItem>[]
91
+ : string;
88
92
 
89
93
  /**
90
94
  * Recursively converts a schema to its
91
- * {@link https://developer.mozilla.org/docs/Web/API/URLSearchParams URLSearchParams}-serialized version. Numbers and
92
- * booleans are converted to `${number}` and `${boolean}` respectively, null becomes undefined and not serializable
93
- * values are excluded, such as functions and dates.
95
+ * {@link https://developer.mozilla.org/docs/Web/API/URLSearchParams URLSearchParams}-serialized version. Numbers,
96
+ * booleans, and null are converted to `${number}`, `${boolean}`, and 'null' respectively, and other values become
97
+ * strings.
94
98
  *
95
99
  * @example
96
100
  * import { type HttpSearchParamsSerialized } from '@zimic/http';
97
101
  *
98
102
  * type Params = HttpSearchParamsSerialized<{
99
- * query: string | null;
103
+ * query?: string;
104
+ * order: 'asc' | 'desc' | null;
100
105
  * page?: number;
101
106
  * full?: boolean;
102
- * date: Date;
103
- * method: () => void;
104
107
  * }>;
105
108
  * // {
106
- * // query: string | undefined;
109
+ * // query?: string;
110
+ * // order: 'asc' | 'desc' | 'null';
107
111
  * // page?: `${number}`;
108
112
  * // full?: "false" | "true";
109
113
  * // }
110
114
  */
111
- export type HttpSearchParamsSerialized<Type> = Type extends HttpSearchParamsSchema
112
- ? Type
113
- : Type extends (infer _ArrayItem)[]
114
- ? never
115
- : Type extends Date
116
- ? never
117
- : Type extends (...parameters: never[]) => unknown
118
- ? never
119
- : Type extends symbol
120
- ? never
121
- : Type extends Map<infer _Key, infer _Value>
122
- ? never
123
- : Type extends Set<infer _Value>
124
- ? never
125
- : Type extends object
126
- ? {
127
- [Key in keyof Type as IfNever<
128
- PrimitiveHttpSearchParamsSerialized<Type[Key]>,
129
- never,
130
- Key
131
- >]: PrimitiveHttpSearchParamsSerialized<Type[Key]>;
132
- }
133
- : never;
115
+ export type HttpSearchParamsSerialized<Type> = [Type] extends [never]
116
+ ? never
117
+ : Type extends HttpSearchParamsSchema
118
+ ? Type
119
+ : Type extends object
120
+ ? {
121
+ [Key in keyof Type as IfNever<
122
+ PrimitiveHttpSearchParamsSerialized<Type[Key]>,
123
+ never,
124
+ Key
125
+ >]: PrimitiveHttpSearchParamsSerialized<Type[Key]>;
126
+ }
127
+ : never;
@@ -264,16 +264,6 @@ function processPendingRequestComponentActions(component: RequestComponent, cont
264
264
  return { bodyQuestionToken };
265
265
  }
266
266
 
267
- function wrapRequestComponentType(type: ts.TypeNode, context: TypeTransformContext) {
268
- context.typeImports.http.add('HttpSchema');
269
-
270
- const httpSchemaRequestWrapper = ts.factory.createQualifiedName(
271
- ts.factory.createIdentifier('HttpSchema'),
272
- ts.factory.createIdentifier('Request'),
273
- );
274
- return ts.factory.createTypeReferenceNode(httpSchemaRequestWrapper, [type]);
275
- }
276
-
277
267
  function normalizeRequestComponent(component: Component, context: TypeTransformContext) {
278
268
  /* istanbul ignore if -- @preserve
279
269
  * Component group members in `requests` are always expected the be request components. */
@@ -289,7 +279,7 @@ function normalizeRequestComponent(component: Component, context: TypeTransformC
289
279
  component.modifiers,
290
280
  component.name,
291
281
  component.questionToken,
292
- wrapRequestComponentType(newType, context),
282
+ newType,
293
283
  );
294
284
  }
295
285
 
@@ -10,10 +10,7 @@ export interface TypePathFilters {
10
10
  type HttpTypeImportName =
11
11
  | 'HttpSchema'
12
12
  | 'HttpFormData'
13
- | 'HttpFormDataSerialized'
14
13
  | 'HttpSearchParams'
15
- | 'HttpSearchParamsSerialized'
16
- | 'HttpHeadersSerialized'
17
14
  | 'HttpStatusCode'
18
15
  | 'MergeHttpResponsesByStatusCode';
19
16
 
@@ -65,22 +65,10 @@ function isRequestHeaders(node: ts.Node): node is RequestHeaders {
65
65
  return isRequestMember(node) && node.name.text === 'headers' && ts.isTypeLiteralNode(node.type);
66
66
  }
67
67
 
68
- type NormalizedRequestHeaders = Override<
69
- RequestHeaders,
70
- {
71
- type: Override<ts.TypeReferenceNode, { typeArguments: ts.NodeArray<ts.TypeLiteralNode> }>;
72
- }
73
- >;
68
+ type NormalizedRequestHeaders = Override<RequestHeaders, { type: ts.TypeLiteralNode }>;
74
69
 
75
70
  function isNormalizedRequestHeaders(node: ts.Node): node is NormalizedRequestHeaders {
76
- return (
77
- isRequestMember(node) &&
78
- node.name.text === 'headers' &&
79
- ts.isTypeReferenceNode(node.type) &&
80
- node.type.typeArguments !== undefined &&
81
- node.type.typeArguments.length === 1 &&
82
- ts.isTypeLiteralNode(node.type.typeArguments[0])
83
- );
71
+ return isRequestMember(node) && node.name.text === 'headers' && ts.isTypeLiteralNode(node.type);
84
72
  }
85
73
 
86
74
  type RequestParameters = Override<RequestMember, { type: ts.TypeLiteralNode }>;
@@ -170,23 +158,17 @@ function removeRedundantNullUnionIfNecessary(type: ts.TypeNode) {
170
158
 
171
159
  function wrapFormDataContentType(type: ts.TypeNode, context: TypeTransformContext) {
172
160
  context.typeImports.http.add('HttpFormData');
173
- context.typeImports.http.add('HttpFormDataSerialized');
174
161
 
175
162
  return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('HttpFormData'), [
176
- ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('HttpFormDataSerialized'), [
177
- renameComponentReferences(type, context),
178
- ]),
163
+ renameComponentReferences(type, context),
179
164
  ]);
180
165
  }
181
166
 
182
167
  function wrapURLEncodedContentType(type: ts.TypeNode, context: TypeTransformContext) {
183
168
  context.typeImports.http.add('HttpSearchParams');
184
- context.typeImports.http.add('HttpSearchParamsSerialized');
185
169
 
186
170
  return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('HttpSearchParams'), [
187
- ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('HttpSearchParamsSerialized'), [
188
- renameComponentReferences(type, context),
189
- ]),
171
+ renameComponentReferences(type, context),
190
172
  ]);
191
173
  }
192
174
 
@@ -224,18 +206,7 @@ function normalizeRequestBodyMember(
224
206
  };
225
207
  }
226
208
 
227
- function wrapHeadersType(type: ts.TypeLiteralNode, context: TypeTransformContext): NormalizedRequestHeaders['type'] {
228
- context.typeImports.http.add('HttpHeadersSerialized');
229
-
230
- const serializedWrapper = ts.factory.createIdentifier('HttpHeadersSerialized');
231
-
232
- return ts.factory.createTypeReferenceNode(
233
- serializedWrapper,
234
- ts.factory.createNodeArray([type]) satisfies NormalizedRequestHeaders['type']['typeArguments'],
235
- ) as NormalizedRequestHeaders['type'];
236
- }
237
-
238
- function normalizeHeaders(headers: ts.TypeLiteralNode, context: TypeTransformContext) {
209
+ function normalizeHeaders(headers: ts.TypeLiteralNode) {
239
210
  const newHeaderMembers = headers.members.filter((header) => {
240
211
  if (ts.isIndexSignatureDeclaration(header)) {
241
212
  return false;
@@ -253,19 +224,15 @@ function normalizeHeaders(headers: ts.TypeLiteralNode, context: TypeTransformCon
253
224
  return undefined;
254
225
  }
255
226
 
256
- const newHeaders = ts.factory.updateTypeLiteralNode(headers, ts.factory.createNodeArray(newHeaderMembers));
257
- return wrapHeadersType(newHeaders, context);
227
+ return ts.factory.updateTypeLiteralNode(headers, ts.factory.createNodeArray(newHeaderMembers));
258
228
  }
259
229
 
260
- function normalizeRequestHeaders(
261
- requestHeader: ts.TypeElement,
262
- context: TypeTransformContext,
263
- ): NormalizedRequestHeaders | undefined {
230
+ function normalizeRequestHeaders(requestHeader: ts.TypeElement): NormalizedRequestHeaders | undefined {
264
231
  if (!isRequestHeaders(requestHeader)) {
265
232
  return undefined;
266
233
  }
267
234
 
268
- const newType = normalizeHeaders(requestHeader.type, context);
235
+ const newType = normalizeHeaders(requestHeader.type);
269
236
 
270
237
  if (!newType) {
271
238
  return undefined;
@@ -283,20 +250,16 @@ function normalizeRequestHeaders(
283
250
  function createHeaderForUnionByContentType(
284
251
  existingHeader: NormalizedRequestHeaders | undefined,
285
252
  contentTypeName: string,
286
- context: TypeTransformContext,
287
253
  ) {
288
- const existingHeaderMembers = existingHeader ? existingHeader.type.typeArguments[0].members : [];
254
+ const existingHeaderMembers = existingHeader?.type.members ?? [];
289
255
 
290
256
  const contentTypeIdentifier = ts.factory.createIdentifier('"content-type"');
291
257
  const contentTypeValue = ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(contentTypeName));
292
258
 
293
- const newHeaderType = wrapHeadersType(
294
- ts.factory.createTypeLiteralNode([
295
- ts.factory.createPropertySignature(undefined, contentTypeIdentifier, undefined, contentTypeValue),
296
- ...existingHeaderMembers,
297
- ]),
298
- context,
299
- );
259
+ const newHeaderType = ts.factory.createTypeLiteralNode([
260
+ ts.factory.createPropertySignature(undefined, contentTypeIdentifier, undefined, contentTypeValue),
261
+ ...existingHeaderMembers,
262
+ ]);
300
263
 
301
264
  return ts.factory.createPropertySignature(
302
265
  existingHeader?.modifiers,
@@ -321,7 +284,7 @@ export function normalizeContentType(
321
284
  return contentType;
322
285
  }
323
286
 
324
- const newHeader = contentType.members.map((member) => normalizeRequestHeaders(member, context)).find(isDefined);
287
+ const newHeader = contentType.members.map(normalizeRequestHeaders).find(isDefined);
325
288
 
326
289
  const newBodyMembers = contentType.members.flatMap((body) => {
327
290
  if (isContentPropertySignature(body)) {
@@ -338,7 +301,7 @@ export function normalizeContentType(
338
301
  return ts.factory.updateTypeLiteralNode(contentType, ts.factory.createNodeArray(newMembers));
339
302
  } else {
340
303
  const bodyMemberUnionTypes = newBodyMembers.map((bodyMember) => {
341
- const headerMember = createHeaderForUnionByContentType(newHeader, bodyMember.contentTypeName, context);
304
+ const headerMember = createHeaderForUnionByContentType(newHeader, bodyMember.contentTypeName);
342
305
  return ts.factory.createTypeLiteralNode([headerMember, bodyMember.propertySignature]);
343
306
  });
344
307
 
@@ -366,29 +329,18 @@ function normalizeRequest(request: MethodMember, context: TypeTransformContext)
366
329
  return ts.factory.updatePropertySignature(request, request.modifiers, newIdentifier, undefined, newType);
367
330
  }
368
331
 
369
- function wrapResponseType(type: ts.TypeNode, context: TypeTransformContext) {
370
- context.typeImports.http.add('HttpSchema');
371
-
372
- const httpSchemaResponseWrapper = ts.factory.createQualifiedName(
373
- ts.factory.createIdentifier('HttpSchema'),
374
- ts.factory.createIdentifier('Response'),
375
- );
376
- return ts.factory.createTypeReferenceNode(httpSchemaResponseWrapper, [type]);
377
- }
378
-
379
332
  function normalizeResponseType(
380
333
  responseType: ts.TypeNode,
381
334
  context: TypeTransformContext,
382
- options: { isComponent: boolean; questionToken?: ts.QuestionToken },
335
+ options: { questionToken?: ts.QuestionToken },
383
336
  ) {
384
- const { isComponent, questionToken } = options;
337
+ const { questionToken } = options;
385
338
 
386
339
  if (!ts.isTypeLiteralNode(responseType)) {
387
340
  return responseType;
388
341
  }
389
342
 
390
- const newType = normalizeContentType(responseType, context, { bodyQuestionToken: questionToken });
391
- return isComponent ? wrapResponseType(newType, context) : newType;
343
+ return normalizeContentType(responseType, context, { bodyQuestionToken: questionToken });
392
344
  }
393
345
 
394
346
  const NON_NUMERIC_RESPONSE_STATUS_TO_MAPPED_TYPE: Record<string, string | undefined> = {
@@ -414,7 +366,6 @@ export function normalizeResponse(
414
366
  }
415
367
 
416
368
  const newType = normalizeResponseType(response.type, context, {
417
- isComponent,
418
369
  questionToken: response.questionToken,
419
370
  });
420
371
 
@@ -552,17 +503,12 @@ function normalizeRequestQueryWithParameters(requestMember: RequestMember, conte
552
503
 
553
504
  const newType = renameComponentReferences(requestMember.type, context);
554
505
 
555
- context.typeImports.http.add('HttpSearchParamsSerialized');
556
-
557
- const serializedWrapper = ts.factory.createIdentifier('HttpSearchParamsSerialized');
558
- const wrappedNewType = ts.factory.createTypeReferenceNode(serializedWrapper, [newType]);
559
-
560
506
  return ts.factory.updatePropertySignature(
561
507
  requestMember,
562
508
  requestMember.modifiers,
563
509
  newIdentifier,
564
510
  newQuestionToken,
565
- wrappedNewType,
511
+ newType,
566
512
  );
567
513
  }
568
514
 
@@ -572,17 +518,12 @@ function normalizeRequestHeadersWithParameters(requestMember: RequestMember, con
572
518
 
573
519
  const newType = renameComponentReferences(requestMember.type, context);
574
520
 
575
- context.typeImports.http.add('HttpHeadersSerialized');
576
-
577
- const serializedWrapper = ts.factory.createIdentifier('HttpHeadersSerialized');
578
- const wrappedNewType = ts.factory.createTypeReferenceNode(serializedWrapper, [newType]);
579
-
580
521
  return ts.factory.updatePropertySignature(
581
522
  requestMember,
582
523
  requestMember.modifiers,
583
524
  newIdentifier,
584
525
  newQuestionToken,
585
- wrappedNewType,
526
+ newType,
586
527
  );
587
528
  }
588
529
 
@@ -602,16 +543,10 @@ function normalizeRequestMemberWithParameters(requestMember: ts.TypeElement, con
602
543
  }
603
544
 
604
545
  function mergeRequestHeadersMember(headers: NormalizedRequestHeaders, otherHeaders: NormalizedRequestHeaders) {
605
- const headersTypeLiteral = headers.type.typeArguments[0];
606
- const otherHeadersTypeLiteral = otherHeaders.type.typeArguments[0];
607
-
608
- const newType = ts.factory.updateTypeReferenceNode(
546
+ const newType = ts.factory.updateTypeLiteralNode(
609
547
  headers.type,
610
- headers.type.typeName,
611
- ts.factory.createNodeArray([
612
- ts.factory.createTypeLiteralNode([...otherHeadersTypeLiteral.members, ...headersTypeLiteral.members]),
613
- ]) satisfies NormalizedRequestHeaders['type']['typeArguments'],
614
- ) as NormalizedRequestHeaders['type'];
548
+ ts.factory.createNodeArray([...otherHeaders.type.members, ...headers.type.members]),
549
+ );
615
550
 
616
551
  return ts.factory.updatePropertySignature(
617
552
  headers,
@@ -36,16 +36,6 @@ function isOperation(node: ts.Node): node is Operation {
36
36
  );
37
37
  }
38
38
 
39
- function wrapOperationType(type: ts.TypeLiteralNode, context: TypeTransformContext) {
40
- context.typeImports.http.add('HttpSchema');
41
-
42
- const httpSchemaMethodWrapper = ts.factory.createQualifiedName(
43
- ts.factory.createIdentifier('HttpSchema'),
44
- ts.factory.createIdentifier('Method'),
45
- );
46
- return ts.factory.createTypeReferenceNode(httpSchemaMethodWrapper, [type]);
47
- }
48
-
49
39
  function normalizeOperation(operation: ts.TypeElement, context: TypeTransformContext) {
50
40
  /* istanbul ignore if -- @preserve
51
41
  * Operation members are always expected to be an operation. */
@@ -60,7 +50,7 @@ function normalizeOperation(operation: ts.TypeElement, context: TypeTransformCon
60
50
  operation.modifiers,
61
51
  operation.name,
62
52
  operation.questionToken,
63
- wrapOperationType(newType, context),
53
+ newType,
64
54
  );
65
55
  }
66
56
 
@@ -41,16 +41,6 @@ function normalizePathNameWithParameters(pathName: string) {
41
41
  return pathName.replace(/{([^}]+)}/g, ':$1');
42
42
  }
43
43
 
44
- function wrapComponentPathType(type: ts.TypeNode, context: TypeTransformContext) {
45
- context.typeImports.http.add('HttpSchema');
46
-
47
- const httpSchemaMethodsWrapper = ts.factory.createQualifiedName(
48
- ts.factory.createIdentifier('HttpSchema'),
49
- ts.factory.createIdentifier('Methods'),
50
- );
51
- return ts.factory.createTypeReferenceNode(httpSchemaMethodsWrapper, [type]);
52
- }
53
-
54
44
  export function normalizePath(
55
45
  path: ts.TypeElement,
56
46
  context: TypeTransformContext,
@@ -83,13 +73,7 @@ export function normalizePath(
83
73
  newType = renameComponentReferences(path.type, context);
84
74
  }
85
75
 
86
- return ts.factory.updatePropertySignature(
87
- path,
88
- path.modifiers,
89
- newIdentifier,
90
- path.questionToken,
91
- isComponent ? wrapComponentPathType(newType, context) : newType,
92
- );
76
+ return ts.factory.updatePropertySignature(path, path.modifiers, newIdentifier, path.questionToken, newType);
93
77
  }
94
78
 
95
79
  function wrapPathsType(type: ts.TypeLiteralNode, context: TypeTransformContext) {
@@ -1,7 +1,7 @@
1
1
  import { Default, DefaultNoExclude, IfNever, ReplaceBy } from '@zimic/utils/types';
2
- import { JSONSerialized, JSONValue } from '@zimic/utils/types/json';
2
+ import { JSONValue } from '@zimic/utils/types/json';
3
3
 
4
- import { HttpMethodSchema, HttpStatusCode } from '@/types/schema';
4
+ import { HttpMethodSchema, HttpRequestSchema, HttpResponseSchema, HttpStatusCode } from '@/types/schema';
5
5
 
6
6
  import HttpFormData from '../formData/HttpFormData';
7
7
  import { HttpFormDataSchema } from '../formData/types';
@@ -17,16 +17,13 @@ export type HttpBody = JSONValue | HttpFormData<any> | HttpSearchParams<any> | B
17
17
  export namespace HttpBody {
18
18
  /** A loose version of the HTTP body type. JSON values are not strictly typed. */
19
19
  export type Loose = ReplaceBy<HttpBody, JSONValue, JSONValue.Loose>;
20
-
21
- /** Convert an HTTP body to be strictly typed. JSON values are serialized to their strict form. */
22
- export type AsStrict<Type> = Type extends Exclude<HttpBody, JSONValue> ? Type : JSONSerialized<Type>;
23
20
  }
24
21
 
25
22
  /**
26
23
  * An HTTP headers object with a strictly-typed schema. Fully compatible with the built-in
27
24
  * {@link https://developer.mozilla.org/docs/Web/API/Headers `Headers`} class.
28
25
  */
29
- export type StrictHeaders<Schema extends HttpHeadersSchema = HttpHeadersSchema> = Pick<
26
+ export type StrictHeaders<Schema extends HttpHeadersSchema.Loose = HttpHeadersSchema.Loose> = Pick<
30
27
  HttpHeaders<Schema>,
31
28
  keyof Headers
32
29
  >;
@@ -35,7 +32,7 @@ export type StrictHeaders<Schema extends HttpHeadersSchema = HttpHeadersSchema>
35
32
  * An HTTP search params object with a strictly-typed schema. Fully compatible with the built-in
36
33
  * {@link https://developer.mozilla.org/docs/Web/API/URLSearchParams `URLSearchParams`} class.
37
34
  */
38
- export type StrictURLSearchParams<Schema extends HttpSearchParamsSchema = HttpSearchParamsSchema> = Pick<
35
+ export type StrictURLSearchParams<Schema extends HttpSearchParamsSchema.Loose = HttpSearchParamsSchema.Loose> = Pick<
39
36
  HttpSearchParams<Schema>,
40
37
  keyof URLSearchParams
41
38
  >;
@@ -44,7 +41,7 @@ export type StrictURLSearchParams<Schema extends HttpSearchParamsSchema = HttpSe
44
41
  * An HTTP form data object with a strictly-typed schema. Fully compatible with the built-in
45
42
  * {@link https://developer.mozilla.org/docs/Web/API/FormData `FormData`} class.
46
43
  */
47
- export type StrictFormData<Schema extends HttpFormDataSchema = HttpFormDataSchema> = Pick<
44
+ export type StrictFormData<Schema extends HttpFormDataSchema.Loose = HttpFormDataSchema.Loose> = Pick<
48
45
  HttpFormData<Schema>,
49
46
  keyof FormData
50
47
  >;
@@ -54,8 +51,8 @@ export type StrictFormData<Schema extends HttpFormDataSchema = HttpFormDataSchem
54
51
  * {@link https://developer.mozilla.org/docs/Web/API/Request `Request`} class.
55
52
  */
56
53
  export interface HttpRequest<
57
- StrictBody extends HttpBody.Loose = HttpBody,
58
- StrictHeadersSchema extends HttpHeadersSchema = HttpHeadersSchema,
54
+ StrictBody extends HttpBody.Loose = HttpBody.Loose,
55
+ StrictHeadersSchema extends HttpHeadersSchema.Loose = HttpHeadersSchema.Loose,
59
56
  > extends Request {
60
57
  headers: StrictHeaders<StrictHeadersSchema>;
61
58
  text: () => Promise<StrictBody extends string ? StrictBody : string>;
@@ -75,9 +72,9 @@ export interface HttpRequest<
75
72
  * {@link https://developer.mozilla.org/docs/Web/API/Response `Response`} class.
76
73
  */
77
74
  export interface HttpResponse<
78
- StrictBody extends HttpBody.Loose = HttpBody,
75
+ StrictBody extends HttpBody.Loose = HttpBody.Loose,
76
+ StrictHeadersSchema extends HttpHeadersSchema.Loose = HttpHeadersSchema.Loose,
79
77
  StatusCode extends number = number,
80
- StrictHeadersSchema extends HttpHeadersSchema = HttpHeadersSchema,
81
78
  > extends Response {
82
79
  ok: StatusCode extends HttpStatusCode.Information | HttpStatusCode.Success | HttpStatusCode.Redirection
83
80
  ? true
@@ -96,13 +93,35 @@ export interface HttpResponse<
96
93
  clone: () => this;
97
94
  }
98
95
 
99
- export type HttpRequestHeadersSchema<MethodSchema extends HttpMethodSchema> = Default<
100
- Default<MethodSchema['request']>['headers']
101
- >;
96
+ type HttpRequestHeadersSchemaFromBody<
97
+ RequestSchema extends HttpRequestSchema,
98
+ DefaultHeadersSchema,
99
+ > = 'body' extends keyof RequestSchema
100
+ ? [RequestSchema['body']] extends [never]
101
+ ? DefaultHeadersSchema
102
+ : [Extract<RequestSchema['body'], BodyInit | HttpFormData | HttpSearchParams>] extends [never]
103
+ ? 'headers' extends keyof RequestSchema
104
+ ? [RequestSchema['headers']] extends [never]
105
+ ? DefaultHeadersSchema
106
+ : 'content-type' extends keyof Default<RequestSchema['headers']>
107
+ ? DefaultHeadersSchema
108
+ : { 'content-type': 'application/json' }
109
+ : { 'content-type': 'application/json' }
110
+ : DefaultHeadersSchema
111
+ : DefaultHeadersSchema;
102
112
 
103
- export type HttpRequestSearchParamsSchema<MethodSchema extends HttpMethodSchema> = Default<
104
- Default<MethodSchema['request']>['searchParams']
105
- >;
113
+ export type HttpRequestHeadersSchema<MethodSchema extends HttpMethodSchema> =
114
+ 'headers' extends keyof MethodSchema['request']
115
+ ? [MethodSchema['request']['headers']] extends [never]
116
+ ? HttpRequestHeadersSchemaFromBody<Default<MethodSchema['request']>, never>
117
+ :
118
+ | (MethodSchema['request']['headers'] &
119
+ HttpRequestHeadersSchemaFromBody<Default<MethodSchema['request']>, {}>)
120
+ | (MethodSchema['request']['headers'] & undefined)
121
+ : HttpRequestHeadersSchemaFromBody<Default<MethodSchema['request']>, never>;
122
+
123
+ export type HttpRequestSearchParamsSchema<MethodSchema extends HttpMethodSchema> =
124
+ 'searchParams' extends keyof MethodSchema['request'] ? Default<MethodSchema['request']>['searchParams'] : never;
106
125
 
107
126
  export type HttpRequestBodySchema<MethodSchema extends HttpMethodSchema> = ReplaceBy<
108
127
  ReplaceBy<IfNever<DefaultNoExclude<Default<MethodSchema['request']>['body']>, null>, undefined, null>,
@@ -110,10 +129,32 @@ export type HttpRequestBodySchema<MethodSchema extends HttpMethodSchema> = Repla
110
129
  Blob
111
130
  >;
112
131
 
132
+ type HttpResponseHeadersSchemaFromBody<
133
+ ResponseSchema extends HttpResponseSchema,
134
+ DefaultHeadersSchema,
135
+ > = 'body' extends keyof ResponseSchema
136
+ ? [ResponseSchema['body']] extends [never]
137
+ ? DefaultHeadersSchema
138
+ : [Extract<ResponseSchema['body'], BodyInit | HttpSearchParams | HttpFormData>] extends [never]
139
+ ? 'headers' extends keyof ResponseSchema
140
+ ? [ResponseSchema['headers']] extends [never]
141
+ ? DefaultHeadersSchema
142
+ : 'content-type' extends keyof Default<ResponseSchema['headers']>
143
+ ? DefaultHeadersSchema
144
+ : { 'content-type': 'application/json' }
145
+ : { 'content-type': 'application/json' }
146
+ : DefaultHeadersSchema
147
+ : DefaultHeadersSchema;
148
+
113
149
  export type HttpResponseHeadersSchema<
114
150
  MethodSchema extends HttpMethodSchema,
115
151
  StatusCode extends HttpStatusCode,
116
- > = Default<DefaultNoExclude<Default<Default<MethodSchema['response']>[StatusCode]>['headers']>>;
152
+ > = 'headers' extends keyof Default<MethodSchema['response']>[StatusCode]
153
+ ? [Default<MethodSchema['response']>[StatusCode]] extends [never]
154
+ ? HttpResponseHeadersSchemaFromBody<Default<Default<MethodSchema['response']>[StatusCode]>, never>
155
+ : Default<Default<MethodSchema['response']>[StatusCode]>['headers'] &
156
+ HttpResponseHeadersSchemaFromBody<Default<Default<MethodSchema['response']>[StatusCode]>, {}>
157
+ : HttpResponseHeadersSchemaFromBody<Default<Default<MethodSchema['response']>[StatusCode]>, never>;
117
158
 
118
159
  export type HttpResponseBodySchema<
119
160
  MethodSchema extends HttpMethodSchema,