api-def 0.6.0-alpha1 → 0.6.0-alpha12

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 CHANGED
@@ -4,7 +4,7 @@ import { CacheSource, EventResultType, RequestEvent, RequestMethod, ResponseType
4
4
  export declare type AcceptableStatus = number | [min: number, max: number];
5
5
  export declare type Headers = Record<string, string | number | boolean | null | undefined>;
6
6
  export declare type Params = string;
7
- export declare type Query = Record<string, string | number | boolean | undefined | null>;
7
+ export declare type Query = Record<string, any>;
8
8
  export declare type Body = string | number | Record<string, any>;
9
9
  export interface ApiResponse<T = any> {
10
10
  status: number;
@@ -17,6 +17,7 @@ export interface BaseRequestConfig {
17
17
  retry?: number | false;
18
18
  headers?: Readonly<Headers>;
19
19
  acceptableStatus?: AcceptableStatus[];
20
+ queryParser?: (query: any) => string;
20
21
  }
21
22
  export declare type RequestConfig<P extends Params | undefined = Params | undefined, Q extends Query | undefined = Query | undefined, B extends Body | undefined = Body | undefined> = (P extends undefined ? {
22
23
  params?: never;
package/cjs/ApiUtils.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { AcceptableStatus, ApiResponse, CancelledRequestError } from "./ApiTypes";
1
+ import { AcceptableStatus, CancelledRequestError } from "./ApiTypes";
2
2
  import { ResponseType } from "./ApiConstants";
3
3
  export declare const isCancelledError: (error: Error) => error is CancelledRequestError;
4
4
  export declare const isNetworkError: (error: Error) => boolean;
5
- export declare const parseResponseDataToObject: (response: ApiResponse) => void;
6
5
  export declare const isAcceptableStatus: (status: number, acceptableStatus?: AcceptableStatus[] | undefined) => boolean;
7
6
  export declare const inferResponseType: (contentType: string | null | undefined) => ResponseType;
package/cjs/ApiUtils.js CHANGED
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.inferResponseType = exports.isAcceptableStatus = exports.parseResponseDataToObject = exports.isNetworkError = exports.isCancelledError = void 0;
4
- var TextDecoding_1 = require("./TextDecoding");
3
+ exports.inferResponseType = exports.isAcceptableStatus = exports.isNetworkError = exports.isCancelledError = void 0;
5
4
  var isCancelledError = function (error) {
6
5
  return "isCancelledRequest" in error;
7
6
  };
@@ -13,22 +12,6 @@ var isNetworkError = function (error) {
13
12
  ((_a = error.constructor) === null || _a === void 0 ? void 0 : _a.name) === "NetworkError");
14
13
  };
15
14
  exports.isNetworkError = isNetworkError;
16
- var parseResponseDataToObject = function (response) {
17
- if (response.data &&
18
- typeof response.data === "object") {
19
- var data = response.data;
20
- if (data.constructor && data.constructor.name === "ArrayBuffer") {
21
- try {
22
- var decodedData = (response.data = TextDecoding_1.textDecode(data));
23
- response.data = JSON.parse(decodedData);
24
- }
25
- catch (e) {
26
- console.warn("Couldn't parse array buffer content to JSON response", e);
27
- }
28
- }
29
- }
30
- };
31
- exports.parseResponseDataToObject = parseResponseDataToObject;
32
15
  var DEFAULT_ACCEPTABLE_STATUS = [[200, 299], 304];
33
16
  var isAcceptableStatus = function (status, acceptableStatus) {
34
17
  var acceptable = acceptableStatus !== null && acceptableStatus !== void 0 ? acceptableStatus : DEFAULT_ACCEPTABLE_STATUS;
@@ -49,11 +32,21 @@ var isAcceptableStatus = function (status, acceptableStatus) {
49
32
  return (false);
50
33
  };
51
34
  exports.isAcceptableStatus = isAcceptableStatus;
35
+ var TEXT_CONTENT_TYPES = ["text/plain"];
52
36
  var JSON_CONTENT_TYPES = ["text/json", "application/json"];
37
+ var ARRRAY_BUFFER_CONTENT_TYPES = ["application/octet-stream"];
53
38
  var inferResponseType = function (contentType) {
54
39
  var contentTypePart = contentType === null || contentType === void 0 ? void 0 : contentType.split(";")[0].trim();
55
- if (contentTypePart && JSON_CONTENT_TYPES.includes(contentTypePart)) {
56
- return "json";
40
+ if (contentTypePart) {
41
+ if (TEXT_CONTENT_TYPES.includes(contentTypePart)) {
42
+ return "text";
43
+ }
44
+ else if (JSON_CONTENT_TYPES.includes(contentTypePart)) {
45
+ return "json";
46
+ }
47
+ else if (ARRRAY_BUFFER_CONTENT_TYPES.includes(contentTypePart)) {
48
+ return "arraybuffer";
49
+ }
57
50
  }
58
51
  return "text";
59
52
  };
package/cjs/Requester.js CHANGED
@@ -56,11 +56,12 @@ var ApiConstants_1 = require("./ApiConstants");
56
56
  var retry_1 = require("./util/retry");
57
57
  var MockRequestBackend_1 = require("./backend/MockRequestBackend");
58
58
  var RequestError_1 = require("./RequestError");
59
+ var TextDecoding_1 = require("./TextDecoding");
59
60
  var locks = {};
60
61
  var runningOperations = {};
61
62
  var MOCK_REQUEST_BACKEND = new MockRequestBackend_1.default();
62
63
  var submit = function (host, config, mocking) { return __awaiter(void 0, void 0, void 0, function () {
63
- var computedConfig, backend, context, key, sameRequest, lock, lockedContext, response, successEventResult, error_1;
64
+ var computedConfig, backend, context, key, lock, lockedContext, response, successEventResult, error_1;
64
65
  return __generator(this, function (_a) {
65
66
  switch (_a.label) {
66
67
  case 0:
@@ -71,10 +72,6 @@ var submit = function (host, config, mocking) { return __awaiter(void 0, void 0,
71
72
  }
72
73
  context = new RequestContext_1.default(backend, host, computedConfig, host.computePath(host.path, config), mocking);
73
74
  key = context.key;
74
- sameRequest = runningOperations[key];
75
- if (sameRequest) {
76
- return [2 /*return*/, sameRequest];
77
- }
78
75
  lock = (context.computedConfig || {}).lock;
79
76
  if (lock) {
80
77
  lockedContext = locks[lock];
@@ -122,6 +119,7 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
122
119
  if (process.env.NODE_ENV === "development") {
123
120
  if (Api.isRequestBackendDefault() && !defaultBackendMessageShown) {
124
121
  defaultBackendMessageShown = true;
122
+ // eslint-disable-next-line
125
123
  console.warn("[api-def] Using default fetch backend, you can use a different one with 'setRequestBackend()' (dev only message)");
126
124
  }
127
125
  }
@@ -177,10 +175,6 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
177
175
  error = _b.sent();
178
176
  context.error = error;
179
177
  context.response = error.response;
180
- // transform array buffer responses to objs
181
- if (context.response) {
182
- ApiUtils.parseResponseDataToObject(context.response);
183
- }
184
178
  return [4 /*yield*/, context.triggerEvent(ApiConstants_1.RequestEvent.Error)];
185
179
  case 6:
186
180
  errorEventResult = _b.sent();
@@ -217,16 +211,46 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
217
211
  });
218
212
  }); };
219
213
  var parseResponse = function (context, response, error) { return __awaiter(void 0, void 0, void 0, function () {
220
- var parsedResponse;
221
- return __generator(this, function (_a) {
222
- switch (_a.label) {
214
+ var parsedResponse, contentType, inferredResponseType, data, decodedData;
215
+ var _a;
216
+ return __generator(this, function (_b) {
217
+ switch (_b.label) {
223
218
  case 0:
224
219
  if (!response) return [3 /*break*/, 2];
225
- return [4 /*yield*/, context.backend.convertResponse(context, response, error)];
220
+ return [4 /*yield*/, context.backend.convertResponse(context, response)];
226
221
  case 1:
227
- parsedResponse = _a.sent();
228
- if (parsedResponse) {
229
- ApiUtils.parseResponseDataToObject(parsedResponse);
222
+ parsedResponse = _b.sent();
223
+ contentType = response.headers["Content-Type"];
224
+ inferredResponseType = ApiUtils_1.inferResponseType(contentType);
225
+ if (!error) {
226
+ // expand to array buffer once we support that in inferResponseType
227
+ if (inferredResponseType === "text" && context.responseType === "json") {
228
+ throw RequestError_1.convertToRequestError({
229
+ error: new Error("[api-def] Expected '" + context.responseType + "' response, got '" + inferredResponseType + "' (from 'Content-Type' of '" + contentType + "')"),
230
+ code: RequestError_1.RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
231
+ response: parsedResponse,
232
+ });
233
+ }
234
+ // transform arrayBuffer to json
235
+ if (inferredResponseType === "arraybuffer" && context.responseType === "json") {
236
+ if (parsedResponse.data &&
237
+ typeof parsedResponse.data === "object") {
238
+ data = response.data;
239
+ if (((_a = data.constructor) === null || _a === void 0 ? void 0 : _a.name) === "ArrayBuffer") {
240
+ try {
241
+ decodedData = (response.data = TextDecoding_1.textDecode(data));
242
+ response.data = JSON.parse(decodedData);
243
+ }
244
+ catch (e) {
245
+ throw RequestError_1.convertToRequestError({
246
+ error: new Error("[api-def] Expected '" + context.responseType + "' response, got '" + inferredResponseType + "' (from 'Content-Type' of '" + contentType + "')"),
247
+ code: RequestError_1.RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
248
+ response: parsedResponse,
249
+ });
250
+ }
251
+ }
252
+ }
253
+ }
230
254
  }
231
255
  return [2 /*return*/, parsedResponse];
232
256
  case 2: return [2 /*return*/, response];
@@ -0,0 +1,5 @@
1
+ import Endpoint from "./Endpoint";
2
+ export declare type ResponseOf<E extends Endpoint<any, any, any, any>> = E extends Endpoint<infer R, any, any, any> ? R : never;
3
+ export declare type ParamsOf<E extends Endpoint<any, any, any, any>> = E extends Endpoint<any, infer P, any, any> ? P : never;
4
+ export declare type QueryOf<E extends Endpoint<any, any, any, any>> = E extends Endpoint<any, any, infer Q, any> ? Q : never;
5
+ export declare type BodyOf<E extends Endpoint<any, any, any, any>> = E extends Endpoint<any, any, any, infer B> ? B : never;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -37,8 +37,6 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.isAxiosError = void 0;
40
- var ApiUtils_1 = require("../ApiUtils");
41
- var RequestError_1 = require("../RequestError");
42
40
  var axios;
43
41
  var isAxiosError = function (error) {
44
42
  return "isAxiosError" in error;
@@ -62,16 +60,7 @@ var AxiosRequestBackend = /** @class */ (function () {
62
60
  };
63
61
  AxiosRequestBackend.prototype.convertResponse = function (context, response) {
64
62
  return __awaiter(this, void 0, void 0, function () {
65
- var inferredResponseType;
66
63
  return __generator(this, function (_a) {
67
- inferredResponseType = ApiUtils_1.inferResponseType(response.headers["Content-Type"]);
68
- // expand to array buffer once we support that in inferResponseType
69
- if (inferredResponseType === "text" && context.responseType === "json") {
70
- throw RequestError_1.convertToRequestError({
71
- error: new Error("[api-def] Expected '" + context.responseType + "' response, got '" + inferredResponseType + "'"),
72
- code: RequestError_1.RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
73
- });
74
- }
75
64
  return [2 /*return*/, response];
76
65
  });
77
66
  });
@@ -90,6 +79,7 @@ var AxiosRequestBackend = /** @class */ (function () {
90
79
  cancelToken: new axios.CancelToken(function (cancellerFunc) {
91
80
  canceler = cancellerFunc;
92
81
  }),
82
+ paramsSerializer: context.computedConfig.queryParser,
93
83
  });
94
84
  return {
95
85
  promise: promise,
@@ -9,7 +9,7 @@ export default class FetchRequestBackend implements RequestBackend<Response> {
9
9
  extractResponseFromError(error: Error): Promise<Response | null | undefined>;
10
10
  convertResponse<T>(context: RequestContext, response: Response & {
11
11
  __text?: string;
12
- }, error?: boolean): Promise<ApiResponse<T>>;
12
+ }): Promise<ApiResponse<T>>;
13
13
  makeRequest(context: RequestContext): RequestOperation<Response>;
14
14
  getErrorInfo(error: Error, response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
15
15
  }
@@ -55,7 +55,6 @@ var Utils = require("../Utils");
55
55
  var Utils_1 = require("../Utils");
56
56
  var ApiConstants_1 = require("../ApiConstants");
57
57
  var ApiUtils_1 = require("../ApiUtils");
58
- var RequestError_1 = require("../RequestError");
59
58
  var FetchError = /** @class */ (function (_super) {
60
59
  __extends(FetchError, _super);
61
60
  function FetchError() {
@@ -83,58 +82,47 @@ var FetchRequestBackend = /** @class */ (function () {
83
82
  });
84
83
  });
85
84
  };
86
- FetchRequestBackend.prototype.convertResponse = function (context, response, error) {
85
+ FetchRequestBackend.prototype.convertResponse = function (context, response) {
87
86
  return __awaiter(this, void 0, void 0, function () {
88
- var data, inferredResponseType, responseType, _a, error_1, status;
87
+ var contentType, responseType, _a, data, status, headers, error_1;
89
88
  return __generator(this, function (_b) {
90
89
  switch (_b.label) {
91
90
  case 0:
92
- inferredResponseType = ApiUtils_1.inferResponseType(response.headers.get("Content-Type"));
93
- responseType = error ? inferredResponseType : context.responseType;
94
- // expand to array buffer once we support that in inferResponseType
95
- if (inferredResponseType === "text" && context.responseType === "json") {
96
- throw RequestError_1.convertToRequestError({
97
- error: new Error("[api-def] Expected '" + context.responseType + "' response, got '" + inferredResponseType + "'"),
98
- code: RequestError_1.RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
99
- });
100
- }
101
- _b.label = 1;
102
- case 1:
103
- _b.trys.push([1, 8, , 9]);
104
- if (!!response.__text) return [3 /*break*/, 3];
91
+ contentType = response.headers.get("Content-Type");
92
+ responseType = ApiUtils_1.inferResponseType(contentType);
93
+ if (!!response.__text) return [3 /*break*/, 2];
105
94
  _a = response;
106
95
  return [4 /*yield*/, response.clone().text()];
107
- case 2:
96
+ case 1:
108
97
  _a.__text = _b.sent();
98
+ _b.label = 2;
99
+ case 2:
100
+ data = response.__text;
101
+ status = response.status, headers = response.headers;
109
102
  _b.label = 3;
110
103
  case 3:
111
- if (!(responseType === ApiConstants_1.ResponseType.Text)) return [3 /*break*/, 4];
112
- data = response.__text;
113
- return [3 /*break*/, 7];
114
- case 4:
115
- if (!(responseType === ApiConstants_1.ResponseType.ArrayBuffer)) return [3 /*break*/, 6];
104
+ _b.trys.push([3, 7, , 8]);
105
+ if (!(responseType === ApiConstants_1.ResponseType.ArrayBuffer)) return [3 /*break*/, 5];
116
106
  return [4 /*yield*/, response.clone().arrayBuffer()];
117
- case 5:
107
+ case 4:
118
108
  data = _b.sent();
119
- return [3 /*break*/, 7];
120
- case 6:
109
+ return [3 /*break*/, 6];
110
+ case 5:
121
111
  if (responseType === ApiConstants_1.ResponseType.Json) {
122
112
  data = JSON.parse(response.__text);
123
113
  }
124
- _b.label = 7;
125
- case 7: return [3 /*break*/, 9];
126
- case 8:
114
+ _b.label = 6;
115
+ case 6: return [3 /*break*/, 8];
116
+ case 7:
127
117
  error_1 = _b.sent();
128
118
  throw Object.assign(new Error("[api-def] Invalid '" + context.responseType + "' response, got: '" + response.__text + "'"), {
129
119
  response: response,
130
120
  });
131
- case 9:
132
- status = response.status;
133
- return [2 /*return*/, {
134
- data: data,
135
- status: status,
136
- headers: response.headers,
137
- }];
121
+ case 8: return [2 /*return*/, {
122
+ data: data,
123
+ status: status,
124
+ headers: headers,
125
+ }];
138
126
  }
139
127
  });
140
128
  });
@@ -157,10 +145,15 @@ var FetchRequestBackend = /** @class */ (function () {
157
145
  }
158
146
  var url = new URL(path, origin);
159
147
  if (computedConfig.query) {
160
- var queryKeys = Object.keys(computedConfig.query);
161
- for (var i = 0; i < queryKeys.length; i++) {
162
- var key = queryKeys[i];
163
- url.searchParams.append(key, ((_a = computedConfig.query[key]) === null || _a === void 0 ? void 0 : _a.toString()) || "");
148
+ if (context.computedConfig.queryParser) {
149
+ url.search = context.computedConfig.queryParser(computedConfig.query);
150
+ }
151
+ else {
152
+ var queryKeys = Object.keys(computedConfig.query);
153
+ for (var i = 0; i < queryKeys.length; i++) {
154
+ var key = queryKeys[i];
155
+ url.searchParams.append(key, ((_a = computedConfig.query[key]) === null || _a === void 0 ? void 0 : _a.toString()) || "");
156
+ }
164
157
  }
165
158
  }
166
159
  // abort controller is a newer feature than fetch
@@ -203,7 +196,6 @@ var FetchRequestBackend = /** @class */ (function () {
203
196
  canceler: abortSignal
204
197
  ? function () { return !responded && abortController.abort(); }
205
198
  : function () {
206
- console.warn("Request aborted");
207
199
  softAbort = true;
208
200
  },
209
201
  };
@@ -10,7 +10,7 @@ export interface RequestBackendErrorInfo {
10
10
  export default interface RequestBackend<R = any> {
11
11
  readonly id: string;
12
12
  makeRequest(context: RequestContext): RequestOperation<R>;
13
- convertResponse<T>(context: RequestContext, response: R, error?: boolean): Promise<ApiResponse<T>>;
13
+ convertResponse<T>(context: RequestContext, response: R): Promise<ApiResponse<T>>;
14
14
  extractResponseFromError(error: Error): Promise<R | null | undefined>;
15
15
  getErrorInfo(error: Error, response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
16
16
  }
package/cjs/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./Api";
2
2
  export * from "./ApiTypes";
3
+ export * from "./UtilTypes";
3
4
  export * from "./ApiConstants";
4
5
  export { isRequestError } from "./RequestError";
5
6
  export { clearCache, setCacheBackend } from "./cache/Caching";
package/cjs/index.js CHANGED
@@ -14,6 +14,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.LoggingMiddleware = exports.CacheMiddleware = exports.FetchRequestBackend = exports.AxiosRequestBackend = exports.LocaleForageCacheBackend = exports.LocalStorageCacheBackend = exports.setCacheBackend = exports.clearCache = exports.isRequestError = void 0;
15
15
  __exportStar(require("./Api"), exports);
16
16
  __exportStar(require("./ApiTypes"), exports);
17
+ __exportStar(require("./UtilTypes"), exports);
17
18
  __exportStar(require("./ApiConstants"), exports);
18
19
  var RequestError_1 = require("./RequestError");
19
20
  Object.defineProperty(exports, "isRequestError", { enumerable: true, get: function () { return RequestError_1.isRequestError; } });
package/esm/ApiTypes.d.ts CHANGED
@@ -4,7 +4,7 @@ import { CacheSource, EventResultType, RequestEvent, RequestMethod, ResponseType
4
4
  export declare type AcceptableStatus = number | [min: number, max: number];
5
5
  export declare type Headers = Record<string, string | number | boolean | null | undefined>;
6
6
  export declare type Params = string;
7
- export declare type Query = Record<string, string | number | boolean | undefined | null>;
7
+ export declare type Query = Record<string, any>;
8
8
  export declare type Body = string | number | Record<string, any>;
9
9
  export interface ApiResponse<T = any> {
10
10
  status: number;
@@ -17,6 +17,7 @@ export interface BaseRequestConfig {
17
17
  retry?: number | false;
18
18
  headers?: Readonly<Headers>;
19
19
  acceptableStatus?: AcceptableStatus[];
20
+ queryParser?: (query: any) => string;
20
21
  }
21
22
  export declare type RequestConfig<P extends Params | undefined = Params | undefined, Q extends Query | undefined = Query | undefined, B extends Body | undefined = Body | undefined> = (P extends undefined ? {
22
23
  params?: never;
package/esm/ApiUtils.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { AcceptableStatus, ApiResponse, CancelledRequestError } from "./ApiTypes";
1
+ import { AcceptableStatus, CancelledRequestError } from "./ApiTypes";
2
2
  import { ResponseType } from "./ApiConstants";
3
3
  export declare const isCancelledError: (error: Error) => error is CancelledRequestError;
4
4
  export declare const isNetworkError: (error: Error) => boolean;
5
- export declare const parseResponseDataToObject: (response: ApiResponse) => void;
6
5
  export declare const isAcceptableStatus: (status: number, acceptableStatus?: AcceptableStatus[] | undefined) => boolean;
7
6
  export declare const inferResponseType: (contentType: string | null | undefined) => ResponseType;
package/esm/ApiUtils.js CHANGED
@@ -1,4 +1,3 @@
1
- import { textDecode } from "./TextDecoding";
2
1
  export var isCancelledError = function (error) {
3
2
  return "isCancelledRequest" in error;
4
3
  };
@@ -8,21 +7,6 @@ export var isNetworkError = function (error) {
8
7
  error.message === "Network Error" ||
9
8
  ((_a = error.constructor) === null || _a === void 0 ? void 0 : _a.name) === "NetworkError");
10
9
  };
11
- export var parseResponseDataToObject = function (response) {
12
- if (response.data &&
13
- typeof response.data === "object") {
14
- var data = response.data;
15
- if (data.constructor && data.constructor.name === "ArrayBuffer") {
16
- try {
17
- var decodedData = (response.data = textDecode(data));
18
- response.data = JSON.parse(decodedData);
19
- }
20
- catch (e) {
21
- console.warn("Couldn't parse array buffer content to JSON response", e);
22
- }
23
- }
24
- }
25
- };
26
10
  var DEFAULT_ACCEPTABLE_STATUS = [[200, 299], 304];
27
11
  export var isAcceptableStatus = function (status, acceptableStatus) {
28
12
  var acceptable = acceptableStatus !== null && acceptableStatus !== void 0 ? acceptableStatus : DEFAULT_ACCEPTABLE_STATUS;
@@ -42,11 +26,21 @@ export var isAcceptableStatus = function (status, acceptableStatus) {
42
26
  }
43
27
  return (false);
44
28
  };
29
+ var TEXT_CONTENT_TYPES = ["text/plain"];
45
30
  var JSON_CONTENT_TYPES = ["text/json", "application/json"];
31
+ var ARRRAY_BUFFER_CONTENT_TYPES = ["application/octet-stream"];
46
32
  export var inferResponseType = function (contentType) {
47
33
  var contentTypePart = contentType === null || contentType === void 0 ? void 0 : contentType.split(";")[0].trim();
48
- if (contentTypePart && JSON_CONTENT_TYPES.includes(contentTypePart)) {
49
- return "json";
34
+ if (contentTypePart) {
35
+ if (TEXT_CONTENT_TYPES.includes(contentTypePart)) {
36
+ return "text";
37
+ }
38
+ else if (JSON_CONTENT_TYPES.includes(contentTypePart)) {
39
+ return "json";
40
+ }
41
+ else if (ARRRAY_BUFFER_CONTENT_TYPES.includes(contentTypePart)) {
42
+ return "arraybuffer";
43
+ }
50
44
  }
51
45
  return "text";
52
46
  };
package/esm/Requester.js CHANGED
@@ -46,18 +46,19 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
46
46
  }
47
47
  };
48
48
  import * as ApiUtils from "./ApiUtils";
49
- import { isAcceptableStatus, isNetworkError } from "./ApiUtils";
49
+ import { inferResponseType, isAcceptableStatus, isNetworkError } from "./ApiUtils";
50
50
  import RequestContext from "./RequestContext";
51
51
  import * as Api from "./Api";
52
52
  import { EventResultType, RequestEvent } from "./ApiConstants";
53
53
  import retry from "./util/retry";
54
54
  import MockRequestBackend from "./backend/MockRequestBackend";
55
55
  import { convertToRequestError, isRequestError, RequestErrorCode } from "./RequestError";
56
+ import { textDecode } from "./TextDecoding";
56
57
  var locks = {};
57
58
  var runningOperations = {};
58
59
  var MOCK_REQUEST_BACKEND = new MockRequestBackend();
59
60
  export var submit = function (host, config, mocking) { return __awaiter(void 0, void 0, void 0, function () {
60
- var computedConfig, backend, context, key, sameRequest, lock, lockedContext, response, successEventResult, error_1;
61
+ var computedConfig, backend, context, key, lock, lockedContext, response, successEventResult, error_1;
61
62
  return __generator(this, function (_a) {
62
63
  switch (_a.label) {
63
64
  case 0:
@@ -68,10 +69,6 @@ export var submit = function (host, config, mocking) { return __awaiter(void 0,
68
69
  }
69
70
  context = new RequestContext(backend, host, computedConfig, host.computePath(host.path, config), mocking);
70
71
  key = context.key;
71
- sameRequest = runningOperations[key];
72
- if (sameRequest) {
73
- return [2 /*return*/, sameRequest];
74
- }
75
72
  lock = (context.computedConfig || {}).lock;
76
73
  if (lock) {
77
74
  lockedContext = locks[lock];
@@ -118,6 +115,7 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
118
115
  if (process.env.NODE_ENV === "development") {
119
116
  if (Api.isRequestBackendDefault() && !defaultBackendMessageShown) {
120
117
  defaultBackendMessageShown = true;
118
+ // eslint-disable-next-line
121
119
  console.warn("[api-def] Using default fetch backend, you can use a different one with 'setRequestBackend()' (dev only message)");
122
120
  }
123
121
  }
@@ -173,10 +171,6 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
173
171
  error = _b.sent();
174
172
  context.error = error;
175
173
  context.response = error.response;
176
- // transform array buffer responses to objs
177
- if (context.response) {
178
- ApiUtils.parseResponseDataToObject(context.response);
179
- }
180
174
  return [4 /*yield*/, context.triggerEvent(RequestEvent.Error)];
181
175
  case 6:
182
176
  errorEventResult = _b.sent();
@@ -213,16 +207,46 @@ var makeRequest = function (context) { return __awaiter(void 0, void 0, void 0,
213
207
  });
214
208
  }); };
215
209
  var parseResponse = function (context, response, error) { return __awaiter(void 0, void 0, void 0, function () {
216
- var parsedResponse;
217
- return __generator(this, function (_a) {
218
- switch (_a.label) {
210
+ var parsedResponse, contentType, inferredResponseType, data, decodedData;
211
+ var _a;
212
+ return __generator(this, function (_b) {
213
+ switch (_b.label) {
219
214
  case 0:
220
215
  if (!response) return [3 /*break*/, 2];
221
- return [4 /*yield*/, context.backend.convertResponse(context, response, error)];
216
+ return [4 /*yield*/, context.backend.convertResponse(context, response)];
222
217
  case 1:
223
- parsedResponse = _a.sent();
224
- if (parsedResponse) {
225
- ApiUtils.parseResponseDataToObject(parsedResponse);
218
+ parsedResponse = _b.sent();
219
+ contentType = response.headers["Content-Type"];
220
+ inferredResponseType = inferResponseType(contentType);
221
+ if (!error) {
222
+ // expand to array buffer once we support that in inferResponseType
223
+ if (inferredResponseType === "text" && context.responseType === "json") {
224
+ throw convertToRequestError({
225
+ error: new Error("[api-def] Expected '" + context.responseType + "' response, got '" + inferredResponseType + "' (from 'Content-Type' of '" + contentType + "')"),
226
+ code: RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
227
+ response: parsedResponse,
228
+ });
229
+ }
230
+ // transform arrayBuffer to json
231
+ if (inferredResponseType === "arraybuffer" && context.responseType === "json") {
232
+ if (parsedResponse.data &&
233
+ typeof parsedResponse.data === "object") {
234
+ data = response.data;
235
+ if (((_a = data.constructor) === null || _a === void 0 ? void 0 : _a.name) === "ArrayBuffer") {
236
+ try {
237
+ decodedData = (response.data = textDecode(data));
238
+ response.data = JSON.parse(decodedData);
239
+ }
240
+ catch (e) {
241
+ throw convertToRequestError({
242
+ error: new Error("[api-def] Expected '" + context.responseType + "' response, got '" + inferredResponseType + "' (from 'Content-Type' of '" + contentType + "')"),
243
+ code: RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
244
+ response: parsedResponse,
245
+ });
246
+ }
247
+ }
248
+ }
249
+ }
226
250
  }
227
251
  return [2 /*return*/, parsedResponse];
228
252
  case 2: return [2 /*return*/, response];
@@ -0,0 +1,5 @@
1
+ import Endpoint from "./Endpoint";
2
+ export declare type ResponseOf<E extends Endpoint<any, any, any, any>> = E extends Endpoint<infer R, any, any, any> ? R : never;
3
+ export declare type ParamsOf<E extends Endpoint<any, any, any, any>> = E extends Endpoint<any, infer P, any, any> ? P : never;
4
+ export declare type QueryOf<E extends Endpoint<any, any, any, any>> = E extends Endpoint<any, any, infer Q, any> ? Q : never;
5
+ export declare type BodyOf<E extends Endpoint<any, any, any, any>> = E extends Endpoint<any, any, any, infer B> ? B : never;
@@ -0,0 +1 @@
1
+ export {};
@@ -34,8 +34,6 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
34
34
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
35
  }
36
36
  };
37
- import { inferResponseType } from "../ApiUtils";
38
- import { convertToRequestError, RequestErrorCode } from "../RequestError";
39
37
  var axios;
40
38
  export var isAxiosError = function (error) {
41
39
  return "isAxiosError" in error;
@@ -58,16 +56,7 @@ var AxiosRequestBackend = /** @class */ (function () {
58
56
  };
59
57
  AxiosRequestBackend.prototype.convertResponse = function (context, response) {
60
58
  return __awaiter(this, void 0, void 0, function () {
61
- var inferredResponseType;
62
59
  return __generator(this, function (_a) {
63
- inferredResponseType = inferResponseType(response.headers["Content-Type"]);
64
- // expand to array buffer once we support that in inferResponseType
65
- if (inferredResponseType === "text" && context.responseType === "json") {
66
- throw convertToRequestError({
67
- error: new Error("[api-def] Expected '" + context.responseType + "' response, got '" + inferredResponseType + "'"),
68
- code: RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
69
- });
70
- }
71
60
  return [2 /*return*/, response];
72
61
  });
73
62
  });
@@ -86,6 +75,7 @@ var AxiosRequestBackend = /** @class */ (function () {
86
75
  cancelToken: new axios.CancelToken(function (cancellerFunc) {
87
76
  canceler = cancellerFunc;
88
77
  }),
78
+ paramsSerializer: context.computedConfig.queryParser,
89
79
  });
90
80
  return {
91
81
  promise: promise,
@@ -9,7 +9,7 @@ export default class FetchRequestBackend implements RequestBackend<Response> {
9
9
  extractResponseFromError(error: Error): Promise<Response | null | undefined>;
10
10
  convertResponse<T>(context: RequestContext, response: Response & {
11
11
  __text?: string;
12
- }, error?: boolean): Promise<ApiResponse<T>>;
12
+ }): Promise<ApiResponse<T>>;
13
13
  makeRequest(context: RequestContext): RequestOperation<Response>;
14
14
  getErrorInfo(error: Error, response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
15
15
  }
@@ -53,7 +53,6 @@ import * as Utils from "../Utils";
53
53
  import { getGlobalFetch } from "../Utils";
54
54
  import { ResponseType } from "../ApiConstants";
55
55
  import { inferResponseType } from "../ApiUtils";
56
- import { convertToRequestError, RequestErrorCode } from "../RequestError";
57
56
  var FetchError = /** @class */ (function (_super) {
58
57
  __extends(FetchError, _super);
59
58
  function FetchError() {
@@ -81,58 +80,47 @@ var FetchRequestBackend = /** @class */ (function () {
81
80
  });
82
81
  });
83
82
  };
84
- FetchRequestBackend.prototype.convertResponse = function (context, response, error) {
83
+ FetchRequestBackend.prototype.convertResponse = function (context, response) {
85
84
  return __awaiter(this, void 0, void 0, function () {
86
- var data, inferredResponseType, responseType, _a, error_1, status;
85
+ var contentType, responseType, _a, data, status, headers, error_1;
87
86
  return __generator(this, function (_b) {
88
87
  switch (_b.label) {
89
88
  case 0:
90
- inferredResponseType = inferResponseType(response.headers.get("Content-Type"));
91
- responseType = error ? inferredResponseType : context.responseType;
92
- // expand to array buffer once we support that in inferResponseType
93
- if (inferredResponseType === "text" && context.responseType === "json") {
94
- throw convertToRequestError({
95
- error: new Error("[api-def] Expected '" + context.responseType + "' response, got '" + inferredResponseType + "'"),
96
- code: RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
97
- });
98
- }
99
- _b.label = 1;
100
- case 1:
101
- _b.trys.push([1, 8, , 9]);
102
- if (!!response.__text) return [3 /*break*/, 3];
89
+ contentType = response.headers.get("Content-Type");
90
+ responseType = inferResponseType(contentType);
91
+ if (!!response.__text) return [3 /*break*/, 2];
103
92
  _a = response;
104
93
  return [4 /*yield*/, response.clone().text()];
105
- case 2:
94
+ case 1:
106
95
  _a.__text = _b.sent();
96
+ _b.label = 2;
97
+ case 2:
98
+ data = response.__text;
99
+ status = response.status, headers = response.headers;
107
100
  _b.label = 3;
108
101
  case 3:
109
- if (!(responseType === ResponseType.Text)) return [3 /*break*/, 4];
110
- data = response.__text;
111
- return [3 /*break*/, 7];
112
- case 4:
113
- if (!(responseType === ResponseType.ArrayBuffer)) return [3 /*break*/, 6];
102
+ _b.trys.push([3, 7, , 8]);
103
+ if (!(responseType === ResponseType.ArrayBuffer)) return [3 /*break*/, 5];
114
104
  return [4 /*yield*/, response.clone().arrayBuffer()];
115
- case 5:
105
+ case 4:
116
106
  data = _b.sent();
117
- return [3 /*break*/, 7];
118
- case 6:
107
+ return [3 /*break*/, 6];
108
+ case 5:
119
109
  if (responseType === ResponseType.Json) {
120
110
  data = JSON.parse(response.__text);
121
111
  }
122
- _b.label = 7;
123
- case 7: return [3 /*break*/, 9];
124
- case 8:
112
+ _b.label = 6;
113
+ case 6: return [3 /*break*/, 8];
114
+ case 7:
125
115
  error_1 = _b.sent();
126
116
  throw Object.assign(new Error("[api-def] Invalid '" + context.responseType + "' response, got: '" + response.__text + "'"), {
127
117
  response: response,
128
118
  });
129
- case 9:
130
- status = response.status;
131
- return [2 /*return*/, {
132
- data: data,
133
- status: status,
134
- headers: response.headers,
135
- }];
119
+ case 8: return [2 /*return*/, {
120
+ data: data,
121
+ status: status,
122
+ headers: headers,
123
+ }];
136
124
  }
137
125
  });
138
126
  });
@@ -155,10 +143,15 @@ var FetchRequestBackend = /** @class */ (function () {
155
143
  }
156
144
  var url = new URL(path, origin);
157
145
  if (computedConfig.query) {
158
- var queryKeys = Object.keys(computedConfig.query);
159
- for (var i = 0; i < queryKeys.length; i++) {
160
- var key = queryKeys[i];
161
- url.searchParams.append(key, ((_a = computedConfig.query[key]) === null || _a === void 0 ? void 0 : _a.toString()) || "");
146
+ if (context.computedConfig.queryParser) {
147
+ url.search = context.computedConfig.queryParser(computedConfig.query);
148
+ }
149
+ else {
150
+ var queryKeys = Object.keys(computedConfig.query);
151
+ for (var i = 0; i < queryKeys.length; i++) {
152
+ var key = queryKeys[i];
153
+ url.searchParams.append(key, ((_a = computedConfig.query[key]) === null || _a === void 0 ? void 0 : _a.toString()) || "");
154
+ }
162
155
  }
163
156
  }
164
157
  // abort controller is a newer feature than fetch
@@ -201,7 +194,6 @@ var FetchRequestBackend = /** @class */ (function () {
201
194
  canceler: abortSignal
202
195
  ? function () { return !responded && abortController.abort(); }
203
196
  : function () {
204
- console.warn("Request aborted");
205
197
  softAbort = true;
206
198
  },
207
199
  };
@@ -10,7 +10,7 @@ export interface RequestBackendErrorInfo {
10
10
  export default interface RequestBackend<R = any> {
11
11
  readonly id: string;
12
12
  makeRequest(context: RequestContext): RequestOperation<R>;
13
- convertResponse<T>(context: RequestContext, response: R, error?: boolean): Promise<ApiResponse<T>>;
13
+ convertResponse<T>(context: RequestContext, response: R): Promise<ApiResponse<T>>;
14
14
  extractResponseFromError(error: Error): Promise<R | null | undefined>;
15
15
  getErrorInfo(error: Error, response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
16
16
  }
package/esm/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./Api";
2
2
  export * from "./ApiTypes";
3
+ export * from "./UtilTypes";
3
4
  export * from "./ApiConstants";
4
5
  export { isRequestError } from "./RequestError";
5
6
  export { clearCache, setCacheBackend } from "./cache/Caching";
package/esm/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  "use strict";
3
3
  export * from "./Api";
4
4
  export * from "./ApiTypes";
5
+ export * from "./UtilTypes";
5
6
  export * from "./ApiConstants";
6
7
  export { isRequestError } from "./RequestError";
7
8
  export { clearCache, setCacheBackend } from "./cache/Caching";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-def",
3
- "version": "0.6.0-alpha1",
3
+ "version": "0.6.0-alpha12",
4
4
  "description": "Typed API definitions with middleware support",
5
5
  "main": "cjs/index.js",
6
6
  "types": "esm/index.d.ts",
package/CHANGELOG.md DELETED
@@ -1,65 +0,0 @@
1
- # 0.6.0
2
-
3
- ## Breaking Changes
4
-
5
- - axios will enforce ResponseType
6
-
7
- ### Fixes
8
-
9
- - absolute paths as `baseUrl` is now supported in fetch backend
10
-
11
- # 0.5.0
12
-
13
- ## Breaking Changes
14
-
15
- - Undocumented mocking API has been overhauled
16
-
17
- ## Changes
18
-
19
- - Add mocking!
20
- - Restructured mocking so that mocks are defined on the endpoint level (documentation updated)
21
- - Add `acceptableStatus` to specify which status codes are considered successful
22
- - Extend retry logic to use exponential back-off, rather than retrying immediately
23
- - Support for additional hot-request methods:
24
- - PUT, DELETE
25
-
26
- # 0.4.1
27
-
28
- - fix fetch backend not working with unbound fetch
29
- - add better error handling on parse
30
-
31
- # 0.4.0
32
-
33
- ## Breaking Changes
34
-
35
- - Move config values from options object up one layer
36
- - In config `retries` -> `retry`
37
- - `defaults` -> `config`
38
-
39
- ## Changes
40
-
41
- - Remove need for enum imports
42
- - Make `name` and `description` optional in endpoint definition
43
-
44
- # 0.3.11
45
-
46
- ## Changes
47
-
48
- - Make fetch backend default if fetch is present
49
- - Fix fetch backends text response type support
50
-
51
- # 0.3.0
52
-
53
- ## Features
54
-
55
- - allow defaults to be a function on api
56
- - add content type header in fetch backend to match axios backend
57
- - remove `flatted`
58
- - polyfill object.assign for ie
59
- - change retry logic so that if a middleware event responds with Retry we always attempt a retry
60
-
61
- # 0.2.5
62
-
63
- ## Changes
64
-
65
- - Remove `window` usages to allow node support