@equinor/fusion-framework-module-http 6.1.0 → 6.2.0
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/CHANGELOG.md +145 -0
- package/README.md +49 -0
- package/dist/esm/configurator.js +7 -2
- package/dist/esm/configurator.js.map +1 -1
- package/dist/esm/lib/operators/capitalize-request-method.operator.js +17 -0
- package/dist/esm/lib/operators/capitalize-request-method.operator.js.map +1 -0
- package/dist/esm/lib/operators/fetch-request.schema.js +109 -0
- package/dist/esm/lib/operators/fetch-request.schema.js.map +1 -0
- package/dist/esm/lib/operators/index.js +2 -0
- package/dist/esm/lib/operators/index.js.map +1 -1
- package/dist/esm/lib/operators/process-operators.js +10 -0
- package/dist/esm/lib/operators/process-operators.js.map +1 -1
- package/dist/esm/lib/operators/request-validation.operator.js +29 -0
- package/dist/esm/lib/operators/request-validation.operator.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/lib/operators/capitalize-request-method.operator.d.ts +10 -0
- package/dist/types/lib/operators/fetch-request.schema.d.ts +170 -0
- package/dist/types/lib/operators/index.d.ts +2 -0
- package/dist/types/lib/operators/process-operators.d.ts +7 -0
- package/dist/types/lib/operators/request-validation.operator.d.ts +33 -0
- package/dist/types/lib/operators/types.d.ts +6 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +2 -1
- package/src/configurator.ts +13 -4
- package/src/lib/operators/capitalize-request-method.operator.ts +22 -0
- package/src/lib/operators/fetch-request.schema.ts +119 -0
- package/src/lib/operators/index.ts +2 -0
- package/src/lib/operators/process-operators.ts +11 -0
- package/src/lib/operators/request-validation.operator.ts +50 -0
- package/src/lib/operators/types.ts +7 -0
- package/src/version.ts +1 -1
- package/tests/operators.test.ts +101 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ProcessOperator } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Ensures that the HTTP method of the given request is in uppercase.
|
|
4
|
+
*
|
|
5
|
+
* @param request - The HTTP request object to process.
|
|
6
|
+
* @returns A new request object with the HTTP method in uppercase.
|
|
7
|
+
*/
|
|
8
|
+
export declare const capitalizeRequestMethodOperator: <T extends RequestInit>(options?: {
|
|
9
|
+
silent?: boolean;
|
|
10
|
+
}) => ProcessOperator<T>;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Validates that the provided HTTP method string is in uppercase.
|
|
4
|
+
*
|
|
5
|
+
* @link https://www.rfc-editor.org/rfc/rfc7231#section-4.1
|
|
6
|
+
*/
|
|
7
|
+
export declare const requestMethodCasing: () => z.ZodType<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Creates a Zod enum schema for HTTP request methods.
|
|
10
|
+
*
|
|
11
|
+
* The schema validates that the value is one of the standard HTTP methods:
|
|
12
|
+
* 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD', 'CONNECT', 'TRACE'.
|
|
13
|
+
*
|
|
14
|
+
* If the validation fails, a custom error message is returned, indicating the
|
|
15
|
+
* expected methods and the received value. The error message also references
|
|
16
|
+
* RFC 2616 for more information.
|
|
17
|
+
*
|
|
18
|
+
* @link https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
|
|
19
|
+
*/
|
|
20
|
+
export declare const requestMethodVerb: () => z.ZodEnum<["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "CONNECT", "TRACE"]>;
|
|
21
|
+
export declare const requestMethod: () => z.ZodPipeline<z.ZodType<string, z.ZodTypeDef, string>, z.ZodEnum<["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "CONNECT", "TRACE"]>>;
|
|
22
|
+
/**
|
|
23
|
+
* Schema for validating the initialization options of a request.
|
|
24
|
+
*
|
|
25
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
|
|
26
|
+
*
|
|
27
|
+
* This schema is used to ensure that the request options conform to the expected structure and types.
|
|
28
|
+
*/
|
|
29
|
+
export declare const requestInitSchema: z.ZodObject<{
|
|
30
|
+
attributionReporting: z.ZodOptional<z.ZodObject<{
|
|
31
|
+
eventSourceEligible: z.ZodOptional<z.ZodBoolean>;
|
|
32
|
+
triggerEligible: z.ZodOptional<z.ZodBoolean>;
|
|
33
|
+
}, "strip", z.ZodTypeAny, {
|
|
34
|
+
eventSourceEligible?: boolean | undefined;
|
|
35
|
+
triggerEligible?: boolean | undefined;
|
|
36
|
+
}, {
|
|
37
|
+
eventSourceEligible?: boolean | undefined;
|
|
38
|
+
triggerEligible?: boolean | undefined;
|
|
39
|
+
}>>;
|
|
40
|
+
body: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodType<Blob, z.ZodTypeDef, Blob>, z.ZodType<ArrayBuffer, z.ZodTypeDef, ArrayBuffer>, z.ZodType<FormData, z.ZodTypeDef, FormData>, z.ZodType<URLSearchParams, z.ZodTypeDef, URLSearchParams>, z.ZodType<ReadableStream<unknown>, z.ZodTypeDef, ReadableStream<unknown>>]>>;
|
|
41
|
+
browsingTopics: z.ZodOptional<z.ZodBoolean>;
|
|
42
|
+
cache: z.ZodOptional<z.ZodEnum<["default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached"]>>;
|
|
43
|
+
credentials: z.ZodOptional<z.ZodEnum<["omit", "same-origin", "include"]>>;
|
|
44
|
+
headers: z.ZodUnion<[z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>, z.ZodType<Headers, z.ZodTypeDef, Headers>]>;
|
|
45
|
+
integrity: z.ZodOptional<z.ZodString>;
|
|
46
|
+
keepalive: z.ZodOptional<z.ZodBoolean>;
|
|
47
|
+
method: z.ZodOptional<z.ZodPipeline<z.ZodType<string, z.ZodTypeDef, string>, z.ZodEnum<["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "CONNECT", "TRACE"]>>>;
|
|
48
|
+
mode: z.ZodOptional<z.ZodEnum<["same-origin", "cors", "no-cors", "navigate", "websocket"]>>;
|
|
49
|
+
priority: z.ZodOptional<z.ZodEnum<["low", "high", "auto"]>>;
|
|
50
|
+
redirect: z.ZodOptional<z.ZodEnum<["follow", "error", "manual"]>>;
|
|
51
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
52
|
+
referrerPolicy: z.ZodOptional<z.ZodEnum<["no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "same-origin", "strict-origin", "strict-origin-when-cross-origin", "unsafe-url"]>>;
|
|
53
|
+
signal: z.ZodOptional<z.ZodType<AbortSignal, z.ZodTypeDef, AbortSignal>>;
|
|
54
|
+
}, "strip", z.ZodTypeAny, {
|
|
55
|
+
body?: string | Blob | ArrayBuffer | FormData | URLSearchParams | ReadableStream<unknown> | undefined;
|
|
56
|
+
cache?: "default" | "force-cache" | "no-cache" | "no-store" | "only-if-cached" | "reload" | undefined;
|
|
57
|
+
credentials?: "include" | "omit" | "same-origin" | undefined;
|
|
58
|
+
headers?: Headers | Record<string, string> | undefined;
|
|
59
|
+
integrity?: string | undefined;
|
|
60
|
+
keepalive?: boolean | undefined;
|
|
61
|
+
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD" | "CONNECT" | "TRACE" | undefined;
|
|
62
|
+
mode?: "cors" | "same-origin" | "navigate" | "no-cors" | "websocket" | undefined;
|
|
63
|
+
priority?: "auto" | "high" | "low" | undefined;
|
|
64
|
+
redirect?: "error" | "follow" | "manual" | undefined;
|
|
65
|
+
referrer?: string | undefined;
|
|
66
|
+
referrerPolicy?: "same-origin" | "no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url" | undefined;
|
|
67
|
+
signal?: AbortSignal | undefined;
|
|
68
|
+
attributionReporting?: {
|
|
69
|
+
eventSourceEligible?: boolean | undefined;
|
|
70
|
+
triggerEligible?: boolean | undefined;
|
|
71
|
+
} | undefined;
|
|
72
|
+
browsingTopics?: boolean | undefined;
|
|
73
|
+
}, {
|
|
74
|
+
body?: string | Blob | ArrayBuffer | FormData | URLSearchParams | ReadableStream<unknown> | undefined;
|
|
75
|
+
cache?: "default" | "force-cache" | "no-cache" | "no-store" | "only-if-cached" | "reload" | undefined;
|
|
76
|
+
credentials?: "include" | "omit" | "same-origin" | undefined;
|
|
77
|
+
headers?: Headers | Record<string, string> | undefined;
|
|
78
|
+
integrity?: string | undefined;
|
|
79
|
+
keepalive?: boolean | undefined;
|
|
80
|
+
method?: string | undefined;
|
|
81
|
+
mode?: "cors" | "same-origin" | "navigate" | "no-cors" | "websocket" | undefined;
|
|
82
|
+
priority?: "auto" | "high" | "low" | undefined;
|
|
83
|
+
redirect?: "error" | "follow" | "manual" | undefined;
|
|
84
|
+
referrer?: string | undefined;
|
|
85
|
+
referrerPolicy?: "same-origin" | "no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url" | undefined;
|
|
86
|
+
signal?: AbortSignal | undefined;
|
|
87
|
+
attributionReporting?: {
|
|
88
|
+
eventSourceEligible?: boolean | undefined;
|
|
89
|
+
triggerEligible?: boolean | undefined;
|
|
90
|
+
} | undefined;
|
|
91
|
+
browsingTopics?: boolean | undefined;
|
|
92
|
+
}>;
|
|
93
|
+
/**
|
|
94
|
+
* Schema for validating fetch request configurations.
|
|
95
|
+
*
|
|
96
|
+
* This schema extends the `requestInitSchema` and adds additional properties:
|
|
97
|
+
* - `uri`: A required string representing the URI of the request.
|
|
98
|
+
* - `path`: An optional string representing the path of the request.
|
|
99
|
+
*/
|
|
100
|
+
export declare const fetchRequestSchema: z.ZodObject<z.objectUtil.extendShape<{
|
|
101
|
+
attributionReporting: z.ZodOptional<z.ZodObject<{
|
|
102
|
+
eventSourceEligible: z.ZodOptional<z.ZodBoolean>;
|
|
103
|
+
triggerEligible: z.ZodOptional<z.ZodBoolean>;
|
|
104
|
+
}, "strip", z.ZodTypeAny, {
|
|
105
|
+
eventSourceEligible?: boolean | undefined;
|
|
106
|
+
triggerEligible?: boolean | undefined;
|
|
107
|
+
}, {
|
|
108
|
+
eventSourceEligible?: boolean | undefined;
|
|
109
|
+
triggerEligible?: boolean | undefined;
|
|
110
|
+
}>>;
|
|
111
|
+
body: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodType<Blob, z.ZodTypeDef, Blob>, z.ZodType<ArrayBuffer, z.ZodTypeDef, ArrayBuffer>, z.ZodType<FormData, z.ZodTypeDef, FormData>, z.ZodType<URLSearchParams, z.ZodTypeDef, URLSearchParams>, z.ZodType<ReadableStream<unknown>, z.ZodTypeDef, ReadableStream<unknown>>]>>;
|
|
112
|
+
browsingTopics: z.ZodOptional<z.ZodBoolean>;
|
|
113
|
+
cache: z.ZodOptional<z.ZodEnum<["default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached"]>>;
|
|
114
|
+
credentials: z.ZodOptional<z.ZodEnum<["omit", "same-origin", "include"]>>;
|
|
115
|
+
headers: z.ZodUnion<[z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>, z.ZodType<Headers, z.ZodTypeDef, Headers>]>;
|
|
116
|
+
integrity: z.ZodOptional<z.ZodString>;
|
|
117
|
+
keepalive: z.ZodOptional<z.ZodBoolean>;
|
|
118
|
+
method: z.ZodOptional<z.ZodPipeline<z.ZodType<string, z.ZodTypeDef, string>, z.ZodEnum<["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "CONNECT", "TRACE"]>>>;
|
|
119
|
+
mode: z.ZodOptional<z.ZodEnum<["same-origin", "cors", "no-cors", "navigate", "websocket"]>>;
|
|
120
|
+
priority: z.ZodOptional<z.ZodEnum<["low", "high", "auto"]>>;
|
|
121
|
+
redirect: z.ZodOptional<z.ZodEnum<["follow", "error", "manual"]>>;
|
|
122
|
+
referrer: z.ZodOptional<z.ZodString>;
|
|
123
|
+
referrerPolicy: z.ZodOptional<z.ZodEnum<["no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "same-origin", "strict-origin", "strict-origin-when-cross-origin", "unsafe-url"]>>;
|
|
124
|
+
signal: z.ZodOptional<z.ZodType<AbortSignal, z.ZodTypeDef, AbortSignal>>;
|
|
125
|
+
}, {
|
|
126
|
+
uri: z.ZodString;
|
|
127
|
+
path: z.ZodOptional<z.ZodString>;
|
|
128
|
+
}>, "strip", z.ZodTypeAny, {
|
|
129
|
+
uri: string;
|
|
130
|
+
body?: string | Blob | ArrayBuffer | FormData | URLSearchParams | ReadableStream<unknown> | undefined;
|
|
131
|
+
path?: string | undefined;
|
|
132
|
+
cache?: "default" | "force-cache" | "no-cache" | "no-store" | "only-if-cached" | "reload" | undefined;
|
|
133
|
+
credentials?: "include" | "omit" | "same-origin" | undefined;
|
|
134
|
+
headers?: Headers | Record<string, string> | undefined;
|
|
135
|
+
integrity?: string | undefined;
|
|
136
|
+
keepalive?: boolean | undefined;
|
|
137
|
+
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD" | "CONNECT" | "TRACE" | undefined;
|
|
138
|
+
mode?: "cors" | "same-origin" | "navigate" | "no-cors" | "websocket" | undefined;
|
|
139
|
+
priority?: "auto" | "high" | "low" | undefined;
|
|
140
|
+
redirect?: "error" | "follow" | "manual" | undefined;
|
|
141
|
+
referrer?: string | undefined;
|
|
142
|
+
referrerPolicy?: "same-origin" | "no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url" | undefined;
|
|
143
|
+
signal?: AbortSignal | undefined;
|
|
144
|
+
attributionReporting?: {
|
|
145
|
+
eventSourceEligible?: boolean | undefined;
|
|
146
|
+
triggerEligible?: boolean | undefined;
|
|
147
|
+
} | undefined;
|
|
148
|
+
browsingTopics?: boolean | undefined;
|
|
149
|
+
}, {
|
|
150
|
+
uri: string;
|
|
151
|
+
body?: string | Blob | ArrayBuffer | FormData | URLSearchParams | ReadableStream<unknown> | undefined;
|
|
152
|
+
path?: string | undefined;
|
|
153
|
+
cache?: "default" | "force-cache" | "no-cache" | "no-store" | "only-if-cached" | "reload" | undefined;
|
|
154
|
+
credentials?: "include" | "omit" | "same-origin" | undefined;
|
|
155
|
+
headers?: Headers | Record<string, string> | undefined;
|
|
156
|
+
integrity?: string | undefined;
|
|
157
|
+
keepalive?: boolean | undefined;
|
|
158
|
+
method?: string | undefined;
|
|
159
|
+
mode?: "cors" | "same-origin" | "navigate" | "no-cors" | "websocket" | undefined;
|
|
160
|
+
priority?: "auto" | "high" | "low" | undefined;
|
|
161
|
+
redirect?: "error" | "follow" | "manual" | undefined;
|
|
162
|
+
referrer?: string | undefined;
|
|
163
|
+
referrerPolicy?: "same-origin" | "no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url" | undefined;
|
|
164
|
+
signal?: AbortSignal | undefined;
|
|
165
|
+
attributionReporting?: {
|
|
166
|
+
eventSourceEligible?: boolean | undefined;
|
|
167
|
+
triggerEligible?: boolean | undefined;
|
|
168
|
+
} | undefined;
|
|
169
|
+
browsingTopics?: boolean | undefined;
|
|
170
|
+
}>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { HttpRequestHandler } from './http-request-handler';
|
|
2
2
|
export { HttpResponseHandler } from './http-response-handler';
|
|
3
3
|
export { ProcessOperators } from './process-operators';
|
|
4
|
+
export { capitalizeRequestMethodOperator } from './capitalize-request-method.operator';
|
|
5
|
+
export { requestValidationOperator } from './request-validation.operator';
|
|
4
6
|
export * from './types';
|
|
@@ -40,6 +40,13 @@ export declare class ProcessOperators<T> implements IProcessOperators<T> {
|
|
|
40
40
|
* @returns The instance of ProcessOperators for chaining.
|
|
41
41
|
*/
|
|
42
42
|
set(key: string, operator: ProcessOperator<T>): ProcessOperators<T>;
|
|
43
|
+
/**
|
|
44
|
+
* Removes an operator from the collection by its key.
|
|
45
|
+
*
|
|
46
|
+
* @param key - The key of the operator to remove.
|
|
47
|
+
* @returns The current instance of `ProcessOperators` for method chaining.
|
|
48
|
+
*/
|
|
49
|
+
remove(key: string): ProcessOperators<T>;
|
|
43
50
|
/**
|
|
44
51
|
* Retrieves an operator from the collection by its key.
|
|
45
52
|
* @param key The key of the operator to retrieve.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ProcessOperator } from './types';
|
|
2
|
+
import type { FetchRequest } from '../client/types';
|
|
3
|
+
/**
|
|
4
|
+
* Validates the given request using the `requestInitSchema`.
|
|
5
|
+
*
|
|
6
|
+
* By default, the validation is not strict, meaning that additional properties
|
|
7
|
+
* not defined in the schema will be allowed and passed through without causing validation errors.
|
|
8
|
+
* Also the operator will not modify the request object, it will only log an error message if the validation fails.
|
|
9
|
+
*
|
|
10
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
|
|
11
|
+
*
|
|
12
|
+
* @param request - The request object to be validated.
|
|
13
|
+
* @returns The validated request object if validation is successful.
|
|
14
|
+
* @throws Will log an error message if the request validation fails.
|
|
15
|
+
*/
|
|
16
|
+
export declare const requestValidationOperator: <T extends FetchRequest>(options?: {
|
|
17
|
+
/**
|
|
18
|
+
* When enabled, the function will return the parsed result.
|
|
19
|
+
* This means that if the request object passes validation,
|
|
20
|
+
* the parsed and potentially transformed request object will be returned.
|
|
21
|
+
* If this option is not enabled, the function will not return anything
|
|
22
|
+
* even if the request object is valid.
|
|
23
|
+
*/
|
|
24
|
+
parse?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* When set to true, the validation will be strict, meaning that any additional properties
|
|
27
|
+
* not defined in the schema will cause the validation to fail. If set to false or omitted,
|
|
28
|
+
* additional properties will be allowed and passed through without causing validation errors.
|
|
29
|
+
*
|
|
30
|
+
* @note this option is only applicable when the `parse` option is enabled.
|
|
31
|
+
*/
|
|
32
|
+
strict?: boolean;
|
|
33
|
+
}) => ProcessOperator<T>;
|
|
@@ -35,6 +35,12 @@ export interface IProcessOperators<T> {
|
|
|
35
35
|
* @returns The updated collection of process operators.
|
|
36
36
|
*/
|
|
37
37
|
set(key: string, operator: ProcessOperator<T>): IProcessOperators<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Removes a process operator from the collection.
|
|
40
|
+
* @param key The key of the operator to remove.
|
|
41
|
+
* @returns The updated collection of process operators.
|
|
42
|
+
*/
|
|
43
|
+
remove(key: string): IProcessOperators<T>;
|
|
38
44
|
/**
|
|
39
45
|
* Gets a process operator from the collection.
|
|
40
46
|
* @param key The key of the operator to retrieve.
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "6.
|
|
1
|
+
export declare const version = "6.2.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@equinor/fusion-framework-module-http",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"rxjs": "^7.8.1",
|
|
58
|
+
"zod": "^3.23.8",
|
|
58
59
|
"@equinor/fusion-framework-module": "^4.3.5",
|
|
59
60
|
"@equinor/fusion-framework-module-msal": "^3.1.5"
|
|
60
61
|
},
|
package/src/configurator.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
capitalizeRequestMethodOperator,
|
|
3
|
+
requestValidationOperator,
|
|
4
|
+
HttpRequestHandler,
|
|
5
|
+
} from './lib/operators';
|
|
2
6
|
|
|
3
7
|
import type { FetchRequest, IHttpClient } from './lib/client';
|
|
4
8
|
import type { IHttpRequestHandler, IHttpResponseHandler } from './lib/operators';
|
|
@@ -132,9 +136,14 @@ export class HttpClientConfigurator<TClient extends IHttpClient>
|
|
|
132
136
|
readonly defaultHttpClientCtor: HttpClientConstructor<TClient>;
|
|
133
137
|
|
|
134
138
|
/** default request handler for http clients, applied on creation */
|
|
135
|
-
readonly defaultHttpRequestHandler = new HttpRequestHandler<
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
readonly defaultHttpRequestHandler = new HttpRequestHandler<HttpClientRequestInitType<TClient>>(
|
|
140
|
+
{
|
|
141
|
+
// convert all request methods to uppercase
|
|
142
|
+
['capitalize-method']: capitalizeRequestMethodOperator(),
|
|
143
|
+
// validate the request object
|
|
144
|
+
['request-validation']: requestValidationOperator(),
|
|
145
|
+
},
|
|
146
|
+
);
|
|
138
147
|
|
|
139
148
|
/**
|
|
140
149
|
* Create a instance of http configuration
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { requestMethodCasing } from './fetch-request.schema';
|
|
2
|
+
import type { ProcessOperator } from './types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Ensures that the HTTP method of the given request is in uppercase.
|
|
6
|
+
*
|
|
7
|
+
* @param request - The HTTP request object to process.
|
|
8
|
+
* @returns A new request object with the HTTP method in uppercase.
|
|
9
|
+
*/
|
|
10
|
+
export const capitalizeRequestMethodOperator =
|
|
11
|
+
<T extends RequestInit>(options?: { silent?: boolean }): ProcessOperator<T> =>
|
|
12
|
+
(request): T => {
|
|
13
|
+
const { error, success, data } = requestMethodCasing().safeParse(request.method);
|
|
14
|
+
|
|
15
|
+
request.method = success ? data : request.method?.toUpperCase();
|
|
16
|
+
|
|
17
|
+
if (error && !options?.silent) {
|
|
18
|
+
error.errors.forEach((e) => console.warn(e.message));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return request;
|
|
22
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validates that the provided HTTP method string is in uppercase.
|
|
5
|
+
*
|
|
6
|
+
* @link https://www.rfc-editor.org/rfc/rfc7231#section-4.1
|
|
7
|
+
*/
|
|
8
|
+
export const requestMethodCasing = (): z.ZodType<string> => {
|
|
9
|
+
return z.custom<string>(
|
|
10
|
+
(value?: string) => value === value?.toUpperCase(),
|
|
11
|
+
(value: string) => ({
|
|
12
|
+
code: z.ZodIssueCode.custom,
|
|
13
|
+
validation: 'uppercase',
|
|
14
|
+
path: ['method'],
|
|
15
|
+
message: [
|
|
16
|
+
`Provided HTTP method '${value}' must be in uppercase.`,
|
|
17
|
+
'See RFC 7231 Section 4.1 for more information',
|
|
18
|
+
'https://www.rfc-editor.org/rfc/rfc7231#section-4.1',
|
|
19
|
+
].join(' '),
|
|
20
|
+
}),
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Creates a Zod enum schema for HTTP request methods.
|
|
26
|
+
*
|
|
27
|
+
* The schema validates that the value is one of the standard HTTP methods:
|
|
28
|
+
* 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD', 'CONNECT', 'TRACE'.
|
|
29
|
+
*
|
|
30
|
+
* If the validation fails, a custom error message is returned, indicating the
|
|
31
|
+
* expected methods and the received value. The error message also references
|
|
32
|
+
* RFC 2616 for more information.
|
|
33
|
+
*
|
|
34
|
+
* @link https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
|
|
35
|
+
*/
|
|
36
|
+
export const requestMethodVerb = () => {
|
|
37
|
+
return z.enum(
|
|
38
|
+
['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD', 'CONNECT', 'TRACE'],
|
|
39
|
+
{
|
|
40
|
+
errorMap: (error) => {
|
|
41
|
+
const { received, options } = error as z.ZodInvalidEnumValueIssue;
|
|
42
|
+
return {
|
|
43
|
+
message: [
|
|
44
|
+
'Invalid request method.',
|
|
45
|
+
`Expected '${options.join(' | ')}', but received '${received}'.`,
|
|
46
|
+
'See RFC 2615 Section 9 for more information',
|
|
47
|
+
'https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html',
|
|
48
|
+
].join(' '),
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const requestMethod = () => requestMethodCasing().pipe(requestMethodVerb());
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Schema for validating the initialization options of a request.
|
|
59
|
+
*
|
|
60
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
|
|
61
|
+
*
|
|
62
|
+
* This schema is used to ensure that the request options conform to the expected structure and types.
|
|
63
|
+
*/
|
|
64
|
+
export const requestInitSchema = z.object({
|
|
65
|
+
attributionReporting: z
|
|
66
|
+
.object({
|
|
67
|
+
eventSourceEligible: z.boolean().optional(),
|
|
68
|
+
triggerEligible: z.boolean().optional(),
|
|
69
|
+
})
|
|
70
|
+
.optional(),
|
|
71
|
+
body: z
|
|
72
|
+
.union([
|
|
73
|
+
z.string(),
|
|
74
|
+
z.instanceof(Blob),
|
|
75
|
+
z.instanceof(ArrayBuffer),
|
|
76
|
+
z.instanceof(FormData),
|
|
77
|
+
z.instanceof(URLSearchParams),
|
|
78
|
+
z.instanceof(ReadableStream),
|
|
79
|
+
])
|
|
80
|
+
.optional(),
|
|
81
|
+
browsingTopics: z.boolean().optional(),
|
|
82
|
+
cache: z
|
|
83
|
+
.enum(['default', 'no-store', 'reload', 'no-cache', 'force-cache', 'only-if-cached'])
|
|
84
|
+
.optional(),
|
|
85
|
+
credentials: z.enum(['omit', 'same-origin', 'include']).optional(),
|
|
86
|
+
headers: z.record(z.string(), z.string()).optional().or(z.instanceof(Headers)),
|
|
87
|
+
integrity: z.string().optional(),
|
|
88
|
+
keepalive: z.boolean().optional(),
|
|
89
|
+
method: requestMethod().optional(),
|
|
90
|
+
mode: z.enum(['same-origin', 'cors', 'no-cors', 'navigate', 'websocket']).optional(),
|
|
91
|
+
priority: z.enum(['low', 'high', 'auto']).optional(),
|
|
92
|
+
redirect: z.enum(['follow', 'error', 'manual']).optional(),
|
|
93
|
+
referrer: z.string().optional(),
|
|
94
|
+
referrerPolicy: z
|
|
95
|
+
.enum([
|
|
96
|
+
'no-referrer',
|
|
97
|
+
'no-referrer-when-downgrade',
|
|
98
|
+
'origin',
|
|
99
|
+
'origin-when-cross-origin',
|
|
100
|
+
'same-origin',
|
|
101
|
+
'strict-origin',
|
|
102
|
+
'strict-origin-when-cross-origin',
|
|
103
|
+
'unsafe-url',
|
|
104
|
+
])
|
|
105
|
+
.optional(),
|
|
106
|
+
signal: z.instanceof(AbortSignal).optional(),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Schema for validating fetch request configurations.
|
|
111
|
+
*
|
|
112
|
+
* This schema extends the `requestInitSchema` and adds additional properties:
|
|
113
|
+
* - `uri`: A required string representing the URI of the request.
|
|
114
|
+
* - `path`: An optional string representing the path of the request.
|
|
115
|
+
*/
|
|
116
|
+
export const fetchRequestSchema = requestInitSchema.extend({
|
|
117
|
+
uri: z.string(),
|
|
118
|
+
path: z.string().optional(),
|
|
119
|
+
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { HttpRequestHandler } from './http-request-handler';
|
|
2
2
|
export { HttpResponseHandler } from './http-response-handler';
|
|
3
3
|
export { ProcessOperators } from './process-operators';
|
|
4
|
+
export { capitalizeRequestMethodOperator } from './capitalize-request-method.operator';
|
|
5
|
+
export { requestValidationOperator } from './request-validation.operator';
|
|
4
6
|
|
|
5
7
|
export * from './types';
|
|
@@ -63,6 +63,17 @@ export class ProcessOperators<T> implements IProcessOperators<T> {
|
|
|
63
63
|
return this;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Removes an operator from the collection by its key.
|
|
68
|
+
*
|
|
69
|
+
* @param key - The key of the operator to remove.
|
|
70
|
+
* @returns The current instance of `ProcessOperators` for method chaining.
|
|
71
|
+
*/
|
|
72
|
+
remove(key: string): ProcessOperators<T> {
|
|
73
|
+
delete this._operators[key];
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
66
77
|
/**
|
|
67
78
|
* Retrieves an operator from the collection by its key.
|
|
68
79
|
* @param key The key of the operator to retrieve.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ProcessOperator } from './types';
|
|
3
|
+
import type { FetchRequest } from '../client/types';
|
|
4
|
+
import { fetchRequestSchema } from './fetch-request.schema';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Validates the given request using the `requestInitSchema`.
|
|
8
|
+
*
|
|
9
|
+
* By default, the validation is not strict, meaning that additional properties
|
|
10
|
+
* not defined in the schema will be allowed and passed through without causing validation errors.
|
|
11
|
+
* Also the operator will not modify the request object, it will only log an error message if the validation fails.
|
|
12
|
+
*
|
|
13
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
|
|
14
|
+
*
|
|
15
|
+
* @param request - The request object to be validated.
|
|
16
|
+
* @returns The validated request object if validation is successful.
|
|
17
|
+
* @throws Will log an error message if the request validation fails.
|
|
18
|
+
*/
|
|
19
|
+
export const requestValidationOperator =
|
|
20
|
+
<T extends FetchRequest>(options?: {
|
|
21
|
+
/**
|
|
22
|
+
* When enabled, the function will return the parsed result.
|
|
23
|
+
* This means that if the request object passes validation,
|
|
24
|
+
* the parsed and potentially transformed request object will be returned.
|
|
25
|
+
* If this option is not enabled, the function will not return anything
|
|
26
|
+
* even if the request object is valid.
|
|
27
|
+
*/
|
|
28
|
+
parse?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* When set to true, the validation will be strict, meaning that any additional properties
|
|
31
|
+
* not defined in the schema will cause the validation to fail. If set to false or omitted,
|
|
32
|
+
* additional properties will be allowed and passed through without causing validation errors.
|
|
33
|
+
*
|
|
34
|
+
* @note this option is only applicable when the `parse` option is enabled.
|
|
35
|
+
*/
|
|
36
|
+
strict?: boolean;
|
|
37
|
+
}): ProcessOperator<T> =>
|
|
38
|
+
(request) => {
|
|
39
|
+
const { strict, parse } = options ?? {};
|
|
40
|
+
const schema = strict ? fetchRequestSchema : fetchRequestSchema.passthrough();
|
|
41
|
+
try {
|
|
42
|
+
const result = schema.parse(request) as T;
|
|
43
|
+
return parse ? result : void 0;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (parse) {
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
console.error('Invalid request options', (error as z.ZodError).message);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
@@ -40,6 +40,13 @@ export interface IProcessOperators<T> {
|
|
|
40
40
|
*/
|
|
41
41
|
set(key: string, operator: ProcessOperator<T>): IProcessOperators<T>;
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Removes a process operator from the collection.
|
|
45
|
+
* @param key The key of the operator to remove.
|
|
46
|
+
* @returns The updated collection of process operators.
|
|
47
|
+
*/
|
|
48
|
+
remove(key: string): IProcessOperators<T>;
|
|
49
|
+
|
|
43
50
|
/**
|
|
44
51
|
* Gets a process operator from the collection.
|
|
45
52
|
* @param key The key of the operator to retrieve.
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '6.
|
|
2
|
+
export const version = '6.2.0';
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
import {
|
|
4
|
+
capitalizeRequestMethodOperator,
|
|
5
|
+
ProcessOperator,
|
|
6
|
+
requestValidationOperator,
|
|
7
|
+
} from '../src/lib/operators';
|
|
8
|
+
import { FetchRequest } from '../src/lib';
|
|
9
|
+
|
|
10
|
+
const executeOperator = <R, O extends ProcessOperator<FetchRequest, R>>(
|
|
11
|
+
operator: O,
|
|
12
|
+
request: Parameters<O>[0],
|
|
13
|
+
): Promise<ReturnType<O>> => {
|
|
14
|
+
return new Promise((resolve, rejects) => {
|
|
15
|
+
try {
|
|
16
|
+
resolve(operator(request) as ReturnType<O>);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
rejects(error);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
describe('capitalizeRequestMethodOperator', () => {
|
|
24
|
+
it('should capitalize request method', async () => {
|
|
25
|
+
const consoleWarn = vi.spyOn(console, 'warn').mockImplementationOnce((msg) => {
|
|
26
|
+
expect(msg).toMatch(/RFC 7231/);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const operator = capitalizeRequestMethodOperator();
|
|
30
|
+
|
|
31
|
+
const result = executeOperator(operator, { method: 'get' });
|
|
32
|
+
|
|
33
|
+
expect(result).resolves.toMatchObject({ method: 'GET' });
|
|
34
|
+
expect(consoleWarn).toHaveBeenCalled();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('requestValidationOperator', () => {
|
|
39
|
+
/**
|
|
40
|
+
* A mock FetchRequest object used for testing purposes.
|
|
41
|
+
*/
|
|
42
|
+
const mockRequest: FetchRequest = Object.freeze({
|
|
43
|
+
method: 'GET',
|
|
44
|
+
uri: 'https://foo.bar',
|
|
45
|
+
path: 'api',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should pass validation for valid request parameters', async () => {
|
|
49
|
+
const operator = requestValidationOperator();
|
|
50
|
+
|
|
51
|
+
const result = executeOperator(operator, mockRequest);
|
|
52
|
+
|
|
53
|
+
expect(result).resolves.toBeUndefined();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should parse valid request parameters', async () => {
|
|
57
|
+
const operator = requestValidationOperator({ parse: true });
|
|
58
|
+
|
|
59
|
+
const result = executeOperator(operator, mockRequest);
|
|
60
|
+
|
|
61
|
+
expect(result).resolves.toStrictEqual(mockRequest);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should allow additional properties when strict validation is disabled', async () => {
|
|
65
|
+
const operator = requestValidationOperator({ parse: true });
|
|
66
|
+
|
|
67
|
+
const result = executeOperator(operator, {
|
|
68
|
+
...mockRequest,
|
|
69
|
+
// @ts-expect-error
|
|
70
|
+
additionalProperty: 'some-value',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(result).resolves.toMatchObject(mockRequest);
|
|
74
|
+
expect(result).resolves.toHaveProperty('additionalProperty', 'some-value');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should remove additional properties when strict validation is enabled', async () => {
|
|
78
|
+
const operator = requestValidationOperator({ parse: true, strict: true });
|
|
79
|
+
|
|
80
|
+
const result = executeOperator(operator, {
|
|
81
|
+
...mockRequest,
|
|
82
|
+
// @ts-expect-error
|
|
83
|
+
additionalProperty: 'some-value',
|
|
84
|
+
method: 'GET',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(result).resolves.toStrictEqual(mockRequest);
|
|
88
|
+
expect(result).resolves.not.toHaveProperty('additionalProperty');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should throw an error for invalid request parameters', async () => {
|
|
92
|
+
const operator = requestValidationOperator({ parse: true });
|
|
93
|
+
|
|
94
|
+
const result = executeOperator(operator, {
|
|
95
|
+
...mockRequest,
|
|
96
|
+
method: 'GETS', // invalid method
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(result).rejects.toThrowError('RFC 2615');
|
|
100
|
+
});
|
|
101
|
+
});
|