api-def 0.8.1 → 0.8.3
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/Api.d.ts +3 -0
- package/cjs/Api.js +22 -1
- package/cjs/ApiTypes.d.ts +9 -1
- package/cjs/Endpoint.d.ts +2 -0
- package/cjs/Endpoint.js +3 -8
- package/cjs/RequestError.d.ts +1 -0
- package/cjs/RequestError.js +1 -0
- package/cjs/Requester.js +28 -33
- package/cjs/Utils.d.ts +1 -0
- package/cjs/Utils.js +14 -6
- package/cjs/backend/FetchRequestBackend.js +4 -0
- package/esm/Api.d.ts +3 -0
- package/esm/Api.js +22 -1
- package/esm/ApiTypes.d.ts +9 -1
- package/esm/Endpoint.d.ts +2 -0
- package/esm/Endpoint.js +3 -7
- package/esm/RequestError.d.ts +1 -0
- package/esm/RequestError.js +1 -0
- package/esm/Requester.js +27 -27
- package/esm/Utils.d.ts +1 -0
- package/esm/Utils.js +12 -5
- package/esm/backend/FetchRequestBackend.js +5 -1
- package/package.json +1 -1
package/cjs/Api.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface ApiInfo {
|
|
|
12
12
|
readonly middleware?: RequestMiddleware[];
|
|
13
13
|
readonly config?: BaseRequestConfig | (() => BaseRequestConfig);
|
|
14
14
|
readonly mocking?: ApiMockingConfig;
|
|
15
|
+
readonly requestBackend?: RequestBackend;
|
|
15
16
|
}
|
|
16
17
|
export declare class Api implements ApiInfo {
|
|
17
18
|
readonly baseUrl: string;
|
|
@@ -19,9 +20,11 @@ export declare class Api implements ApiInfo {
|
|
|
19
20
|
readonly middleware: RequestMiddleware[];
|
|
20
21
|
readonly config?: BaseRequestConfig | (() => BaseRequestConfig);
|
|
21
22
|
readonly mocking?: ApiMockingConfig;
|
|
23
|
+
readonly requestBackend: RequestBackend;
|
|
22
24
|
protected readonly endpoints: Record<string, Endpoint>;
|
|
23
25
|
constructor(info: ApiInfo);
|
|
24
26
|
endpoint(): EndpointBuilder;
|
|
27
|
+
getRequestBackend(): RequestBackend;
|
|
25
28
|
getConfig(): BaseRequestConfig;
|
|
26
29
|
private hotRequest;
|
|
27
30
|
get: <R = unknown>(path: string, config: RequestConfig) => Promise<ApiResponse<R>>;
|
package/cjs/Api.js
CHANGED
|
@@ -83,12 +83,16 @@ var HotRequestHost = /** @class */ (function () {
|
|
|
83
83
|
HotRequestHost.prototype.computePath = function (path, config) {
|
|
84
84
|
return path.startsWith("/") ? path : "/".concat(path);
|
|
85
85
|
};
|
|
86
|
+
HotRequestHost.prototype.getRequestBackend = function () {
|
|
87
|
+
return this.api.getRequestBackend();
|
|
88
|
+
};
|
|
86
89
|
return HotRequestHost;
|
|
87
90
|
}());
|
|
91
|
+
var defaultBackendMessageShown = false;
|
|
88
92
|
var Api = /** @class */ (function () {
|
|
89
93
|
function Api(info) {
|
|
90
94
|
var _this = this;
|
|
91
|
-
var _a;
|
|
95
|
+
var _a, _b;
|
|
92
96
|
this.endpoints = {};
|
|
93
97
|
this.hotRequest = function (requestMethod) { return function (path, config) { return __awaiter(_this, void 0, void 0, function () {
|
|
94
98
|
return __generator(this, function (_a) {
|
|
@@ -109,10 +113,27 @@ var Api = /** @class */ (function () {
|
|
|
109
113
|
this.endpoints = {};
|
|
110
114
|
this.config = info.config;
|
|
111
115
|
this.mocking = (_a = info.mocking) !== null && _a !== void 0 ? _a : undefined;
|
|
116
|
+
var requestBackend = (_b = info.requestBackend) !== null && _b !== void 0 ? _b : (0, exports.getRequestBackend)();
|
|
117
|
+
if (!requestBackend) {
|
|
118
|
+
throw new Error("[api-def] No request backend provided in either Api options or globally, use `setRequestBackend()` to set one or pass one via `requestBackend`");
|
|
119
|
+
}
|
|
120
|
+
this.requestBackend = requestBackend;
|
|
121
|
+
if (!info.requestBackend) {
|
|
122
|
+
if (process.env.NODE_ENV === "development") {
|
|
123
|
+
if ((0, exports.isRequestBackendDefault)() && !defaultBackendMessageShown) {
|
|
124
|
+
defaultBackendMessageShown = true;
|
|
125
|
+
// eslint-disable-next-line
|
|
126
|
+
console.warn("[api-def] Using default fetch backend, you can use a different one with 'setRequestBackend()' (dev only message)");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
112
130
|
}
|
|
113
131
|
Api.prototype.endpoint = function () {
|
|
114
132
|
return new EndpointBuilder_1.default(this);
|
|
115
133
|
};
|
|
134
|
+
Api.prototype.getRequestBackend = function () {
|
|
135
|
+
return this.requestBackend;
|
|
136
|
+
};
|
|
116
137
|
Api.prototype.getConfig = function () {
|
|
117
138
|
return ((typeof this.config === "function" ? this.config() : this.config) ||
|
|
118
139
|
{});
|
package/cjs/ApiTypes.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import RequestContext from "./RequestContext";
|
|
2
2
|
import { Api } from "./Api";
|
|
3
3
|
import { CacheSource, EventResultType, RequestEvent, RequestMethod, ResponseType } from "./ApiConstants";
|
|
4
|
+
import RequestBackend from "./backend/RequestBackend";
|
|
4
5
|
export declare type AcceptableStatus = number | [min: number, max: number];
|
|
5
6
|
export declare type Headers = Record<string, string | number | boolean | null | undefined>;
|
|
6
7
|
export declare type Params = string;
|
|
@@ -20,10 +21,16 @@ interface QueryHandling {
|
|
|
20
21
|
parse: QueryParse;
|
|
21
22
|
stringify: QueryStringify;
|
|
22
23
|
}
|
|
24
|
+
export interface RetryOptions {
|
|
25
|
+
maxAttempts: number;
|
|
26
|
+
shouldRetry?: (error: Error) => boolean;
|
|
27
|
+
minDelay?: number;
|
|
28
|
+
maxDelay?: number;
|
|
29
|
+
}
|
|
23
30
|
export interface BaseRequestConfig {
|
|
24
31
|
cache?: number | boolean;
|
|
25
32
|
lock?: RequestLock;
|
|
26
|
-
retry?: number | false;
|
|
33
|
+
retry?: number | false | RetryOptions;
|
|
27
34
|
headers?: Readonly<Headers>;
|
|
28
35
|
acceptableStatus?: AcceptableStatus[];
|
|
29
36
|
/**
|
|
@@ -82,6 +89,7 @@ export interface RequestHost {
|
|
|
82
89
|
readonly responseType: ResponseType | undefined;
|
|
83
90
|
computeConfig<P extends Params | undefined, Q extends Query | undefined, B extends Body | undefined>(config: RequestConfig<P, Q, B>): ComputedRequestConfig<P, Q, B>;
|
|
84
91
|
computePath(path: string, config: RequestConfig): string;
|
|
92
|
+
getRequestBackend(): RequestBackend;
|
|
85
93
|
}
|
|
86
94
|
export interface CancelledRequestError extends Error {
|
|
87
95
|
isCancelledRequest: true;
|
package/cjs/Endpoint.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Api } from "./Api";
|
|
|
2
2
|
import { ApiResponse, BaseRequestConfig, Body, ComputedRequestConfig, Params, Query, RequestConfig, RequestHost } from "./ApiTypes";
|
|
3
3
|
import * as Mocking from "./MockingTypes";
|
|
4
4
|
import { RequestMethod, ResponseType } from "./ApiConstants";
|
|
5
|
+
import RequestBackend from "./backend/RequestBackend";
|
|
5
6
|
export interface EndpointConfig<R, P extends Params | undefined, Q extends Query | undefined, B extends Body | undefined> {
|
|
6
7
|
readonly id: string;
|
|
7
8
|
readonly method: RequestMethod;
|
|
@@ -44,4 +45,5 @@ export default class Endpoint<R = any, P extends Params | undefined = Params | u
|
|
|
44
45
|
computePath(path: string, request: RequestConfig): string;
|
|
45
46
|
get baseUrl(): string;
|
|
46
47
|
computeConfig<P extends Params | undefined, Q extends Query | undefined, B extends Body | undefined>(config: RequestConfig<P, Q, B>): ComputedRequestConfig<P, Q, B>;
|
|
48
|
+
getRequestBackend(): RequestBackend;
|
|
47
49
|
}
|
package/cjs/Endpoint.js
CHANGED
|
@@ -37,8 +37,6 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
var Requester = require("./Requester");
|
|
40
|
-
var ApiTypes_1 = require("./ApiTypes");
|
|
41
|
-
var Utils = require("./Utils");
|
|
42
40
|
var RequestConfig_1 = require("./RequestConfig");
|
|
43
41
|
var Endpoint = /** @class */ (function () {
|
|
44
42
|
function Endpoint(api, info) {
|
|
@@ -94,18 +92,15 @@ var Endpoint = /** @class */ (function () {
|
|
|
94
92
|
configurable: true
|
|
95
93
|
});
|
|
96
94
|
Endpoint.prototype.computeConfig = function (config) {
|
|
97
|
-
var _a;
|
|
98
95
|
var apiDefaults = this.api.getConfig();
|
|
99
|
-
var computedConfig = Utils.assign((_a = {},
|
|
100
|
-
_a[ApiTypes_1.COMPUTED_CONFIG_SYMBOL] = true,
|
|
101
|
-
_a), apiDefaults, this.config, config);
|
|
102
96
|
return (0, RequestConfig_1.computeRequestConfig)([
|
|
103
97
|
apiDefaults,
|
|
104
98
|
this.config,
|
|
105
99
|
config,
|
|
106
100
|
]);
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
};
|
|
102
|
+
Endpoint.prototype.getRequestBackend = function () {
|
|
103
|
+
return this.api.getRequestBackend();
|
|
109
104
|
};
|
|
110
105
|
return Endpoint;
|
|
111
106
|
}());
|
package/cjs/RequestError.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface RequestError extends Error {
|
|
|
13
13
|
isRequestError: true;
|
|
14
14
|
response: ApiResponse | undefined | null;
|
|
15
15
|
code: string;
|
|
16
|
+
attempts: number;
|
|
16
17
|
}
|
|
17
18
|
export declare const isRequestError: (error: Error) => error is RequestError;
|
|
18
19
|
export interface RequestErrorConfig {
|
package/cjs/RequestError.js
CHANGED
package/cjs/Requester.js
CHANGED
|
@@ -48,10 +48,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
48
48
|
};
|
|
49
49
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
50
|
exports.submit = void 0;
|
|
51
|
-
var ApiUtils = require("./ApiUtils");
|
|
52
51
|
var ApiUtils_1 = require("./ApiUtils");
|
|
53
52
|
var RequestContext_1 = require("./RequestContext");
|
|
54
|
-
var Api = require("./Api");
|
|
55
53
|
var ApiConstants_1 = require("./ApiConstants");
|
|
56
54
|
var retry_1 = require("./util/retry");
|
|
57
55
|
var MockRequestBackend_1 = require("./backend/MockRequestBackend");
|
|
@@ -66,10 +64,7 @@ var submit = function (host, config, mocking) { return __awaiter(void 0, void 0,
|
|
|
66
64
|
switch (_a.label) {
|
|
67
65
|
case 0:
|
|
68
66
|
computedConfig = host.computeConfig(config);
|
|
69
|
-
backend = mocking ? MOCK_REQUEST_BACKEND :
|
|
70
|
-
if (!backend) {
|
|
71
|
-
throw new Error("[api-def] Please specify a backend you wish to use, this can be done either with 'setRequestBackend()'");
|
|
72
|
-
}
|
|
67
|
+
backend = mocking ? MOCK_REQUEST_BACKEND : host.getRequestBackend();
|
|
73
68
|
context = new RequestContext_1.default(backend, host, computedConfig, host.computePath(host.path, config), mocking);
|
|
74
69
|
key = context.key;
|
|
75
70
|
lock = (context.computedConfig || {}).lock;
|
|
@@ -109,38 +104,38 @@ var submit = function (host, config, mocking) { return __awaiter(void 0, void 0,
|
|
|
109
104
|
});
|
|
110
105
|
}); };
|
|
111
106
|
exports.submit = submit;
|
|
112
|
-
var
|
|
107
|
+
var parseRetryOptions = function (retryConfig) {
|
|
108
|
+
if (retryConfig && typeof retryConfig === "object") {
|
|
109
|
+
return retryConfig;
|
|
110
|
+
}
|
|
111
|
+
if (typeof retryConfig === "number") {
|
|
112
|
+
return { maxAttempts: retryConfig };
|
|
113
|
+
}
|
|
114
|
+
return { maxAttempts: 0 };
|
|
115
|
+
};
|
|
113
116
|
var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0, function () {
|
|
114
|
-
var beforeSendEventResult,
|
|
115
|
-
var _a;
|
|
116
|
-
return __generator(this, function (
|
|
117
|
-
switch (
|
|
118
|
-
case 0:
|
|
119
|
-
if (process.env.NODE_ENV === "development") {
|
|
120
|
-
if (Api.isRequestBackendDefault() && !defaultBackendMessageShown) {
|
|
121
|
-
defaultBackendMessageShown = true;
|
|
122
|
-
// eslint-disable-next-line
|
|
123
|
-
console.warn("[api-def] Using default fetch backend, you can use a different one with 'setRequestBackend()' (dev only message)");
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return [4 /*yield*/, context.triggerEvent(ApiConstants_1.RequestEvent.BeforeSend)];
|
|
117
|
+
var beforeSendEventResult, retryOptions, internalRetryOptions, performRequest, response;
|
|
118
|
+
var _a, _b, _c;
|
|
119
|
+
return __generator(this, function (_d) {
|
|
120
|
+
switch (_d.label) {
|
|
121
|
+
case 0: return [4 /*yield*/, context.triggerEvent(ApiConstants_1.RequestEvent.BeforeSend)];
|
|
127
122
|
case 1:
|
|
128
|
-
beforeSendEventResult =
|
|
123
|
+
beforeSendEventResult = _d.sent();
|
|
129
124
|
if (beforeSendEventResult &&
|
|
130
125
|
beforeSendEventResult.type === ApiConstants_1.EventResultType.Respond) {
|
|
131
126
|
return [2 /*return*/, (context.response = beforeSendEventResult.response)];
|
|
132
127
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
retries:
|
|
128
|
+
retryOptions = parseRetryOptions((_a = context.computedConfig) === null || _a === void 0 ? void 0 : _a.retry);
|
|
129
|
+
internalRetryOptions = {
|
|
130
|
+
retries: retryOptions.maxAttempts,
|
|
136
131
|
// assume most users won't want to tune the delay between retries
|
|
137
|
-
minTimeout:
|
|
138
|
-
maxTimeout:
|
|
132
|
+
minTimeout: (_b = retryOptions.minDelay) !== null && _b !== void 0 ? _b : 200,
|
|
133
|
+
maxTimeout: (_c = retryOptions.maxDelay) !== null && _c !== void 0 ? _c : 1000,
|
|
139
134
|
randomize: true,
|
|
140
135
|
};
|
|
141
136
|
context.stats.attempt = 0;
|
|
142
137
|
performRequest = function (fnBail, attemptCount) { return __awaiter(void 0, void 0, void 0, function () {
|
|
143
|
-
var _a, promise, canceler, response_1, parsedResponse, rawError_1, error, errorEventResult,
|
|
138
|
+
var _a, promise, canceler, response_1, parsedResponse, rawError_1, error, errorEventResult, forceRetry, unrecoverableErrorEventResult;
|
|
144
139
|
return __generator(this, function (_b) {
|
|
145
140
|
switch (_b.label) {
|
|
146
141
|
case 0:
|
|
@@ -182,14 +177,14 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
|
|
|
182
177
|
if ((errorEventResult === null || errorEventResult === void 0 ? void 0 : errorEventResult.type) === ApiConstants_1.EventResultType.Respond) {
|
|
183
178
|
return [2 /*return*/, errorEventResult.response];
|
|
184
179
|
}
|
|
185
|
-
shouldNaturallyRetry = ApiUtils.isNetworkError(error);
|
|
186
|
-
if (shouldNaturallyRetry) {
|
|
187
|
-
throw error;
|
|
188
|
-
}
|
|
189
180
|
forceRetry = (errorEventResult === null || errorEventResult === void 0 ? void 0 : errorEventResult.type) === ApiConstants_1.EventResultType.Retry;
|
|
190
181
|
if (forceRetry) {
|
|
191
182
|
return [2 /*return*/, performRequest(fnBail, attemptCount)];
|
|
192
183
|
}
|
|
184
|
+
// allow retry logic to handle
|
|
185
|
+
if (!retryOptions.shouldRetry || retryOptions.shouldRetry(error)) {
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
193
188
|
return [4 /*yield*/, context.triggerEvent(ApiConstants_1.RequestEvent.UnrecoverableError)];
|
|
194
189
|
case 7:
|
|
195
190
|
unrecoverableErrorEventResult = _b.sent();
|
|
@@ -204,9 +199,9 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
|
|
|
204
199
|
}
|
|
205
200
|
});
|
|
206
201
|
}); };
|
|
207
|
-
return [4 /*yield*/, (0, retry_1.default)(performRequest,
|
|
202
|
+
return [4 /*yield*/, (0, retry_1.default)(performRequest, internalRetryOptions)];
|
|
208
203
|
case 2:
|
|
209
|
-
response =
|
|
204
|
+
response = _d.sent();
|
|
210
205
|
return [2 /*return*/, (response)];
|
|
211
206
|
}
|
|
212
207
|
});
|
package/cjs/Utils.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export declare const assign: typeof Object["assign"];
|
|
|
2
2
|
export declare const padNumber: (stringOrNumber: string | number, maxLength: number) => string;
|
|
3
3
|
export declare type EnumOf<T extends Record<string, any>> = T[keyof T];
|
|
4
4
|
export declare type Fetch = typeof window.fetch;
|
|
5
|
+
export declare const getGlobal: () => any;
|
|
5
6
|
export declare const getGlobalFetch: () => Fetch | undefined;
|
|
6
7
|
export declare const noop: () => void;
|
|
7
8
|
/**
|
package/cjs/Utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.randInt = exports.delayThenReturn = exports.noop = exports.getGlobalFetch = exports.padNumber = exports.assign = void 0;
|
|
3
|
+
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
|
exports.assign = Object.assign || function (target, varArgs) {
|
|
6
6
|
if (target === null || target === undefined) {
|
|
@@ -28,14 +28,22 @@ var padNumber = function (stringOrNumber, maxLength) {
|
|
|
28
28
|
: "0".repeat(maxLength - string.length) + string;
|
|
29
29
|
};
|
|
30
30
|
exports.padNumber = padNumber;
|
|
31
|
+
var getGlobal = function () {
|
|
32
|
+
if (typeof global !== "undefined") {
|
|
33
|
+
return global;
|
|
34
|
+
}
|
|
35
|
+
if (typeof window !== "undefined") {
|
|
36
|
+
return window;
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
};
|
|
40
|
+
exports.getGlobal = getGlobal;
|
|
31
41
|
var getGlobalFetch = function () {
|
|
32
|
-
|
|
42
|
+
var global = (0, exports.getGlobal)();
|
|
43
|
+
if (global && typeof global.fetch === "function") {
|
|
33
44
|
return global.fetch.bind(global);
|
|
34
45
|
}
|
|
35
|
-
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
return window.fetch.bind(window);
|
|
46
|
+
return undefined;
|
|
39
47
|
};
|
|
40
48
|
exports.getGlobalFetch = getGlobalFetch;
|
|
41
49
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
@@ -67,6 +67,10 @@ var FetchRequestBackend = /** @class */ (function () {
|
|
|
67
67
|
this.id = "fetch";
|
|
68
68
|
if (fetchLibrary !== undefined) {
|
|
69
69
|
this.fetch = fetchLibrary;
|
|
70
|
+
// otherwise window throws illegal invocation
|
|
71
|
+
if (fetchLibrary === (0, Utils_1.getGlobalFetch)()) {
|
|
72
|
+
this.fetch = fetchLibrary.bind((0, Utils_1.getGlobal)());
|
|
73
|
+
}
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
76
|
FetchRequestBackend.prototype.extractResponseFromError = function (error) {
|
package/esm/Api.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface ApiInfo {
|
|
|
12
12
|
readonly middleware?: RequestMiddleware[];
|
|
13
13
|
readonly config?: BaseRequestConfig | (() => BaseRequestConfig);
|
|
14
14
|
readonly mocking?: ApiMockingConfig;
|
|
15
|
+
readonly requestBackend?: RequestBackend;
|
|
15
16
|
}
|
|
16
17
|
export declare class Api implements ApiInfo {
|
|
17
18
|
readonly baseUrl: string;
|
|
@@ -19,9 +20,11 @@ export declare class Api implements ApiInfo {
|
|
|
19
20
|
readonly middleware: RequestMiddleware[];
|
|
20
21
|
readonly config?: BaseRequestConfig | (() => BaseRequestConfig);
|
|
21
22
|
readonly mocking?: ApiMockingConfig;
|
|
23
|
+
readonly requestBackend: RequestBackend;
|
|
22
24
|
protected readonly endpoints: Record<string, Endpoint>;
|
|
23
25
|
constructor(info: ApiInfo);
|
|
24
26
|
endpoint(): EndpointBuilder;
|
|
27
|
+
getRequestBackend(): RequestBackend;
|
|
25
28
|
getConfig(): BaseRequestConfig;
|
|
26
29
|
private hotRequest;
|
|
27
30
|
get: <R = unknown>(path: string, config: RequestConfig) => Promise<ApiResponse<R>>;
|
package/esm/Api.js
CHANGED
|
@@ -46,10 +46,14 @@ class HotRequestHost {
|
|
|
46
46
|
computePath(path, config) {
|
|
47
47
|
return path.startsWith("/") ? path : `/${path}`;
|
|
48
48
|
}
|
|
49
|
+
getRequestBackend() {
|
|
50
|
+
return this.api.getRequestBackend();
|
|
51
|
+
}
|
|
49
52
|
}
|
|
53
|
+
let defaultBackendMessageShown = false;
|
|
50
54
|
export class Api {
|
|
51
55
|
constructor(info) {
|
|
52
|
-
var _a;
|
|
56
|
+
var _a, _b;
|
|
53
57
|
this.endpoints = {};
|
|
54
58
|
this.hotRequest = (requestMethod) => (path, config) => __awaiter(this, void 0, void 0, function* () {
|
|
55
59
|
return (yield Requester.submit(new HotRequestHost(this, path, requestMethod), config, null));
|
|
@@ -65,10 +69,27 @@ export class Api {
|
|
|
65
69
|
this.endpoints = {};
|
|
66
70
|
this.config = info.config;
|
|
67
71
|
this.mocking = (_a = info.mocking) !== null && _a !== void 0 ? _a : undefined;
|
|
72
|
+
const requestBackend = (_b = info.requestBackend) !== null && _b !== void 0 ? _b : getRequestBackend();
|
|
73
|
+
if (!requestBackend) {
|
|
74
|
+
throw new Error("[api-def] No request backend provided in either Api options or globally, use `setRequestBackend()` to set one or pass one via `requestBackend`");
|
|
75
|
+
}
|
|
76
|
+
this.requestBackend = requestBackend;
|
|
77
|
+
if (!info.requestBackend) {
|
|
78
|
+
if (process.env.NODE_ENV === "development") {
|
|
79
|
+
if (isRequestBackendDefault() && !defaultBackendMessageShown) {
|
|
80
|
+
defaultBackendMessageShown = true;
|
|
81
|
+
// eslint-disable-next-line
|
|
82
|
+
console.warn("[api-def] Using default fetch backend, you can use a different one with 'setRequestBackend()' (dev only message)");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
68
86
|
}
|
|
69
87
|
endpoint() {
|
|
70
88
|
return new EndpointBuilder(this);
|
|
71
89
|
}
|
|
90
|
+
getRequestBackend() {
|
|
91
|
+
return this.requestBackend;
|
|
92
|
+
}
|
|
72
93
|
getConfig() {
|
|
73
94
|
return ((typeof this.config === "function" ? this.config() : this.config) ||
|
|
74
95
|
{});
|
package/esm/ApiTypes.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import RequestContext from "./RequestContext";
|
|
2
2
|
import { Api } from "./Api";
|
|
3
3
|
import { CacheSource, EventResultType, RequestEvent, RequestMethod, ResponseType } from "./ApiConstants";
|
|
4
|
+
import RequestBackend from "./backend/RequestBackend";
|
|
4
5
|
export declare type AcceptableStatus = number | [min: number, max: number];
|
|
5
6
|
export declare type Headers = Record<string, string | number | boolean | null | undefined>;
|
|
6
7
|
export declare type Params = string;
|
|
@@ -20,10 +21,16 @@ interface QueryHandling {
|
|
|
20
21
|
parse: QueryParse;
|
|
21
22
|
stringify: QueryStringify;
|
|
22
23
|
}
|
|
24
|
+
export interface RetryOptions {
|
|
25
|
+
maxAttempts: number;
|
|
26
|
+
shouldRetry?: (error: Error) => boolean;
|
|
27
|
+
minDelay?: number;
|
|
28
|
+
maxDelay?: number;
|
|
29
|
+
}
|
|
23
30
|
export interface BaseRequestConfig {
|
|
24
31
|
cache?: number | boolean;
|
|
25
32
|
lock?: RequestLock;
|
|
26
|
-
retry?: number | false;
|
|
33
|
+
retry?: number | false | RetryOptions;
|
|
27
34
|
headers?: Readonly<Headers>;
|
|
28
35
|
acceptableStatus?: AcceptableStatus[];
|
|
29
36
|
/**
|
|
@@ -82,6 +89,7 @@ export interface RequestHost {
|
|
|
82
89
|
readonly responseType: ResponseType | undefined;
|
|
83
90
|
computeConfig<P extends Params | undefined, Q extends Query | undefined, B extends Body | undefined>(config: RequestConfig<P, Q, B>): ComputedRequestConfig<P, Q, B>;
|
|
84
91
|
computePath(path: string, config: RequestConfig): string;
|
|
92
|
+
getRequestBackend(): RequestBackend;
|
|
85
93
|
}
|
|
86
94
|
export interface CancelledRequestError extends Error {
|
|
87
95
|
isCancelledRequest: true;
|
package/esm/Endpoint.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Api } from "./Api";
|
|
|
2
2
|
import { ApiResponse, BaseRequestConfig, Body, ComputedRequestConfig, Params, Query, RequestConfig, RequestHost } from "./ApiTypes";
|
|
3
3
|
import * as Mocking from "./MockingTypes";
|
|
4
4
|
import { RequestMethod, ResponseType } from "./ApiConstants";
|
|
5
|
+
import RequestBackend from "./backend/RequestBackend";
|
|
5
6
|
export interface EndpointConfig<R, P extends Params | undefined, Q extends Query | undefined, B extends Body | undefined> {
|
|
6
7
|
readonly id: string;
|
|
7
8
|
readonly method: RequestMethod;
|
|
@@ -44,4 +45,5 @@ export default class Endpoint<R = any, P extends Params | undefined = Params | u
|
|
|
44
45
|
computePath(path: string, request: RequestConfig): string;
|
|
45
46
|
get baseUrl(): string;
|
|
46
47
|
computeConfig<P extends Params | undefined, Q extends Query | undefined, B extends Body | undefined>(config: RequestConfig<P, Q, B>): ComputedRequestConfig<P, Q, B>;
|
|
48
|
+
getRequestBackend(): RequestBackend;
|
|
47
49
|
}
|
package/esm/Endpoint.js
CHANGED
|
@@ -8,8 +8,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import * as Requester from "./Requester";
|
|
11
|
-
import { COMPUTED_CONFIG_SYMBOL, } from "./ApiTypes";
|
|
12
|
-
import * as Utils from "./Utils";
|
|
13
11
|
import { computeRequestConfig } from "./RequestConfig";
|
|
14
12
|
export default class Endpoint {
|
|
15
13
|
constructor(api, info) {
|
|
@@ -59,15 +57,13 @@ export default class Endpoint {
|
|
|
59
57
|
}
|
|
60
58
|
computeConfig(config) {
|
|
61
59
|
const apiDefaults = this.api.getConfig();
|
|
62
|
-
const computedConfig = Utils.assign({
|
|
63
|
-
[COMPUTED_CONFIG_SYMBOL]: true,
|
|
64
|
-
}, apiDefaults, this.config, config);
|
|
65
60
|
return computeRequestConfig([
|
|
66
61
|
apiDefaults,
|
|
67
62
|
this.config,
|
|
68
63
|
config,
|
|
69
64
|
]);
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
}
|
|
66
|
+
getRequestBackend() {
|
|
67
|
+
return this.api.getRequestBackend();
|
|
72
68
|
}
|
|
73
69
|
}
|
package/esm/RequestError.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface RequestError extends Error {
|
|
|
13
13
|
isRequestError: true;
|
|
14
14
|
response: ApiResponse | undefined | null;
|
|
15
15
|
code: string;
|
|
16
|
+
attempts: number;
|
|
16
17
|
}
|
|
17
18
|
export declare const isRequestError: (error: Error) => error is RequestError;
|
|
18
19
|
export interface RequestErrorConfig {
|
package/esm/RequestError.js
CHANGED
package/esm/Requester.js
CHANGED
|
@@ -7,10 +7,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import * as ApiUtils from "./ApiUtils";
|
|
11
10
|
import { inferResponseType, isAcceptableStatus, isNetworkError } from "./ApiUtils";
|
|
12
11
|
import RequestContext from "./RequestContext";
|
|
13
|
-
import * as Api from "./Api";
|
|
14
12
|
import { EventResultType, RequestEvent } from "./ApiConstants";
|
|
15
13
|
import retry from "./util/retry";
|
|
16
14
|
import MockRequestBackend from "./backend/MockRequestBackend";
|
|
@@ -21,10 +19,7 @@ const runningOperations = {};
|
|
|
21
19
|
const MOCK_REQUEST_BACKEND = new MockRequestBackend();
|
|
22
20
|
export const submit = (host, config, mocking) => __awaiter(void 0, void 0, void 0, function* () {
|
|
23
21
|
const computedConfig = host.computeConfig(config);
|
|
24
|
-
const backend = mocking ? MOCK_REQUEST_BACKEND :
|
|
25
|
-
if (!backend) {
|
|
26
|
-
throw new Error("[api-def] Please specify a backend you wish to use, this can be done either with 'setRequestBackend()'");
|
|
27
|
-
}
|
|
22
|
+
const backend = mocking ? MOCK_REQUEST_BACKEND : host.getRequestBackend();
|
|
28
23
|
const context = new RequestContext(backend, host, computedConfig, host.computePath(host.path, config), mocking);
|
|
29
24
|
const { key } = context;
|
|
30
25
|
// don't do this -- should only be for GET requests anyway and should be opt-in
|
|
@@ -63,27 +58,28 @@ export const submit = (host, config, mocking) => __awaiter(void 0, void 0, void
|
|
|
63
58
|
}
|
|
64
59
|
}
|
|
65
60
|
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (process.env.NODE_ENV === "development") {
|
|
70
|
-
if (Api.isRequestBackendDefault() && !defaultBackendMessageShown) {
|
|
71
|
-
defaultBackendMessageShown = true;
|
|
72
|
-
// eslint-disable-next-line
|
|
73
|
-
console.warn("[api-def] Using default fetch backend, you can use a different one with 'setRequestBackend()' (dev only message)");
|
|
74
|
-
}
|
|
61
|
+
const parseRetryOptions = (retryConfig) => {
|
|
62
|
+
if (retryConfig && typeof retryConfig === "object") {
|
|
63
|
+
return retryConfig;
|
|
75
64
|
}
|
|
65
|
+
if (typeof retryConfig === "number") {
|
|
66
|
+
return { maxAttempts: retryConfig };
|
|
67
|
+
}
|
|
68
|
+
return { maxAttempts: 0 };
|
|
69
|
+
};
|
|
70
|
+
const makeRequest = (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
71
|
+
var _a, _b, _c;
|
|
76
72
|
const beforeSendEventResult = yield context.triggerEvent(RequestEvent.BeforeSend);
|
|
77
73
|
if (beforeSendEventResult &&
|
|
78
74
|
beforeSendEventResult.type === EventResultType.Respond) {
|
|
79
75
|
return (context.response = beforeSendEventResult.response);
|
|
80
76
|
}
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
retries:
|
|
77
|
+
const retryOptions = parseRetryOptions((_a = context.computedConfig) === null || _a === void 0 ? void 0 : _a.retry);
|
|
78
|
+
const internalRetryOptions = {
|
|
79
|
+
retries: retryOptions.maxAttempts,
|
|
84
80
|
// assume most users won't want to tune the delay between retries
|
|
85
|
-
minTimeout:
|
|
86
|
-
maxTimeout:
|
|
81
|
+
minTimeout: (_b = retryOptions.minDelay) !== null && _b !== void 0 ? _b : 200,
|
|
82
|
+
maxTimeout: (_c = retryOptions.maxDelay) !== null && _c !== void 0 ? _c : 1000,
|
|
87
83
|
randomize: true,
|
|
88
84
|
};
|
|
89
85
|
context.stats.attempt = 0;
|
|
@@ -117,15 +113,19 @@ const makeRequest = (context) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
117
113
|
return errorEventResult.response;
|
|
118
114
|
}
|
|
119
115
|
// allow retry logic to handle network errors
|
|
120
|
-
const shouldNaturallyRetry = ApiUtils.isNetworkError(error);
|
|
121
|
-
if (shouldNaturallyRetry) {
|
|
122
|
-
|
|
123
|
-
}
|
|
116
|
+
//const shouldNaturallyRetry = ApiUtils.isNetworkError(error);
|
|
117
|
+
//if (shouldNaturallyRetry) {
|
|
118
|
+
// throw error;
|
|
119
|
+
//}
|
|
124
120
|
// if we have an event that tells us to retry, we must do it
|
|
125
121
|
const forceRetry = (errorEventResult === null || errorEventResult === void 0 ? void 0 : errorEventResult.type) === EventResultType.Retry;
|
|
126
122
|
if (forceRetry) {
|
|
127
123
|
return performRequest(fnBail, attemptCount);
|
|
128
124
|
}
|
|
125
|
+
// allow retry logic to handle
|
|
126
|
+
if (!retryOptions.shouldRetry || retryOptions.shouldRetry(error)) {
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
129
|
// error is unrecoverable, bail
|
|
130
130
|
const unrecoverableErrorEventResult = yield context.triggerEvent(RequestEvent.UnrecoverableError);
|
|
131
131
|
if (unrecoverableErrorEventResult) {
|
|
@@ -136,11 +136,11 @@ const makeRequest = (context) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
136
136
|
fnBail(error);
|
|
137
137
|
}
|
|
138
138
|
});
|
|
139
|
-
const response = yield retry(performRequest,
|
|
139
|
+
const response = yield retry(performRequest, internalRetryOptions);
|
|
140
140
|
return (response);
|
|
141
141
|
});
|
|
142
142
|
const parseResponse = (context, response, error) => __awaiter(void 0, void 0, void 0, function* () {
|
|
143
|
-
var
|
|
143
|
+
var _d;
|
|
144
144
|
if (response) {
|
|
145
145
|
const parsedResponse = yield context.backend.convertResponse(context, response);
|
|
146
146
|
// lowercase all header names
|
|
@@ -165,7 +165,7 @@ const parseResponse = (context, response, error) => __awaiter(void 0, void 0, vo
|
|
|
165
165
|
if (parsedResponse.data &&
|
|
166
166
|
typeof parsedResponse.data === "object") {
|
|
167
167
|
const data = response.data;
|
|
168
|
-
if (((
|
|
168
|
+
if (((_d = data.constructor) === null || _d === void 0 ? void 0 : _d.name) === "ArrayBuffer") {
|
|
169
169
|
try {
|
|
170
170
|
const decodedData = (response.data = textDecode(data));
|
|
171
171
|
response.data = JSON.parse(decodedData);
|
package/esm/Utils.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export declare const assign: typeof Object["assign"];
|
|
|
2
2
|
export declare const padNumber: (stringOrNumber: string | number, maxLength: number) => string;
|
|
3
3
|
export declare type EnumOf<T extends Record<string, any>> = T[keyof T];
|
|
4
4
|
export declare type Fetch = typeof window.fetch;
|
|
5
|
+
export declare const getGlobal: () => any;
|
|
5
6
|
export declare const getGlobalFetch: () => Fetch | undefined;
|
|
6
7
|
export declare const noop: () => void;
|
|
7
8
|
/**
|
package/esm/Utils.js
CHANGED
|
@@ -24,14 +24,21 @@ export const padNumber = (stringOrNumber, maxLength) => {
|
|
|
24
24
|
? string
|
|
25
25
|
: "0".repeat(maxLength - string.length) + string;
|
|
26
26
|
};
|
|
27
|
+
export const getGlobal = () => {
|
|
28
|
+
if (typeof global !== "undefined") {
|
|
29
|
+
return global;
|
|
30
|
+
}
|
|
31
|
+
if (typeof window !== "undefined") {
|
|
32
|
+
return window;
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
};
|
|
27
36
|
export const getGlobalFetch = () => {
|
|
28
|
-
|
|
37
|
+
const global = getGlobal();
|
|
38
|
+
if (global && typeof global.fetch === "function") {
|
|
29
39
|
return global.fetch.bind(global);
|
|
30
40
|
}
|
|
31
|
-
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
return window.fetch.bind(window);
|
|
41
|
+
return undefined;
|
|
35
42
|
};
|
|
36
43
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
37
44
|
export const noop = () => {
|
|
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import * as Utils from "../Utils";
|
|
11
|
-
import { getGlobalFetch } from "../Utils";
|
|
11
|
+
import { getGlobal, getGlobalFetch } from "../Utils";
|
|
12
12
|
import { inferResponseType } from "../ApiUtils";
|
|
13
13
|
class FetchError extends Error {
|
|
14
14
|
}
|
|
@@ -18,6 +18,10 @@ export default class FetchRequestBackend {
|
|
|
18
18
|
this.id = "fetch";
|
|
19
19
|
if (fetchLibrary !== undefined) {
|
|
20
20
|
this.fetch = fetchLibrary;
|
|
21
|
+
// otherwise window throws illegal invocation
|
|
22
|
+
if (fetchLibrary === getGlobalFetch()) {
|
|
23
|
+
this.fetch = fetchLibrary.bind(getGlobal());
|
|
24
|
+
}
|
|
21
25
|
}
|
|
22
26
|
}
|
|
23
27
|
extractResponseFromError(error) {
|