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 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
- delete computedConfig.queryParser;
108
- return computedConfig;
101
+ };
102
+ Endpoint.prototype.getRequestBackend = function () {
103
+ return this.api.getRequestBackend();
109
104
  };
110
105
  return Endpoint;
111
106
  }());
@@ -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 {
@@ -20,6 +20,7 @@ var convertToRequestError = function (config) {
20
20
  response: response,
21
21
  code: code,
22
22
  isRequestError: true,
23
+ attempts: context.stats.attempt,
23
24
  request: {
24
25
  url: context.getRequestUrl().href,
25
26
  query: context.computedConfig.queryObject,
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 : Api.getRequestBackend();
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 defaultBackendMessageShown = false;
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, maxRetries, retryOpts, performRequest, response;
115
- var _a;
116
- return __generator(this, function (_b) {
117
- switch (_b.label) {
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 = _b.sent();
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
- maxRetries = ((_a = context.computedConfig) === null || _a === void 0 ? void 0 : _a.retry) || 0;
134
- retryOpts = {
135
- retries: maxRetries,
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: 1 * 1000,
138
- maxTimeout: 5 * 1000,
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, shouldNaturallyRetry, forceRetry, unrecoverableErrorEventResult;
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, retryOpts)];
202
+ return [4 /*yield*/, (0, retry_1.default)(performRequest, internalRetryOptions)];
208
203
  case 2:
209
- response = _b.sent();
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
- if (typeof global !== "undefined" && typeof global.fetch === "function") {
42
+ var global = (0, exports.getGlobal)();
43
+ if (global && typeof global.fetch === "function") {
33
44
  return global.fetch.bind(global);
34
45
  }
35
- if (typeof window === "undefined") {
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
- delete computedConfig.queryParser;
71
- return computedConfig;
65
+ }
66
+ getRequestBackend() {
67
+ return this.api.getRequestBackend();
72
68
  }
73
69
  }
@@ -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 {
@@ -16,6 +16,7 @@ export const convertToRequestError = (config) => {
16
16
  response: response,
17
17
  code: code,
18
18
  isRequestError: true,
19
+ attempts: context.stats.attempt,
19
20
  request: {
20
21
  url: context.getRequestUrl().href,
21
22
  query: context.computedConfig.queryObject,
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 : Api.getRequestBackend();
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
- let defaultBackendMessageShown = false;
67
- const makeRequest = (context) => __awaiter(void 0, void 0, void 0, function* () {
68
- var _a;
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 maxRetries = ((_a = context.computedConfig) === null || _a === void 0 ? void 0 : _a.retry) || 0;
82
- const retryOpts = {
83
- retries: maxRetries,
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: 1 * 1000,
86
- maxTimeout: 5 * 1000,
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
- throw error;
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, retryOpts);
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 _b;
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 (((_b = data.constructor) === null || _b === void 0 ? void 0 : _b.name) === "ArrayBuffer") {
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
- if (typeof global !== "undefined" && typeof global.fetch === "function") {
37
+ const global = getGlobal();
38
+ if (global && typeof global.fetch === "function") {
29
39
  return global.fetch.bind(global);
30
40
  }
31
- if (typeof window === "undefined") {
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-def",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "Typed API definitions with middleware support",
5
5
  "main": "cjs/index.js",
6
6
  "types": "esm/index.d.ts",