api-def 0.14.0-alpha.2 → 0.14.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/cjs/ApiTypes.d.ts +4 -4
- package/cjs/Endpoint.d.ts +1 -1
- package/cjs/MockingTypes.d.ts +3 -3
- package/cjs/RequestError.js +1 -1
- package/cjs/Requester.js +1 -1
- package/cjs/TextDecoding.d.ts +1 -1
- package/cjs/TextDecoding.js +1 -1
- package/cjs/Utils.d.ts +2 -0
- package/cjs/Utils.js +41 -7
- package/cjs/backend/AxiosRequestBackend.d.ts +1 -1
- package/cjs/backend/AxiosRequestBackend.js +1 -1
- package/cjs/backend/FetchRequestBackend.d.ts +1 -1
- package/cjs/backend/FetchRequestBackend.js +5 -2
- package/cjs/backend/MockRequestBackend.d.ts +2 -2
- package/cjs/backend/MockRequestBackend.js +2 -2
- package/cjs/util/retry/lib/retryOperation.js +2 -2
- package/esm/ApiTypes.d.ts +4 -4
- package/esm/Endpoint.d.ts +1 -1
- package/esm/MockingTypes.d.ts +3 -3
- package/esm/RequestError.js +1 -1
- package/esm/Requester.js +1 -1
- package/esm/TextDecoding.d.ts +1 -1
- package/esm/TextDecoding.js +1 -1
- package/esm/Utils.d.ts +2 -0
- package/esm/Utils.js +38 -6
- package/esm/backend/AxiosRequestBackend.d.ts +1 -1
- package/esm/backend/AxiosRequestBackend.js +1 -1
- package/esm/backend/FetchRequestBackend.d.ts +1 -1
- package/esm/backend/FetchRequestBackend.js +5 -2
- package/esm/backend/MockRequestBackend.d.ts +2 -2
- package/esm/backend/MockRequestBackend.js +2 -2
- package/esm/util/retry/lib/retryOperation.js +2 -2
- package/package.json +1 -1
package/cjs/ApiTypes.d.ts
CHANGED
|
@@ -59,7 +59,7 @@ export type RequestConfig<TParams extends Params | undefined = Params | undefine
|
|
|
59
59
|
body?: never;
|
|
60
60
|
} : {
|
|
61
61
|
body: TBody;
|
|
62
|
-
}) & (
|
|
62
|
+
}) & (Record<never, never> extends TState ? {
|
|
63
63
|
state?: TState;
|
|
64
64
|
} : {
|
|
65
65
|
state: TState;
|
|
@@ -80,13 +80,13 @@ interface BaseEventResult<T extends EventResultType> {
|
|
|
80
80
|
export type ResponseEventResult<R> = BaseEventResult<"respond"> & {
|
|
81
81
|
response: ApiResponse<R>;
|
|
82
82
|
};
|
|
83
|
-
export type RetryEventResult<
|
|
83
|
+
export type RetryEventResult<_R> = BaseEventResult<"retry">;
|
|
84
84
|
export type EventResult<R> = ResponseEventResult<R> | RetryEventResult<R>;
|
|
85
|
-
export type RequestEventHandler<R> = (context: RequestContext<R>) => EventResult<R> |
|
|
85
|
+
export type RequestEventHandler<R> = (context: RequestContext<R>) => EventResult<R> | undefined | Promise<EventResult<R> | undefined>;
|
|
86
86
|
export type RequestEventHandlers<R> = {
|
|
87
87
|
[key in RequestEvent]?: Array<RequestEventHandler<R>>;
|
|
88
88
|
};
|
|
89
|
-
export type RequestMiddleware<
|
|
89
|
+
export type RequestMiddleware<_O = undefined> = {
|
|
90
90
|
[key in RequestEvent]?: RequestEventHandler<any> | undefined | false;
|
|
91
91
|
};
|
|
92
92
|
export interface RequestStats {
|
package/cjs/Endpoint.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface EndpointResolveUrlOptions<TParams extends Params | undefined, T
|
|
|
12
12
|
export interface EndpointResolvePathOptions<TParams extends Params | undefined> {
|
|
13
13
|
params?: (TParams extends string | symbol | number ? Record<TParams, string> : never) | undefined;
|
|
14
14
|
}
|
|
15
|
-
export interface EndpointOptions<TResponse, TParams extends Params | undefined, TQuery extends Query | undefined, TBody extends Body | undefined, TState extends State = State, TPath extends string = string,
|
|
15
|
+
export interface EndpointOptions<TResponse, TParams extends Params | undefined, TQuery extends Query | undefined, TBody extends Body | undefined, TState extends State = State, TPath extends string = string, _TRequestHeaders extends RawHeaders | undefined = RawHeaders | undefined, _TResponseHeaders extends RawHeaders | undefined = RawHeaders | undefined> {
|
|
16
16
|
readonly id: string;
|
|
17
17
|
readonly method: RequestMethod;
|
|
18
18
|
readonly path: TPath;
|
package/cjs/MockingTypes.d.ts
CHANGED
|
@@ -2,15 +2,15 @@ import type { ApiResponse, Body, Params, Query, RawHeaders, State } from "./ApiT
|
|
|
2
2
|
export interface ApiMockingConfig {
|
|
3
3
|
enabled: boolean | (() => boolean);
|
|
4
4
|
}
|
|
5
|
-
export interface MockRequest<
|
|
6
|
-
params: TParams extends Params ? Record<TParams, string> :
|
|
5
|
+
export interface MockRequest<_TResponse = any, TParams extends Params | undefined = Params | undefined, TQuery extends Query | undefined = Query | undefined, TBody extends Body | undefined = Body | undefined, TState extends State = State> {
|
|
6
|
+
params: TParams extends Params ? Record<TParams, string> : Record<keyof any, never>;
|
|
7
7
|
body: TBody;
|
|
8
8
|
query: TQuery;
|
|
9
9
|
headers: Readonly<RawHeaders>;
|
|
10
10
|
url: string;
|
|
11
11
|
state: TState;
|
|
12
12
|
}
|
|
13
|
-
export interface MockResponse<TResponse = any,
|
|
13
|
+
export interface MockResponse<TResponse = any, _TParams extends Params | undefined = Params | undefined, _TQuery extends Query | undefined = Query | undefined, _TBody extends Body | undefined = Body | undefined, _TState extends State = State> {
|
|
14
14
|
statusCode: number;
|
|
15
15
|
response: TResponse | undefined;
|
|
16
16
|
headers: RawHeaders;
|
package/cjs/RequestError.js
CHANGED
|
@@ -53,7 +53,7 @@ const convertToRequestError = (config) => {
|
|
|
53
53
|
value: `A ${context.method.toUpperCase()} request to '${context.requestUrl.href}' failed${response?.status ? ` with status code ${response.status}` : ""} [${code}]: ${resultError.message}`,
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
-
catch (
|
|
56
|
+
catch (_e) {
|
|
57
57
|
// ignore
|
|
58
58
|
}
|
|
59
59
|
resultError.config = undefined;
|
package/cjs/Requester.js
CHANGED
|
@@ -199,7 +199,7 @@ const parseResponse = async (context, response, error) => {
|
|
|
199
199
|
const decodedData = (response.data = (0, TextDecoding_1.textDecode)(data));
|
|
200
200
|
response.data = JSON.parse(decodedData);
|
|
201
201
|
}
|
|
202
|
-
catch (
|
|
202
|
+
catch (_e) {
|
|
203
203
|
throw (0, RequestError_1.convertToRequestError)({
|
|
204
204
|
error: new Error(`[api-def] Expected '${context.responseType}' response, got '${inferredResponseType}' (from 'Content-Type' of '${contentType}')`),
|
|
205
205
|
code: RequestError_1.RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
|
package/cjs/TextDecoding.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const textDecode: (inputArrayOrBuffer: any,
|
|
1
|
+
export declare const textDecode: (inputArrayOrBuffer: any, _options?: any) => string;
|
package/cjs/TextDecoding.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.textDecode = void 0;
|
|
4
4
|
// polyfill from https://github.com/anonyco/FastestSmallestTextEncoderDecoder/blob/master/EncoderDecoderTogether.src.js
|
|
5
|
-
const textDecode = (inputArrayOrBuffer,
|
|
5
|
+
const textDecode = (inputArrayOrBuffer, _options) => {
|
|
6
6
|
if (typeof TextDecoder !== "undefined") {
|
|
7
7
|
return new TextDecoder("utf-8").decode(inputArrayOrBuffer);
|
|
8
8
|
}
|
package/cjs/Utils.d.ts
CHANGED
|
@@ -14,4 +14,6 @@ export declare const noop: () => void;
|
|
|
14
14
|
export declare const delayThenReturn: <T>(value: T, delayMs: number) => Promise<T>;
|
|
15
15
|
export declare const randInt: (min: number, max: number) => number;
|
|
16
16
|
export declare const isFormData: (value: any) => value is FormData;
|
|
17
|
+
export declare const isFormDataLike: (value: any) => value is FormData;
|
|
17
18
|
export declare const toFormData: (body: any) => FormData;
|
|
19
|
+
export declare const assertFetchCompatibleFormData: (body: FormData) => void;
|
package/cjs/Utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toFormData = exports.isFormData = exports.randInt = exports.delayThenReturn = exports.noop = exports.getGlobalFetch = exports.getGlobal = exports.padNumber = exports.assign = void 0;
|
|
3
|
+
exports.assertFetchCompatibleFormData = exports.toFormData = exports.isFormDataLike = exports.isFormData = exports.randInt = exports.delayThenReturn = exports.noop = exports.getGlobalFetch = exports.getGlobal = exports.padNumber = exports.assign = void 0;
|
|
4
4
|
// polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
|
5
5
|
const hasOwn = (object, key) => Object.prototype.hasOwnProperty.call(object, key);
|
|
6
6
|
exports.assign = Object.assign ||
|
|
@@ -76,8 +76,16 @@ const isFormData = (value) => {
|
|
|
76
76
|
return typeof FormData !== "undefined" && value instanceof FormData;
|
|
77
77
|
};
|
|
78
78
|
exports.isFormData = isFormData;
|
|
79
|
+
const isFormDataLike = (value) => {
|
|
80
|
+
return value !== null && typeof value === "object" && typeof value.append === "function";
|
|
81
|
+
};
|
|
82
|
+
exports.isFormDataLike = isFormDataLike;
|
|
79
83
|
const isPlainObject = (value) => {
|
|
80
|
-
|
|
84
|
+
if (Object.prototype.toString.call(value) !== "[object Object]") {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const prototype = Object.getPrototypeOf(value);
|
|
88
|
+
return prototype === Object.prototype || prototype === null;
|
|
81
89
|
};
|
|
82
90
|
const isBlob = (value) => {
|
|
83
91
|
return typeof Blob !== "undefined" && value instanceof Blob;
|
|
@@ -98,9 +106,15 @@ const toFormDataValue = (value) => {
|
|
|
98
106
|
return String(value);
|
|
99
107
|
};
|
|
100
108
|
const toFormData = (body) => {
|
|
101
|
-
if ((0, exports.
|
|
109
|
+
if ((0, exports.isFormDataLike)(body)) {
|
|
102
110
|
return body;
|
|
103
111
|
}
|
|
112
|
+
if (!isPlainObject(body)) {
|
|
113
|
+
throw new Error("[api-def] multipart/form-data body must be a plain object or FormData");
|
|
114
|
+
}
|
|
115
|
+
if (typeof FormData === "undefined") {
|
|
116
|
+
throw new Error("[api-def] multipart/form-data requires a FormData implementation");
|
|
117
|
+
}
|
|
104
118
|
const formData = new FormData();
|
|
105
119
|
const appendValue = (key, value) => {
|
|
106
120
|
if (value === undefined || value === null) {
|
|
@@ -120,11 +134,31 @@ const toFormData = (body) => {
|
|
|
120
134
|
}
|
|
121
135
|
formData.append(key, toFormDataValue(value));
|
|
122
136
|
};
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
appendValue(key, body[key]);
|
|
126
|
-
}
|
|
137
|
+
for (const key of Object.keys(body)) {
|
|
138
|
+
appendValue(key, body[key]);
|
|
127
139
|
}
|
|
128
140
|
return formData;
|
|
129
141
|
};
|
|
130
142
|
exports.toFormData = toFormData;
|
|
143
|
+
const assertFetchCompatibleFormData = (body) => {
|
|
144
|
+
if (typeof Request === "undefined") {
|
|
145
|
+
throw new Error("[api-def] multipart/form-data requires a fetch-compatible Request implementation");
|
|
146
|
+
}
|
|
147
|
+
let request;
|
|
148
|
+
try {
|
|
149
|
+
request = new Request("https://api-def.local", {
|
|
150
|
+
method: "POST",
|
|
151
|
+
body: body,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
throw Object.assign(new Error("[api-def] multipart/form-data requires a fetch-compatible FormData implementation"), {
|
|
156
|
+
cause: error,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
const contentType = request.headers.get("content-type");
|
|
160
|
+
if (!contentType?.toLowerCase().startsWith("multipart/form-data")) {
|
|
161
|
+
throw new Error("[api-def] multipart/form-data requires a fetch-compatible FormData implementation");
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
exports.assertFetchCompatibleFormData = assertFetchCompatibleFormData;
|
|
@@ -10,5 +10,5 @@ export default class AxiosRequestBackend implements RequestBackend<AxiosResponse
|
|
|
10
10
|
extractResponseFromError(error: Error): Promise<AxiosResponse | null | undefined>;
|
|
11
11
|
convertResponse<T>(context: RequestContext, response: AxiosResponse): Promise<ConvertedApiResponse<T>>;
|
|
12
12
|
makeRequest(context: RequestContext): RequestOperation<AxiosResponse>;
|
|
13
|
-
getErrorInfo(
|
|
13
|
+
getErrorInfo(_error: Error, _response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
|
|
14
14
|
}
|
|
@@ -12,5 +12,5 @@ export default class FetchRequestBackend implements RequestBackend<Response> {
|
|
|
12
12
|
__text?: string;
|
|
13
13
|
}): Promise<ConvertedApiResponse<T>>;
|
|
14
14
|
makeRequest(context: RequestContext): RequestOperation<Response>;
|
|
15
|
-
getErrorInfo(
|
|
15
|
+
getErrorInfo(_error: Error, _response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
|
|
16
16
|
}
|
|
@@ -111,7 +111,10 @@ class FetchRequestBackend {
|
|
|
111
111
|
let softAbort = false;
|
|
112
112
|
let responded = false;
|
|
113
113
|
const body = context.getParsedBody();
|
|
114
|
-
const bodyJsonify = body !== null && typeof body === "object" && !Utils.
|
|
114
|
+
const bodyJsonify = body !== null && typeof body === "object" && !Utils.isFormDataLike(body) && !(body instanceof URLSearchParams);
|
|
115
|
+
if (context.validation.bodyEncoding === "multipart/form-data" && Utils.isFormDataLike(body)) {
|
|
116
|
+
Utils.assertFetchCompatibleFormData(body);
|
|
117
|
+
}
|
|
115
118
|
const headers = Utils.assign({
|
|
116
119
|
// logic from axios
|
|
117
120
|
"Content-Type": bodyJsonify ? "application/json;charset=utf-8" : undefined,
|
|
@@ -161,7 +164,7 @@ class FetchRequestBackend {
|
|
|
161
164
|
},
|
|
162
165
|
};
|
|
163
166
|
}
|
|
164
|
-
getErrorInfo(
|
|
167
|
+
getErrorInfo(_error, _response) {
|
|
165
168
|
return undefined;
|
|
166
169
|
}
|
|
167
170
|
}
|
|
@@ -4,9 +4,9 @@ import type RequestBackend from "./RequestBackend";
|
|
|
4
4
|
import type { RequestBackendErrorInfo, RequestOperation } from "./RequestBackend";
|
|
5
5
|
export default class MockRequestBackend implements RequestBackend<ApiResponse> {
|
|
6
6
|
readonly id = "mock";
|
|
7
|
-
convertResponse<T>(
|
|
7
|
+
convertResponse<T>(_context: RequestContext, response: ApiResponse, _error?: boolean): Promise<ApiResponse<T>>;
|
|
8
8
|
extractResponseFromError(error: Error): Promise<ApiResponse | null | undefined>;
|
|
9
9
|
private runRequest;
|
|
10
10
|
makeRequest(context: RequestContext): RequestOperation<ApiResponse>;
|
|
11
|
-
getErrorInfo(
|
|
11
|
+
getErrorInfo(_error: Error, _response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
|
|
12
12
|
}
|
|
@@ -41,7 +41,7 @@ class MockRequestBackend {
|
|
|
41
41
|
constructor() {
|
|
42
42
|
this.id = "mock";
|
|
43
43
|
}
|
|
44
|
-
async convertResponse(
|
|
44
|
+
async convertResponse(_context, response, _error) {
|
|
45
45
|
return response;
|
|
46
46
|
}
|
|
47
47
|
async extractResponseFromError(error) {
|
|
@@ -157,7 +157,7 @@ class MockRequestBackend {
|
|
|
157
157
|
promise: this.runRequest(context),
|
|
158
158
|
};
|
|
159
159
|
}
|
|
160
|
-
getErrorInfo(
|
|
160
|
+
getErrorInfo(_error, _response) {
|
|
161
161
|
return undefined;
|
|
162
162
|
}
|
|
163
163
|
}
|
|
@@ -38,7 +38,7 @@ RetryOperation.prototype.retry = function (err) {
|
|
|
38
38
|
if (!err) {
|
|
39
39
|
return false;
|
|
40
40
|
}
|
|
41
|
-
const currentTime =
|
|
41
|
+
const currentTime = Date.now();
|
|
42
42
|
if (err && currentTime - this._operationStart >= this._maxRetryTime) {
|
|
43
43
|
this._errors.push(err);
|
|
44
44
|
this._errors.unshift(new Error("RetryOperation timeout occurred"));
|
|
@@ -88,7 +88,7 @@ RetryOperation.prototype.attempt = function (fn, timeoutOps) {
|
|
|
88
88
|
this._operationTimeoutCb();
|
|
89
89
|
}, this._operationTimeout);
|
|
90
90
|
}
|
|
91
|
-
this._operationStart =
|
|
91
|
+
this._operationStart = Date.now();
|
|
92
92
|
this._fn(this._attempts);
|
|
93
93
|
};
|
|
94
94
|
RetryOperation.prototype.try = function (fn) {
|
package/esm/ApiTypes.d.ts
CHANGED
|
@@ -59,7 +59,7 @@ export type RequestConfig<TParams extends Params | undefined = Params | undefine
|
|
|
59
59
|
body?: never;
|
|
60
60
|
} : {
|
|
61
61
|
body: TBody;
|
|
62
|
-
}) & (
|
|
62
|
+
}) & (Record<never, never> extends TState ? {
|
|
63
63
|
state?: TState;
|
|
64
64
|
} : {
|
|
65
65
|
state: TState;
|
|
@@ -80,13 +80,13 @@ interface BaseEventResult<T extends EventResultType> {
|
|
|
80
80
|
export type ResponseEventResult<R> = BaseEventResult<"respond"> & {
|
|
81
81
|
response: ApiResponse<R>;
|
|
82
82
|
};
|
|
83
|
-
export type RetryEventResult<
|
|
83
|
+
export type RetryEventResult<_R> = BaseEventResult<"retry">;
|
|
84
84
|
export type EventResult<R> = ResponseEventResult<R> | RetryEventResult<R>;
|
|
85
|
-
export type RequestEventHandler<R> = (context: RequestContext<R>) => EventResult<R> |
|
|
85
|
+
export type RequestEventHandler<R> = (context: RequestContext<R>) => EventResult<R> | undefined | Promise<EventResult<R> | undefined>;
|
|
86
86
|
export type RequestEventHandlers<R> = {
|
|
87
87
|
[key in RequestEvent]?: Array<RequestEventHandler<R>>;
|
|
88
88
|
};
|
|
89
|
-
export type RequestMiddleware<
|
|
89
|
+
export type RequestMiddleware<_O = undefined> = {
|
|
90
90
|
[key in RequestEvent]?: RequestEventHandler<any> | undefined | false;
|
|
91
91
|
};
|
|
92
92
|
export interface RequestStats {
|
package/esm/Endpoint.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface EndpointResolveUrlOptions<TParams extends Params | undefined, T
|
|
|
12
12
|
export interface EndpointResolvePathOptions<TParams extends Params | undefined> {
|
|
13
13
|
params?: (TParams extends string | symbol | number ? Record<TParams, string> : never) | undefined;
|
|
14
14
|
}
|
|
15
|
-
export interface EndpointOptions<TResponse, TParams extends Params | undefined, TQuery extends Query | undefined, TBody extends Body | undefined, TState extends State = State, TPath extends string = string,
|
|
15
|
+
export interface EndpointOptions<TResponse, TParams extends Params | undefined, TQuery extends Query | undefined, TBody extends Body | undefined, TState extends State = State, TPath extends string = string, _TRequestHeaders extends RawHeaders | undefined = RawHeaders | undefined, _TResponseHeaders extends RawHeaders | undefined = RawHeaders | undefined> {
|
|
16
16
|
readonly id: string;
|
|
17
17
|
readonly method: RequestMethod;
|
|
18
18
|
readonly path: TPath;
|
package/esm/MockingTypes.d.ts
CHANGED
|
@@ -2,15 +2,15 @@ import type { ApiResponse, Body, Params, Query, RawHeaders, State } from "./ApiT
|
|
|
2
2
|
export interface ApiMockingConfig {
|
|
3
3
|
enabled: boolean | (() => boolean);
|
|
4
4
|
}
|
|
5
|
-
export interface MockRequest<
|
|
6
|
-
params: TParams extends Params ? Record<TParams, string> :
|
|
5
|
+
export interface MockRequest<_TResponse = any, TParams extends Params | undefined = Params | undefined, TQuery extends Query | undefined = Query | undefined, TBody extends Body | undefined = Body | undefined, TState extends State = State> {
|
|
6
|
+
params: TParams extends Params ? Record<TParams, string> : Record<keyof any, never>;
|
|
7
7
|
body: TBody;
|
|
8
8
|
query: TQuery;
|
|
9
9
|
headers: Readonly<RawHeaders>;
|
|
10
10
|
url: string;
|
|
11
11
|
state: TState;
|
|
12
12
|
}
|
|
13
|
-
export interface MockResponse<TResponse = any,
|
|
13
|
+
export interface MockResponse<TResponse = any, _TParams extends Params | undefined = Params | undefined, _TQuery extends Query | undefined = Query | undefined, _TBody extends Body | undefined = Body | undefined, _TState extends State = State> {
|
|
14
14
|
statusCode: number;
|
|
15
15
|
response: TResponse | undefined;
|
|
16
16
|
headers: RawHeaders;
|
package/esm/RequestError.js
CHANGED
|
@@ -49,7 +49,7 @@ export const convertToRequestError = (config) => {
|
|
|
49
49
|
value: `A ${context.method.toUpperCase()} request to '${context.requestUrl.href}' failed${response?.status ? ` with status code ${response.status}` : ""} [${code}]: ${resultError.message}`,
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
|
-
catch (
|
|
52
|
+
catch (_e) {
|
|
53
53
|
// ignore
|
|
54
54
|
}
|
|
55
55
|
resultError.config = undefined;
|
package/esm/Requester.js
CHANGED
|
@@ -192,7 +192,7 @@ const parseResponse = async (context, response, error) => {
|
|
|
192
192
|
const decodedData = (response.data = textDecode(data));
|
|
193
193
|
response.data = JSON.parse(decodedData);
|
|
194
194
|
}
|
|
195
|
-
catch (
|
|
195
|
+
catch (_e) {
|
|
196
196
|
throw convertToRequestError({
|
|
197
197
|
error: new Error(`[api-def] Expected '${context.responseType}' response, got '${inferredResponseType}' (from 'Content-Type' of '${contentType}')`),
|
|
198
198
|
code: RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
|
package/esm/TextDecoding.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const textDecode: (inputArrayOrBuffer: any,
|
|
1
|
+
export declare const textDecode: (inputArrayOrBuffer: any, _options?: any) => string;
|
package/esm/TextDecoding.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// polyfill from https://github.com/anonyco/FastestSmallestTextEncoderDecoder/blob/master/EncoderDecoderTogether.src.js
|
|
2
|
-
export const textDecode = (inputArrayOrBuffer,
|
|
2
|
+
export const textDecode = (inputArrayOrBuffer, _options) => {
|
|
3
3
|
if (typeof TextDecoder !== "undefined") {
|
|
4
4
|
return new TextDecoder("utf-8").decode(inputArrayOrBuffer);
|
|
5
5
|
}
|
package/esm/Utils.d.ts
CHANGED
|
@@ -14,4 +14,6 @@ export declare const noop: () => void;
|
|
|
14
14
|
export declare const delayThenReturn: <T>(value: T, delayMs: number) => Promise<T>;
|
|
15
15
|
export declare const randInt: (min: number, max: number) => number;
|
|
16
16
|
export declare const isFormData: (value: any) => value is FormData;
|
|
17
|
+
export declare const isFormDataLike: (value: any) => value is FormData;
|
|
17
18
|
export declare const toFormData: (body: any) => FormData;
|
|
19
|
+
export declare const assertFetchCompatibleFormData: (body: FormData) => void;
|
package/esm/Utils.js
CHANGED
|
@@ -66,8 +66,15 @@ export const randInt = (min, max) => {
|
|
|
66
66
|
export const isFormData = (value) => {
|
|
67
67
|
return typeof FormData !== "undefined" && value instanceof FormData;
|
|
68
68
|
};
|
|
69
|
+
export const isFormDataLike = (value) => {
|
|
70
|
+
return value !== null && typeof value === "object" && typeof value.append === "function";
|
|
71
|
+
};
|
|
69
72
|
const isPlainObject = (value) => {
|
|
70
|
-
|
|
73
|
+
if (Object.prototype.toString.call(value) !== "[object Object]") {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const prototype = Object.getPrototypeOf(value);
|
|
77
|
+
return prototype === Object.prototype || prototype === null;
|
|
71
78
|
};
|
|
72
79
|
const isBlob = (value) => {
|
|
73
80
|
return typeof Blob !== "undefined" && value instanceof Blob;
|
|
@@ -88,9 +95,15 @@ const toFormDataValue = (value) => {
|
|
|
88
95
|
return String(value);
|
|
89
96
|
};
|
|
90
97
|
export const toFormData = (body) => {
|
|
91
|
-
if (
|
|
98
|
+
if (isFormDataLike(body)) {
|
|
92
99
|
return body;
|
|
93
100
|
}
|
|
101
|
+
if (!isPlainObject(body)) {
|
|
102
|
+
throw new Error("[api-def] multipart/form-data body must be a plain object or FormData");
|
|
103
|
+
}
|
|
104
|
+
if (typeof FormData === "undefined") {
|
|
105
|
+
throw new Error("[api-def] multipart/form-data requires a FormData implementation");
|
|
106
|
+
}
|
|
94
107
|
const formData = new FormData();
|
|
95
108
|
const appendValue = (key, value) => {
|
|
96
109
|
if (value === undefined || value === null) {
|
|
@@ -110,10 +123,29 @@ export const toFormData = (body) => {
|
|
|
110
123
|
}
|
|
111
124
|
formData.append(key, toFormDataValue(value));
|
|
112
125
|
};
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
appendValue(key, body[key]);
|
|
116
|
-
}
|
|
126
|
+
for (const key of Object.keys(body)) {
|
|
127
|
+
appendValue(key, body[key]);
|
|
117
128
|
}
|
|
118
129
|
return formData;
|
|
119
130
|
};
|
|
131
|
+
export const assertFetchCompatibleFormData = (body) => {
|
|
132
|
+
if (typeof Request === "undefined") {
|
|
133
|
+
throw new Error("[api-def] multipart/form-data requires a fetch-compatible Request implementation");
|
|
134
|
+
}
|
|
135
|
+
let request;
|
|
136
|
+
try {
|
|
137
|
+
request = new Request("https://api-def.local", {
|
|
138
|
+
method: "POST",
|
|
139
|
+
body: body,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
throw Object.assign(new Error("[api-def] multipart/form-data requires a fetch-compatible FormData implementation"), {
|
|
144
|
+
cause: error,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
const contentType = request.headers.get("content-type");
|
|
148
|
+
if (!contentType?.toLowerCase().startsWith("multipart/form-data")) {
|
|
149
|
+
throw new Error("[api-def] multipart/form-data requires a fetch-compatible FormData implementation");
|
|
150
|
+
}
|
|
151
|
+
};
|
|
@@ -10,5 +10,5 @@ export default class AxiosRequestBackend implements RequestBackend<AxiosResponse
|
|
|
10
10
|
extractResponseFromError(error: Error): Promise<AxiosResponse | null | undefined>;
|
|
11
11
|
convertResponse<T>(context: RequestContext, response: AxiosResponse): Promise<ConvertedApiResponse<T>>;
|
|
12
12
|
makeRequest(context: RequestContext): RequestOperation<AxiosResponse>;
|
|
13
|
-
getErrorInfo(
|
|
13
|
+
getErrorInfo(_error: Error, _response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
|
|
14
14
|
}
|
|
@@ -12,5 +12,5 @@ export default class FetchRequestBackend implements RequestBackend<Response> {
|
|
|
12
12
|
__text?: string;
|
|
13
13
|
}): Promise<ConvertedApiResponse<T>>;
|
|
14
14
|
makeRequest(context: RequestContext): RequestOperation<Response>;
|
|
15
|
-
getErrorInfo(
|
|
15
|
+
getErrorInfo(_error: Error, _response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
|
|
16
16
|
}
|
|
@@ -76,7 +76,10 @@ export default class FetchRequestBackend {
|
|
|
76
76
|
let softAbort = false;
|
|
77
77
|
let responded = false;
|
|
78
78
|
const body = context.getParsedBody();
|
|
79
|
-
const bodyJsonify = body !== null && typeof body === "object" && !Utils.
|
|
79
|
+
const bodyJsonify = body !== null && typeof body === "object" && !Utils.isFormDataLike(body) && !(body instanceof URLSearchParams);
|
|
80
|
+
if (context.validation.bodyEncoding === "multipart/form-data" && Utils.isFormDataLike(body)) {
|
|
81
|
+
Utils.assertFetchCompatibleFormData(body);
|
|
82
|
+
}
|
|
80
83
|
const headers = Utils.assign({
|
|
81
84
|
// logic from axios
|
|
82
85
|
"Content-Type": bodyJsonify ? "application/json;charset=utf-8" : undefined,
|
|
@@ -126,7 +129,7 @@ export default class FetchRequestBackend {
|
|
|
126
129
|
},
|
|
127
130
|
};
|
|
128
131
|
}
|
|
129
|
-
getErrorInfo(
|
|
132
|
+
getErrorInfo(_error, _response) {
|
|
130
133
|
return undefined;
|
|
131
134
|
}
|
|
132
135
|
}
|
|
@@ -4,9 +4,9 @@ import type RequestBackend from "./RequestBackend.js";
|
|
|
4
4
|
import type { RequestBackendErrorInfo, RequestOperation } from "./RequestBackend.js";
|
|
5
5
|
export default class MockRequestBackend implements RequestBackend<ApiResponse> {
|
|
6
6
|
readonly id = "mock";
|
|
7
|
-
convertResponse<T>(
|
|
7
|
+
convertResponse<T>(_context: RequestContext, response: ApiResponse, _error?: boolean): Promise<ApiResponse<T>>;
|
|
8
8
|
extractResponseFromError(error: Error): Promise<ApiResponse | null | undefined>;
|
|
9
9
|
private runRequest;
|
|
10
10
|
makeRequest(context: RequestContext): RequestOperation<ApiResponse>;
|
|
11
|
-
getErrorInfo(
|
|
11
|
+
getErrorInfo(_error: Error, _response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
|
|
12
12
|
}
|
|
@@ -6,7 +6,7 @@ export default class MockRequestBackend {
|
|
|
6
6
|
constructor() {
|
|
7
7
|
this.id = "mock";
|
|
8
8
|
}
|
|
9
|
-
async convertResponse(
|
|
9
|
+
async convertResponse(_context, response, _error) {
|
|
10
10
|
return response;
|
|
11
11
|
}
|
|
12
12
|
async extractResponseFromError(error) {
|
|
@@ -122,7 +122,7 @@ export default class MockRequestBackend {
|
|
|
122
122
|
promise: this.runRequest(context),
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
|
-
getErrorInfo(
|
|
125
|
+
getErrorInfo(_error, _response) {
|
|
126
126
|
return undefined;
|
|
127
127
|
}
|
|
128
128
|
}
|
|
@@ -36,7 +36,7 @@ RetryOperation.prototype.retry = function (err) {
|
|
|
36
36
|
if (!err) {
|
|
37
37
|
return false;
|
|
38
38
|
}
|
|
39
|
-
const currentTime =
|
|
39
|
+
const currentTime = Date.now();
|
|
40
40
|
if (err && currentTime - this._operationStart >= this._maxRetryTime) {
|
|
41
41
|
this._errors.push(err);
|
|
42
42
|
this._errors.unshift(new Error("RetryOperation timeout occurred"));
|
|
@@ -86,7 +86,7 @@ RetryOperation.prototype.attempt = function (fn, timeoutOps) {
|
|
|
86
86
|
this._operationTimeoutCb();
|
|
87
87
|
}, this._operationTimeout);
|
|
88
88
|
}
|
|
89
|
-
this._operationStart =
|
|
89
|
+
this._operationStart = Date.now();
|
|
90
90
|
this._fn(this._attempts);
|
|
91
91
|
};
|
|
92
92
|
RetryOperation.prototype.try = function (fn) {
|