@zimic/http 0.0.1-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.
- package/LICENSE.md +16 -0
- package/README.md +230 -0
- package/dist/chunk-VHQRAQPQ.mjs +1371 -0
- package/dist/chunk-VHQRAQPQ.mjs.map +1 -0
- package/dist/chunk-VUDGONB5.js +1382 -0
- package/dist/chunk-VUDGONB5.js.map +1 -0
- package/dist/cli.js +116 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.mjs +109 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.ts +1306 -0
- package/dist/index.js +544 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +537 -0
- package/dist/index.mjs.map +1 -0
- package/dist/typegen.d.ts +86 -0
- package/dist/typegen.js +12 -0
- package/dist/typegen.js.map +1 -0
- package/dist/typegen.mjs +3 -0
- package/dist/typegen.mjs.map +1 -0
- package/index.d.ts +1 -0
- package/package.json +110 -0
- package/src/cli/cli.ts +92 -0
- package/src/cli/index.ts +4 -0
- package/src/cli/typegen/openapi.ts +24 -0
- package/src/formData/HttpFormData.ts +300 -0
- package/src/formData/types.ts +110 -0
- package/src/headers/HttpHeaders.ts +217 -0
- package/src/headers/types.ts +65 -0
- package/src/index.ts +55 -0
- package/src/pathParams/types.ts +67 -0
- package/src/searchParams/HttpSearchParams.ts +258 -0
- package/src/searchParams/types.ts +133 -0
- package/src/typegen/index.ts +12 -0
- package/src/typegen/namespace/TypegenNamespace.ts +18 -0
- package/src/typegen/openapi/generate.ts +168 -0
- package/src/typegen/openapi/transform/components.ts +481 -0
- package/src/typegen/openapi/transform/context.ts +67 -0
- package/src/typegen/openapi/transform/filters.ts +71 -0
- package/src/typegen/openapi/transform/imports.ts +15 -0
- package/src/typegen/openapi/transform/io.ts +86 -0
- package/src/typegen/openapi/transform/methods.ts +803 -0
- package/src/typegen/openapi/transform/operations.ts +120 -0
- package/src/typegen/openapi/transform/paths.ts +119 -0
- package/src/typegen/openapi/utils/types.ts +45 -0
- package/src/types/arrays.d.ts +4 -0
- package/src/types/json.ts +89 -0
- package/src/types/objects.d.ts +14 -0
- package/src/types/requests.ts +96 -0
- package/src/types/schema.ts +834 -0
- package/src/types/strings.d.ts +9 -0
- package/src/types/utils.ts +64 -0
- package/src/utils/console.ts +7 -0
- package/src/utils/data.ts +13 -0
- package/src/utils/files.ts +28 -0
- package/src/utils/imports.ts +12 -0
- package/src/utils/prettier.ts +13 -0
- package/src/utils/strings.ts +3 -0
- package/src/utils/time.ts +25 -0
- package/src/utils/urls.ts +52 -0
- package/typegen.d.ts +1 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
import { Override } from '@/types/utils';
|
|
4
|
+
import { isDefined } from '@/utils/data';
|
|
5
|
+
|
|
6
|
+
import { TypeTransformContext } from './context';
|
|
7
|
+
import { normalizeTypeLiteralMethodType } from './methods';
|
|
8
|
+
|
|
9
|
+
export function createOperationsIdentifierText(serviceName: string) {
|
|
10
|
+
return `${serviceName}Operations`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createOperationsIdentifier(serviceName: string) {
|
|
14
|
+
return ts.factory.createIdentifier(createOperationsIdentifierText(serviceName));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type OperationsDeclaration = ts.InterfaceDeclaration;
|
|
18
|
+
|
|
19
|
+
export function isOperationsDeclaration(node: ts.Node | undefined): node is OperationsDeclaration {
|
|
20
|
+
return node !== undefined && ts.isInterfaceDeclaration(node) && node.name.text === 'operations';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type Operation = Override<
|
|
24
|
+
ts.PropertySignature,
|
|
25
|
+
{
|
|
26
|
+
type: ts.TypeLiteralNode;
|
|
27
|
+
name: ts.Identifier | ts.StringLiteral;
|
|
28
|
+
}
|
|
29
|
+
>;
|
|
30
|
+
|
|
31
|
+
function isOperation(node: ts.Node): node is Operation {
|
|
32
|
+
return (
|
|
33
|
+
ts.isPropertySignature(node) &&
|
|
34
|
+
(ts.isIdentifier(node.name) || ts.isStringLiteral(node.name)) &&
|
|
35
|
+
node.type !== undefined &&
|
|
36
|
+
ts.isTypeLiteralNode(node.type)
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function wrapOperationType(type: ts.TypeLiteralNode, context: TypeTransformContext) {
|
|
41
|
+
context.typeImports.http.add('HttpSchema');
|
|
42
|
+
|
|
43
|
+
const httpSchemaMethodWrapper = ts.factory.createQualifiedName(
|
|
44
|
+
ts.factory.createIdentifier('HttpSchema'),
|
|
45
|
+
ts.factory.createIdentifier('Method'),
|
|
46
|
+
);
|
|
47
|
+
return ts.factory.createTypeReferenceNode(httpSchemaMethodWrapper, [type]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalizeOperation(operation: ts.TypeElement, context: TypeTransformContext) {
|
|
51
|
+
/* istanbul ignore if -- @preserve
|
|
52
|
+
* Operation members are always expected to be an operation. */
|
|
53
|
+
if (!isOperation(operation)) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const newType = normalizeTypeLiteralMethodType(operation.type, context);
|
|
58
|
+
|
|
59
|
+
return ts.factory.updatePropertySignature(
|
|
60
|
+
operation,
|
|
61
|
+
operation.modifiers,
|
|
62
|
+
operation.name,
|
|
63
|
+
operation.questionToken,
|
|
64
|
+
wrapOperationType(newType, context),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function normalizeOperations(operations: OperationsDeclaration, context: TypeTransformContext) {
|
|
69
|
+
const newIdentifier = createOperationsIdentifier(context.serviceName);
|
|
70
|
+
|
|
71
|
+
const newMembers = operations.members.map((operation) => normalizeOperation(operation, context)).filter(isDefined);
|
|
72
|
+
|
|
73
|
+
return ts.factory.updateInterfaceDeclaration(
|
|
74
|
+
operations,
|
|
75
|
+
operations.modifiers,
|
|
76
|
+
newIdentifier,
|
|
77
|
+
operations.typeParameters,
|
|
78
|
+
operations.heritageClauses,
|
|
79
|
+
newMembers,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function removeOperationIfUnreferenced(operation: ts.TypeElement, context: TypeTransformContext) {
|
|
84
|
+
/* istanbul ignore if -- @preserve
|
|
85
|
+
* Operation members are always expected to be an operation. */
|
|
86
|
+
if (!isOperation(operation)) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const operationName = operation.name.text;
|
|
91
|
+
const isReferenced = context.referencedTypes.operations.has(operationName);
|
|
92
|
+
|
|
93
|
+
if (isReferenced) {
|
|
94
|
+
context.referencedTypes.operations.delete(operationName);
|
|
95
|
+
return operation;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function removeUnreferencedOperations(operations: OperationsDeclaration, context: TypeTransformContext) {
|
|
102
|
+
const newMembers = operations.members
|
|
103
|
+
.map((operation) => removeOperationIfUnreferenced(operation, context))
|
|
104
|
+
.filter(isDefined);
|
|
105
|
+
|
|
106
|
+
context.referencedTypes.operations.clear();
|
|
107
|
+
|
|
108
|
+
if (newMembers.length === 0) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return ts.factory.updateInterfaceDeclaration(
|
|
113
|
+
operations,
|
|
114
|
+
operations.modifiers,
|
|
115
|
+
operations.name,
|
|
116
|
+
operations.typeParameters,
|
|
117
|
+
operations.heritageClauses,
|
|
118
|
+
newMembers,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
import { Override } from '@/types/utils';
|
|
4
|
+
import { isDefined } from '@/utils/data';
|
|
5
|
+
|
|
6
|
+
import { renameComponentReferences } from './components';
|
|
7
|
+
import { TypeTransformContext } from './context';
|
|
8
|
+
import { normalizeMethod } from './methods';
|
|
9
|
+
|
|
10
|
+
export function createPathsIdentifier(serviceName: string) {
|
|
11
|
+
return ts.factory.createIdentifier(`${serviceName}Schema`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type PathsDeclaration = ts.InterfaceDeclaration | ts.TypeAliasDeclaration;
|
|
15
|
+
|
|
16
|
+
export function isPathsDeclaration(node: ts.Node | undefined): node is PathsDeclaration {
|
|
17
|
+
return (
|
|
18
|
+
node !== undefined &&
|
|
19
|
+
(ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) &&
|
|
20
|
+
node.name.text === 'paths'
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type Path = Override<
|
|
25
|
+
ts.PropertySignature,
|
|
26
|
+
{
|
|
27
|
+
type: ts.TypeLiteralNode | ts.IndexedAccessTypeNode;
|
|
28
|
+
name: ts.Identifier | ts.StringLiteral;
|
|
29
|
+
}
|
|
30
|
+
>;
|
|
31
|
+
|
|
32
|
+
function isPath(node: ts.TypeElement): node is Path {
|
|
33
|
+
return (
|
|
34
|
+
ts.isPropertySignature(node) &&
|
|
35
|
+
(ts.isIdentifier(node.name) || ts.isStringLiteral(node.name)) &&
|
|
36
|
+
node.type !== undefined &&
|
|
37
|
+
(ts.isTypeLiteralNode(node.type) || ts.isIndexedAccessTypeNode(node.type))
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizePathNameWithParameters(pathName: string) {
|
|
42
|
+
return pathName.replace(/{([^}]+)}/g, ':$1');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function wrapComponentPathType(type: ts.TypeNode, context: TypeTransformContext) {
|
|
46
|
+
context.typeImports.http.add('HttpSchema');
|
|
47
|
+
|
|
48
|
+
const httpSchemaMethodsWrapper = ts.factory.createQualifiedName(
|
|
49
|
+
ts.factory.createIdentifier('HttpSchema'),
|
|
50
|
+
ts.factory.createIdentifier('Methods'),
|
|
51
|
+
);
|
|
52
|
+
return ts.factory.createTypeReferenceNode(httpSchemaMethodsWrapper, [type]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function normalizePath(
|
|
56
|
+
path: ts.TypeElement,
|
|
57
|
+
context: TypeTransformContext,
|
|
58
|
+
options: { isComponent?: boolean } = {},
|
|
59
|
+
) {
|
|
60
|
+
const { isComponent = false } = options;
|
|
61
|
+
|
|
62
|
+
/* istanbul ignore if -- @preserve
|
|
63
|
+
* Path members are always expected to be a path. */
|
|
64
|
+
if (!isPath(path)) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const newPathName = isComponent ? path.name.text : normalizePathNameWithParameters(path.name.text);
|
|
69
|
+
const newIdentifier = isComponent ? path.name : ts.factory.createStringLiteral(newPathName);
|
|
70
|
+
|
|
71
|
+
let newType: ts.TypeNode;
|
|
72
|
+
|
|
73
|
+
if (ts.isTypeLiteralNode(path.type)) {
|
|
74
|
+
const newMethods = path.type.members
|
|
75
|
+
.map((method) => normalizeMethod(method, context, { pathName: newPathName }))
|
|
76
|
+
.filter(isDefined);
|
|
77
|
+
|
|
78
|
+
if (newMethods.length === 0) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
newType = ts.factory.updateTypeLiteralNode(path.type, ts.factory.createNodeArray(newMethods));
|
|
83
|
+
} else {
|
|
84
|
+
newType = renameComponentReferences(path.type, context);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return ts.factory.updatePropertySignature(
|
|
88
|
+
path,
|
|
89
|
+
path.modifiers,
|
|
90
|
+
newIdentifier,
|
|
91
|
+
path.questionToken,
|
|
92
|
+
isComponent ? wrapComponentPathType(newType, context) : newType,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function wrapPathsType(type: ts.TypeLiteralNode, context: TypeTransformContext) {
|
|
97
|
+
context.typeImports.http.add('HttpSchema');
|
|
98
|
+
|
|
99
|
+
const httpSchemaPathsWrapper = ts.factory.createIdentifier('HttpSchema');
|
|
100
|
+
return ts.factory.createTypeReferenceNode(httpSchemaPathsWrapper, [type]);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function normalizePaths(pathsOrTypeAlias: PathsDeclaration, context: TypeTransformContext) {
|
|
104
|
+
const newIdentifier = createPathsIdentifier(context.serviceName);
|
|
105
|
+
|
|
106
|
+
const paths = ts.isTypeAliasDeclaration(pathsOrTypeAlias)
|
|
107
|
+
? ts.factory.createInterfaceDeclaration(pathsOrTypeAlias.modifiers, pathsOrTypeAlias.name, undefined, undefined, [])
|
|
108
|
+
: pathsOrTypeAlias;
|
|
109
|
+
|
|
110
|
+
const newMembers = paths.members.map((path) => normalizePath(path, context)).filter(isDefined);
|
|
111
|
+
const newType = ts.factory.createTypeLiteralNode(newMembers);
|
|
112
|
+
|
|
113
|
+
return ts.factory.createTypeAliasDeclaration(
|
|
114
|
+
paths.modifiers,
|
|
115
|
+
newIdentifier,
|
|
116
|
+
paths.typeParameters,
|
|
117
|
+
wrapPathsType(newType, context),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
export function isNeverType(type: ts.TypeNode) {
|
|
4
|
+
return type.kind === ts.SyntaxKind.NeverKeyword;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function isUnknownType(type: ts.TypeNode) {
|
|
8
|
+
return type.kind === ts.SyntaxKind.UnknownKeyword;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function isNumericType(type: ts.TypeNode) {
|
|
12
|
+
return type.kind === ts.SyntaxKind.NumberKeyword;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function isBooleanType(type: ts.TypeNode) {
|
|
16
|
+
return type.kind === ts.SyntaxKind.BooleanKeyword;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isNullType(type: ts.TypeNode | ts.LiteralTypeNode['literal']) {
|
|
20
|
+
return type.kind === ts.SyntaxKind.NullKeyword;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function createBlobType() {
|
|
24
|
+
return ts.factory.createTypeReferenceNode('Blob');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createNullType() {
|
|
28
|
+
return ts.factory.createLiteralTypeNode(ts.factory.createNull());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createImportSpecifier(importName: string) {
|
|
32
|
+
return ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(importName));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function createImportDeclaration(
|
|
36
|
+
importSpecifiers: ts.ImportSpecifier[],
|
|
37
|
+
moduleName: string,
|
|
38
|
+
options: { typeOnly: boolean },
|
|
39
|
+
) {
|
|
40
|
+
return ts.factory.createImportDeclaration(
|
|
41
|
+
undefined,
|
|
42
|
+
ts.factory.createImportClause(options.typeOnly, undefined, ts.factory.createNamedImports(importSpecifiers)),
|
|
43
|
+
ts.factory.createStringLiteral(moduleName),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
type JSON = { [key: string]: JSON } | JSON[] | string | number | boolean | null | undefined;
|
|
2
|
+
|
|
3
|
+
namespace JSON {
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
+
export type Loose = Record<string, any> | Loose[] | string | number | boolean | null | undefined;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Represents or validates a type that is compatible with JSON.
|
|
10
|
+
*
|
|
11
|
+
* **IMPORTANT**: the input of `JSONValue` and all of its internal types must be declared inline or as a type aliases
|
|
12
|
+
* (`type`). They cannot be interfaces.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* import { type JSONValue } from '@zimic/http';
|
|
16
|
+
*
|
|
17
|
+
* // Can be used as a standalone type:
|
|
18
|
+
* const value: JSONValue = {
|
|
19
|
+
* name: 'example',
|
|
20
|
+
* tags: ['one', 'two'],
|
|
21
|
+
* };
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* import { type JSONValue } from '@zimic/http';
|
|
25
|
+
*
|
|
26
|
+
* // Can be used with a type argument to validate a JSON value:
|
|
27
|
+
* type ValidJSON = JSONValue<{
|
|
28
|
+
* id: string;
|
|
29
|
+
* email: string;
|
|
30
|
+
* createdAt: string;
|
|
31
|
+
* }>;
|
|
32
|
+
*
|
|
33
|
+
* // This results in a type error:
|
|
34
|
+
* type InvalidJSON = JSONValue<{
|
|
35
|
+
* id: string;
|
|
36
|
+
* email: string;
|
|
37
|
+
* createdAt: Date; // `Date` is not a valid JSON value.
|
|
38
|
+
* save: () => Promise<void>; // Functions are not valid JSON values.
|
|
39
|
+
* }>;
|
|
40
|
+
*/
|
|
41
|
+
export type JSONValue<Type extends JSON = JSON> = Type;
|
|
42
|
+
|
|
43
|
+
export namespace JSONValue {
|
|
44
|
+
/** A loose version of the JSON value type. JSON objects are not strictly typed. */
|
|
45
|
+
export type Loose<Type extends JSON.Loose = JSON.Loose> = Type;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Recursively converts a type to its JSON-serialized version. Dates are converted to strings and keys with non-JSON
|
|
50
|
+
* values are excluded.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* import { type JSONSerialized } from '@zimic/http';
|
|
54
|
+
*
|
|
55
|
+
* type SerializedUser = JSONSerialized<{
|
|
56
|
+
* id: string;
|
|
57
|
+
* email: string;
|
|
58
|
+
* createdAt: Date;
|
|
59
|
+
* save: () => Promise<void>;
|
|
60
|
+
* }>;
|
|
61
|
+
* // {
|
|
62
|
+
* // id: string;
|
|
63
|
+
* // email: string;
|
|
64
|
+
* // createdAt: string;
|
|
65
|
+
* // }
|
|
66
|
+
*/
|
|
67
|
+
export type JSONSerialized<Type> = Type extends JSONValue
|
|
68
|
+
? Type
|
|
69
|
+
: Type extends string | number | boolean | null | undefined
|
|
70
|
+
? Type
|
|
71
|
+
: Type extends Date
|
|
72
|
+
? string
|
|
73
|
+
: Type extends (...parameters: never[]) => unknown
|
|
74
|
+
? never
|
|
75
|
+
: Type extends symbol
|
|
76
|
+
? never
|
|
77
|
+
: Type extends Map<infer _Key, infer _Value>
|
|
78
|
+
? Record<string, never>
|
|
79
|
+
: Type extends Set<infer _Value>
|
|
80
|
+
? Record<string, never>
|
|
81
|
+
: Type extends (infer ArrayItem)[]
|
|
82
|
+
? JSONSerialized<ArrayItem>[]
|
|
83
|
+
: Type extends object
|
|
84
|
+
? {
|
|
85
|
+
[Key in keyof Type as [JSONSerialized<Type[Key]>] extends [never] ? never : Key]: JSONSerialized<
|
|
86
|
+
Type[Key]
|
|
87
|
+
>;
|
|
88
|
+
}
|
|
89
|
+
: never;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type ObjectKey<Type> = keyof Type & string;
|
|
2
|
+
type ObjectValue<Type> = Type[ObjectKey<Type>];
|
|
3
|
+
type ObjectEntry<Type> = [ObjectKey<Type>, ObjectValue<Type>];
|
|
4
|
+
|
|
5
|
+
interface ObjectConstructor {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
|
7
|
+
keys<Type>(object: Type): ObjectKey<Type>[];
|
|
8
|
+
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
|
10
|
+
values<Type>(object: Type): ObjectValue<Type>[];
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/method-signature-style
|
|
13
|
+
entries<Type>(object: Type): ObjectEntry<Type>[];
|
|
14
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { JSONSerialized, JSONValue } from '@/types/json';
|
|
2
|
+
import { HttpStatusCode } from '@/types/schema';
|
|
3
|
+
import { ReplaceBy } from '@/types/utils';
|
|
4
|
+
|
|
5
|
+
import HttpFormData from '../formData/HttpFormData';
|
|
6
|
+
import { HttpFormDataSchema } from '../formData/types';
|
|
7
|
+
import HttpHeaders from '../headers/HttpHeaders';
|
|
8
|
+
import { HttpHeadersSchema } from '../headers/types';
|
|
9
|
+
import HttpSearchParams from '../searchParams/HttpSearchParams';
|
|
10
|
+
import { HttpSearchParamsSchema } from '../searchParams/types';
|
|
11
|
+
|
|
12
|
+
/** The body type for HTTP requests and responses. */
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
export type HttpBody = JSONValue | HttpFormData<any> | HttpSearchParams<any> | Blob | ArrayBuffer;
|
|
15
|
+
|
|
16
|
+
export namespace HttpBody {
|
|
17
|
+
/** A loose version of the HTTP body type. JSON values are not strictly typed. */
|
|
18
|
+
export type Loose = ReplaceBy<HttpBody, JSONValue, JSONValue.Loose>;
|
|
19
|
+
|
|
20
|
+
/** Convert a possibly loose HTTP body to be strictly typed. JSON values are serialized to their strict form. */
|
|
21
|
+
export type ConvertToStrict<Type> = Type extends Exclude<HttpBody, JSONValue> ? Type : JSONSerialized<Type>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* An HTTP headers object with a strictly-typed schema. Fully compatible with the built-in
|
|
26
|
+
* {@link https://developer.mozilla.org/docs/Web/API/Headers `Headers`} class.
|
|
27
|
+
*/
|
|
28
|
+
export type StrictHeaders<Schema extends HttpHeadersSchema = HttpHeadersSchema> = Pick<
|
|
29
|
+
HttpHeaders<Schema>,
|
|
30
|
+
keyof Headers
|
|
31
|
+
>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* An HTTP search params object with a strictly-typed schema. Fully compatible with the built-in
|
|
35
|
+
* {@link https://developer.mozilla.org/docs/Web/API/URLSearchParams `URLSearchParams`} class.
|
|
36
|
+
*/
|
|
37
|
+
export type StrictURLSearchParams<Schema extends HttpSearchParamsSchema = HttpSearchParamsSchema> = Pick<
|
|
38
|
+
HttpSearchParams<Schema>,
|
|
39
|
+
keyof URLSearchParams
|
|
40
|
+
>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* An HTTP form data object with a strictly-typed schema. Fully compatible with the built-in
|
|
44
|
+
* {@link https://developer.mozilla.org/docs/Web/API/FormData `FormData`} class.
|
|
45
|
+
*/
|
|
46
|
+
export type StrictFormData<Schema extends HttpFormDataSchema = HttpFormDataSchema> = Pick<
|
|
47
|
+
HttpFormData<Schema>,
|
|
48
|
+
keyof FormData
|
|
49
|
+
>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* An HTTP request with a strictly-typed JSON body. Fully compatible with the built-in
|
|
53
|
+
* {@link https://developer.mozilla.org/docs/Web/API/Request `Request`} class.
|
|
54
|
+
*/
|
|
55
|
+
export interface HttpRequest<
|
|
56
|
+
StrictBody extends HttpBody.Loose = HttpBody,
|
|
57
|
+
StrictHeadersSchema extends HttpHeadersSchema = HttpHeadersSchema,
|
|
58
|
+
> extends Request {
|
|
59
|
+
headers: StrictHeaders<StrictHeadersSchema>;
|
|
60
|
+
text: () => Promise<StrictBody extends string ? StrictBody : string>;
|
|
61
|
+
json: () => Promise<StrictBody extends string | Exclude<HttpBody, JSONValue> ? never : StrictBody>;
|
|
62
|
+
formData: () => Promise<
|
|
63
|
+
StrictBody extends HttpFormData<infer HttpFormDataSchema>
|
|
64
|
+
? StrictFormData<HttpFormDataSchema>
|
|
65
|
+
: StrictBody extends HttpSearchParams<infer HttpSearchParamsSchema>
|
|
66
|
+
? StrictFormData<HttpSearchParamsSchema>
|
|
67
|
+
: FormData
|
|
68
|
+
>;
|
|
69
|
+
clone: () => this;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* An HTTP response with a strictly-typed JSON body and status code. Fully compatible with the built-in
|
|
74
|
+
* {@link https://developer.mozilla.org/docs/Web/API/Response `Response`} class.
|
|
75
|
+
*/
|
|
76
|
+
export interface HttpResponse<
|
|
77
|
+
StrictBody extends HttpBody.Loose = HttpBody,
|
|
78
|
+
StatusCode extends number = number,
|
|
79
|
+
StrictHeadersSchema extends HttpHeadersSchema = HttpHeadersSchema,
|
|
80
|
+
> extends Response {
|
|
81
|
+
ok: StatusCode extends HttpStatusCode.Information | HttpStatusCode.Success | HttpStatusCode.Redirection
|
|
82
|
+
? true
|
|
83
|
+
: false;
|
|
84
|
+
status: StatusCode;
|
|
85
|
+
headers: StrictHeaders<StrictHeadersSchema>;
|
|
86
|
+
text: () => Promise<StrictBody extends string ? StrictBody : string>;
|
|
87
|
+
json: () => Promise<StrictBody extends string | Exclude<HttpBody, JSONValue> ? never : StrictBody>;
|
|
88
|
+
formData: () => Promise<
|
|
89
|
+
StrictBody extends HttpFormData<infer HttpFormDataSchema>
|
|
90
|
+
? StrictFormData<HttpFormDataSchema>
|
|
91
|
+
: StrictBody extends HttpSearchParams<infer HttpSearchParamsSchema>
|
|
92
|
+
? StrictFormData<HttpSearchParamsSchema>
|
|
93
|
+
: FormData
|
|
94
|
+
>;
|
|
95
|
+
clone: () => this;
|
|
96
|
+
}
|