api-def 0.6.0-alpha9 → 0.6.2-alpha.1

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.
Files changed (45) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +34 -34
  3. package/cjs/Api.d.ts +1 -0
  4. package/cjs/Api.js +2 -1
  5. package/cjs/ApiConstants.d.ts +1 -0
  6. package/cjs/ApiConstants.js +1 -0
  7. package/cjs/ApiUtils.d.ts +2 -3
  8. package/cjs/ApiUtils.js +13 -21
  9. package/cjs/Endpoint.js +4 -4
  10. package/cjs/MockingTypes.d.ts +1 -0
  11. package/cjs/RequestContext.d.ts +3 -0
  12. package/cjs/RequestContext.js +29 -2
  13. package/cjs/RequestError.js +4 -1
  14. package/cjs/Requester.js +52 -20
  15. package/cjs/backend/AxiosRequestBackend.d.ts +3 -3
  16. package/cjs/backend/AxiosRequestBackend.js +8 -15
  17. package/cjs/backend/FetchRequestBackend.d.ts +3 -3
  18. package/cjs/backend/FetchRequestBackend.js +27 -39
  19. package/cjs/backend/MockRequestBackend.js +15 -7
  20. package/cjs/backend/RequestBackend.d.ts +4 -1
  21. package/cjs/cache/Caching.d.ts +1 -1
  22. package/cjs/index.js +5 -1
  23. package/cjs/middleware/LoggingMiddleware.js +7 -7
  24. package/esm/Api.d.ts +1 -0
  25. package/esm/Api.js +2 -1
  26. package/esm/ApiConstants.d.ts +1 -0
  27. package/esm/ApiConstants.js +1 -0
  28. package/esm/ApiUtils.d.ts +2 -3
  29. package/esm/ApiUtils.js +12 -19
  30. package/esm/Endpoint.js +4 -4
  31. package/esm/MockingTypes.d.ts +1 -0
  32. package/esm/RequestContext.d.ts +3 -0
  33. package/esm/RequestContext.js +29 -2
  34. package/esm/RequestError.js +4 -1
  35. package/esm/Requester.js +46 -14
  36. package/esm/backend/AxiosRequestBackend.d.ts +3 -3
  37. package/esm/backend/AxiosRequestBackend.js +7 -14
  38. package/esm/backend/FetchRequestBackend.d.ts +3 -3
  39. package/esm/backend/FetchRequestBackend.js +26 -38
  40. package/esm/backend/MockRequestBackend.js +11 -3
  41. package/esm/backend/RequestBackend.d.ts +4 -1
  42. package/esm/cache/Caching.d.ts +1 -1
  43. package/esm/middleware/LoggingMiddleware.js +7 -7
  44. package/package.json +17 -5
  45. package/CHANGELOG.md +0 -71
@@ -1,13 +1,13 @@
1
- import RequestBackend, { RequestBackendErrorInfo, RequestOperation } from "./RequestBackend";
1
+ import RequestBackend, { ConvertedApiResponse, RequestBackendErrorInfo, RequestOperation } from "./RequestBackend";
2
2
  import { ApiResponse } from "../ApiTypes";
3
3
  import type { AxiosError, AxiosResponse } from "axios";
4
4
  import RequestContext from "../RequestContext";
5
- export declare const isAxiosError: (error: Error) => error is AxiosError<any>;
5
+ export declare const isAxiosError: (error: Error) => error is AxiosError<unknown, any>;
6
6
  export default class AxiosRequestBackend implements RequestBackend<AxiosResponse> {
7
7
  readonly id = "axios";
8
8
  constructor(axiosLibrary: any);
9
9
  extractResponseFromError(error: Error): Promise<AxiosResponse | null | undefined>;
10
- convertResponse<T>(context: RequestContext, response: AxiosResponse): Promise<ApiResponse<T>>;
10
+ convertResponse<T>(context: RequestContext, response: AxiosResponse): Promise<ConvertedApiResponse<T>>;
11
11
  makeRequest(context: RequestContext): RequestOperation<AxiosResponse>;
12
12
  getErrorInfo(error: Error, response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
13
13
  }
@@ -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,18 +56,13 @@ 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 contentType, inferredResponseType;
62
59
  return __generator(this, function (_a) {
63
- contentType = response.headers["content-type"];
64
- inferredResponseType = inferResponseType(contentType);
65
- // expand to array buffer once we support that in inferResponseType
66
- if (inferredResponseType === "text" && context.responseType === "json") {
67
- throw convertToRequestError({
68
- error: new Error("[api-def] Expected '" + context.responseType + "' response, got '" + inferredResponseType + "' (from 'Content-Type' of '" + contentType + "')"),
69
- code: RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
70
- });
71
- }
72
- return [2 /*return*/, response];
60
+ return [2 /*return*/, {
61
+ data: response.data,
62
+ headers: response.headers,
63
+ status: response.status,
64
+ __lowercaseHeaders: response._lowerCaseResponseHeaders,
65
+ }];
73
66
  });
74
67
  });
75
68
  };
@@ -80,7 +73,7 @@ var AxiosRequestBackend = /** @class */ (function () {
80
73
  method: context.method,
81
74
  baseURL: context.baseUrl,
82
75
  url: context.computedPath,
83
- data: computedConfig.body || {},
76
+ data: context.getParsedBody() || {},
84
77
  params: computedConfig.query || {},
85
78
  headers: computedConfig.headers || {},
86
79
  responseType: context.responseType,
@@ -1,15 +1,15 @@
1
- import RequestBackend, { RequestBackendErrorInfo, RequestOperation } from "./RequestBackend";
1
+ import RequestBackend, { ConvertedApiResponse, RequestBackendErrorInfo, RequestOperation } from "./RequestBackend";
2
2
  import { ApiResponse } from "../ApiTypes";
3
3
  import RequestContext from "../RequestContext";
4
4
  import { Fetch } from "../Utils";
5
5
  export default class FetchRequestBackend implements RequestBackend<Response> {
6
- fetch: (((input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch) | undefined;
6
+ fetch: (((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>) & typeof fetch) | undefined;
7
7
  readonly id = "fetch";
8
8
  constructor(fetchLibrary?: Fetch);
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<ConvertedApiResponse<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,59 +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, contentType, 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
89
  contentType = response.headers.get("Content-Type");
91
- inferredResponseType = inferResponseType(contentType);
92
- responseType = error ? inferredResponseType : context.responseType;
93
- // expand to array buffer once we support that in inferResponseType
94
- if (inferredResponseType === "text" && context.responseType === "json") {
95
- throw convertToRequestError({
96
- error: new Error("[api-def] Expected '" + context.responseType + "' response, got '" + inferredResponseType + "' (from 'Content-Type' of '" + contentType + "')"),
97
- code: RequestErrorCode.REQUEST_MISMATCH_RESPONSE_TYPE,
98
- });
99
- }
100
- _b.label = 1;
101
- case 1:
102
- _b.trys.push([1, 8, , 9]);
103
- if (!!response.__text) return [3 /*break*/, 3];
90
+ responseType = inferResponseType(contentType);
91
+ if (!!response.__text) return [3 /*break*/, 2];
104
92
  _a = response;
105
93
  return [4 /*yield*/, response.clone().text()];
106
- case 2:
94
+ case 1:
107
95
  _a.__text = _b.sent();
96
+ _b.label = 2;
97
+ case 2:
98
+ data = response.__text;
99
+ status = response.status, headers = response.headers;
108
100
  _b.label = 3;
109
101
  case 3:
110
- if (!(responseType === ResponseType.Text)) return [3 /*break*/, 4];
111
- data = response.__text;
112
- return [3 /*break*/, 7];
113
- case 4:
114
- if (!(responseType === ResponseType.ArrayBuffer)) return [3 /*break*/, 6];
102
+ _b.trys.push([3, 7, , 8]);
103
+ if (!(responseType === ResponseType.ArrayBuffer)) return [3 /*break*/, 5];
115
104
  return [4 /*yield*/, response.clone().arrayBuffer()];
116
- case 5:
105
+ case 4:
117
106
  data = _b.sent();
118
- return [3 /*break*/, 7];
119
- case 6:
107
+ return [3 /*break*/, 6];
108
+ case 5:
120
109
  if (responseType === ResponseType.Json) {
121
110
  data = JSON.parse(response.__text);
122
111
  }
123
- _b.label = 7;
124
- case 7: return [3 /*break*/, 9];
125
- case 8:
112
+ _b.label = 6;
113
+ case 6: return [3 /*break*/, 8];
114
+ case 7:
126
115
  error_1 = _b.sent();
127
- throw Object.assign(new Error("[api-def] Invalid '" + context.responseType + "' response, got: '" + response.__text + "'"), {
116
+ throw Object.assign(new Error("[api-def] Invalid '".concat(context.responseType, "' response, got: '").concat(response.__text, "'")), {
128
117
  response: response,
129
118
  });
130
- case 9:
131
- status = response.status;
132
- return [2 /*return*/, {
133
- data: data,
134
- status: status,
135
- headers: response.headers,
136
- }];
119
+ case 8: return [2 /*return*/, {
120
+ data: data,
121
+ status: status,
122
+ headers: headers,
123
+ }];
137
124
  }
138
125
  });
139
126
  });
@@ -172,7 +159,8 @@ var FetchRequestBackend = /** @class */ (function () {
172
159
  var abortSignal = abortController ? abortController.signal : undefined;
173
160
  var softAbort = false;
174
161
  var responded = false;
175
- var bodyJsonify = computedConfig.body !== null && typeof computedConfig.body === "object";
162
+ var body = context.getParsedBody();
163
+ var bodyJsonify = body !== null && typeof body === "object";
176
164
  var headers = Utils.assign({
177
165
  // logic from axios
178
166
  "Content-Type": bodyJsonify ? "application/json;charset=utf-8" : "application/x-www-form-urlencoded",
@@ -186,7 +174,7 @@ var FetchRequestBackend = /** @class */ (function () {
186
174
  }, {});
187
175
  var promise = this.fetch(url.href, {
188
176
  method: context.method,
189
- body: bodyJsonify ? JSON.stringify(computedConfig.body) : computedConfig.body,
177
+ body: bodyJsonify ? JSON.stringify(body) : body,
190
178
  headers: parsedHeaders,
191
179
  mode: "cors",
192
180
  signal: abortSignal,
@@ -63,7 +63,7 @@ var MockRequestBackend = /** @class */ (function () {
63
63
  MockRequestBackend.prototype.runRequest = function (context) {
64
64
  var _a, _b, _c, _d;
65
65
  return __awaiter(this, void 0, void 0, function () {
66
- var mockingFunc, req, res, delay, delayMs, min, max, _e;
66
+ var mockingFunc, req, res, delay, delayMs, min, max, _e, parsedHeaders;
67
67
  return __generator(this, function (_f) {
68
68
  switch (_f.label) {
69
69
  case 0:
@@ -75,13 +75,14 @@ var MockRequestBackend = /** @class */ (function () {
75
75
  });
76
76
  }
77
77
  req = {
78
- body: context.computedConfig.body,
78
+ body: context.getParsedBody(),
79
79
  params: (_b = context.computedConfig.params) !== null && _b !== void 0 ? _b : {},
80
80
  query: context.computedConfig.query,
81
81
  headers: (_c = context.computedConfig.headers) !== null && _c !== void 0 ? _c : {},
82
82
  };
83
83
  res = {
84
84
  statusCode: -1,
85
+ headers: {},
85
86
  response: undefined,
86
87
  status: function (statusCode) {
87
88
  res.statusCode = statusCode;
@@ -89,6 +90,9 @@ var MockRequestBackend = /** @class */ (function () {
89
90
  },
90
91
  send: function (response) {
91
92
  res.response = response;
93
+ if (response && typeof response === "object") {
94
+ res.headers["Content-Type"] = "application/json";
95
+ }
92
96
  return res;
93
97
  },
94
98
  };
@@ -125,8 +129,12 @@ var MockRequestBackend = /** @class */ (function () {
125
129
  code: RequestErrorCode.REQUEST_INVALID_CONFIG,
126
130
  });
127
131
  }
132
+ parsedHeaders = Object.keys(res.headers).reduce(function (parsedHeaders, key) {
133
+ parsedHeaders[key] = res.headers[key].toString();
134
+ return parsedHeaders;
135
+ }, {});
128
136
  return [2 /*return*/, {
129
- headers: {},
137
+ headers: parsedHeaders,
130
138
  data: res.response,
131
139
  status: res.statusCode,
132
140
  }];
@@ -7,10 +7,13 @@ export interface RequestOperation<R> {
7
7
  export interface RequestBackendErrorInfo {
8
8
  code: string;
9
9
  }
10
+ export declare type ConvertedApiResponse<T> = ApiResponse<T> & {
11
+ __lowercaseHeaders?: any;
12
+ };
10
13
  export default interface RequestBackend<R = any> {
11
14
  readonly id: string;
12
15
  makeRequest(context: RequestContext): RequestOperation<R>;
13
- convertResponse<T>(context: RequestContext, response: R, error?: boolean): Promise<ApiResponse<T>>;
16
+ convertResponse<T>(context: RequestContext, response: R): Promise<ConvertedApiResponse<T>>;
14
17
  extractResponseFromError(error: Error): Promise<R | null | undefined>;
15
18
  getErrorInfo(error: Error, response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
16
19
  }
@@ -6,5 +6,5 @@ export interface CacheEntry {
6
6
  data: any;
7
7
  expiry: number | null;
8
8
  }
9
- export declare const setCachedItem: <T>(key: string, value: T, expiry?: number | undefined) => Promise<T>;
9
+ export declare const setCachedItem: <T>(key: string, value: T, expiry?: number) => Promise<T>;
10
10
  export declare const getCachedItem: <T = any>(key: string) => Promise<T | undefined>;
@@ -28,12 +28,12 @@ var diagnoseError = function (error) {
28
28
  var _a = error.response, status = _a.status, data = _a.data;
29
29
  var code = data === null || data === void 0 ? void 0 : data.code;
30
30
  return {
31
- message: "responded with " + status + (code ? " (" + code + ")" : ""),
31
+ message: "responded with ".concat(status).concat(code ? " (".concat(code, ")") : ""),
32
32
  response: error.response,
33
33
  };
34
34
  };
35
35
  var formatTime = function (time) {
36
- return Utils.padNumber(time.getHours(), 2) + ":" + Utils.padNumber(time.getMinutes(), 2) + ":" + Utils.padNumber(time.getSeconds(), 2) + "." + Utils.padNumber(time.getMilliseconds(), 3);
36
+ return "".concat(Utils.padNumber(time.getHours(), 2), ":").concat(Utils.padNumber(time.getMinutes(), 2), ":").concat(Utils.padNumber(time.getSeconds(), 2), ".").concat(Utils.padNumber(time.getMilliseconds(), 3));
37
37
  };
38
38
  var log = function (context, type, message, config, objects) {
39
39
  if (typeof config.predicate === "function" && !config.predicate()) {
@@ -43,10 +43,10 @@ var log = function (context, type, message, config, objects) {
43
43
  var color = COLOR_MAP[type];
44
44
  var timestamp = formatTime(new Date());
45
45
  var args = [
46
- "%cnetwork %c[" + context.api.name + "] " + context.method.toUpperCase() + " " + computedPath + " %c" + message + " %c@ " + timestamp,
46
+ "%cnetwork %c[".concat(context.api.name, "] ").concat(context.method.toUpperCase(), " ").concat(computedPath, " %c").concat(message, " %c@ ").concat(timestamp),
47
47
  "color:gray",
48
48
  "color:auto",
49
- "color:" + color,
49
+ "color:".concat(color),
50
50
  "color:gray",
51
51
  ];
52
52
  /* eslint-disable-next-line no-console */
@@ -66,18 +66,18 @@ var LoggingMiddleware = function (config) {
66
66
  _a[RequestEvent.Success] = function (context) {
67
67
  var _a;
68
68
  var cacheSource = context.cacheInfo.source;
69
- log(context, LogType.Success, "responded with " + ((_a = context.response) === null || _a === void 0 ? void 0 : _a.status) + (cacheSource ? " (cached by " + cacheSource + ")" : ""), config);
69
+ log(context, LogType.Success, "responded with ".concat((_a = context.response) === null || _a === void 0 ? void 0 : _a.status).concat(cacheSource ? " (cached by ".concat(cacheSource, ")") : ""), config);
70
70
  },
71
71
  _a[RequestEvent.Error] = function (context) {
72
72
  if (context.error) {
73
73
  var _a = diagnoseError(context.error), error = _a.error, message = _a.message;
74
- log(context, LogType.Warn, "error on attempt " + context.stats.attempt + " - " + message, config, { error: error });
74
+ log(context, LogType.Warn, "error on attempt ".concat(context.stats.attempt, " - ").concat(message), config, { error: error });
75
75
  }
76
76
  },
77
77
  _a[RequestEvent.UnrecoverableError] = function (context) {
78
78
  if (context.error) {
79
79
  var _a = diagnoseError(context.error), error = _a.error, message = _a.message;
80
- log(context, LogType.Error, "failed - " + message, config, {
80
+ log(context, LogType.Error, "failed - ".concat(message), config, {
81
81
  error: error,
82
82
  });
83
83
  }
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "api-def",
3
- "version": "0.6.0-alpha9",
3
+ "version": "0.6.2-alpha.1",
4
4
  "description": "Typed API definitions with middleware support",
5
5
  "main": "cjs/index.js",
6
6
  "types": "esm/index.d.ts",
7
7
  "module": "esm/index.js",
8
8
  "sideEffects": false,
9
9
  "scripts": {
10
+ "prepublishOnly": "npm run test && npm run build",
10
11
  "test": "npm run test:types && npm run test:lint && npm run test:unit",
11
- "test:unit": "cross-env NODE_ENV=test jest",
12
+ "test:unit": "ava",
12
13
  "test:lint": "npm run lint:strict",
13
14
  "test:types": "tsc --noEmit -p .",
14
15
  "example:start": "cd example && npm run start",
@@ -45,19 +46,30 @@
45
46
  "cjs/"
46
47
  ],
47
48
  "repository": "https://github.com/Censkh/api-def",
49
+ "ava": {
50
+ "extensions": [
51
+ "ts"
52
+ ],
53
+ "files": [
54
+ "src/tests/**/*.test.ts"
55
+ ],
56
+ "nodeArguments": [
57
+ "--require=@esbuild-kit/cjs-loader"
58
+ ]
59
+ },
48
60
  "devDependencies": {
49
61
  "@babel/preset-env": "7.14.8",
50
62
  "@babel/preset-typescript": "7.14.5",
63
+ "@esbuild-kit/cjs-loader": "2.4.0",
51
64
  "@types/axios": "0.14.0",
52
- "@types/jest": "26.0.24",
53
65
  "@types/node": "16.4.6",
54
66
  "@typescript-eslint/eslint-plugin": "4.28.5",
55
67
  "@typescript-eslint/parser": "4.28.5",
68
+ "ava": "5.0.1",
56
69
  "cross-env": "7.0.3",
57
70
  "eslint": "7.31.0",
58
71
  "eslint-watch": "7.0.0",
59
- "jest": "27.0.6",
60
72
  "npm-run-all": "4.1.5",
61
- "typescript": "4.3.5"
73
+ "typescript": "4.8.4"
62
74
  }
63
75
  }
package/CHANGELOG.md DELETED
@@ -1,71 +0,0 @@
1
- # 0.6.0
2
-
3
- ## Breaking Changes
4
-
5
- - axios will enforce `ResponseType`
6
-
7
- ## Features
8
-
9
- - add `ResponseOf`, `QueryOf`, `BodyOf`, `ParamsOf` to quickly get types from endpoints
10
- - new `queryParser` config option to allow custom query string parsing
11
-
12
- ## Fixes
13
-
14
- - absolute paths as `baseUrl` is now supported in fetch backend
15
- - don't re-use same requests based upon URL to allow for concurrent POST requests
16
-
17
- # 0.5.0
18
-
19
- ## Breaking Changes
20
-
21
- - Undocumented mocking API has been overhauled
22
-
23
- ## Changes
24
-
25
- - Add mocking!
26
- - Restructured mocking so that mocks are defined on the endpoint level (documentation updated)
27
- - Add `acceptableStatus` to specify which status codes are considered successful
28
- - Extend retry logic to use exponential back-off, rather than retrying immediately
29
- - Support for additional hot-request methods:
30
- - PUT, DELETE
31
-
32
- # 0.4.1
33
-
34
- - fix fetch backend not working with unbound fetch
35
- - add better error handling on parse
36
-
37
- # 0.4.0
38
-
39
- ## Breaking Changes
40
-
41
- - Move config values from options object up one layer
42
- - In config `retries` -> `retry`
43
- - `defaults` -> `config`
44
-
45
- ## Changes
46
-
47
- - Remove need for enum imports
48
- - Make `name` and `description` optional in endpoint definition
49
-
50
- # 0.3.11
51
-
52
- ## Changes
53
-
54
- - Make fetch backend default if fetch is present
55
- - Fix fetch backends text response type support
56
-
57
- # 0.3.0
58
-
59
- ## Features
60
-
61
- - allow defaults to be a function on api
62
- - add content type header in fetch backend to match axios backend
63
- - remove `flatted`
64
- - polyfill object.assign for ie
65
- - change retry logic so that if a middleware event responds with Retry we always attempt a retry
66
-
67
- # 0.2.5
68
-
69
- ## Changes
70
-
71
- - Remove `window` usages to allow node support