keq 2.2.0 → 2.3.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [2.3.1](https://github.com/keq-request/keq/compare/v2.3.0...v2.3.1) (2024-03-22)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * unabled send formDate request with buffer file ([9db1508](https://github.com/keq-request/keq/commit/9db1508c3366a5dd264e3de87bd8afef777f761f))
11
+
12
+ ## [2.3.0](https://github.com/keq-request/keq/compare/v2.2.0...v2.3.0) (2024-02-24)
13
+
14
+
15
+ ### Features
16
+
17
+ * add .resolveWith method add deprecated resolveWithFullResponse ([1e01e7f](https://github.com/keq-request/keq/commit/1e01e7fe07c146d9f122f0f52778b45258797c68))
18
+
5
19
  ## [2.2.0](https://github.com/keq-request/keq/compare/v2.1.2...v2.2.0) (2024-02-04)
6
20
 
7
21
 
package/README.md CHANGED
@@ -82,20 +82,22 @@ await request.del("https://example.com/search");
82
82
  > `.del()` is the alias of `.delete()`.
83
83
 
84
84
  `Keq` will parse `body` according to the `Content-Type` of [`Response`][Response MDN]
85
- and return `body` of [`Response`][Response MDN] by defaulted.
86
- Add option `resolveWithFullResponse` to get the origin [`Response`][Response MDN] Object.
85
+ and return `undefined` if `Content-Type` not found.
86
+ Add invoke `.resolveWith('response')` to get the origin [`Response`][Response MDN] Object.
87
87
 
88
88
  ```javascript
89
89
  import { request } from "keq";
90
90
 
91
91
  const response = await request
92
92
  .get("http://test.com")
93
- .option("resolveWithFullResponse");
93
+ .resolve('response')
94
94
 
95
95
  const body = await response.json();
96
96
  ```
97
97
 
98
- ###### `Keq` won't auto parse body, if response.status is 204. The HTTP 204 No Content success status response code indicates that server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation
98
+ We will introduce `resolveWith` in more detail later.
99
+
100
+ ###### `Keq` won't auto parse body, if response.status is 204. The HTTP 204 No Content success status response code indicates that server has fulfilled the request but does not need to return an entity-body, and might want to return updated meta information
99
101
 
100
102
  ### Setting header fields
101
103
 
@@ -269,6 +271,22 @@ await request
269
271
  | jpeg, bmp, apng, gif, x-icon, png, webp, tiff | image/jpeg, image/bmp, image/apng, image/gif, image/x-icon, image/png, image/webp, image/tiff |
270
272
  | svg | image/svg+xml |
271
273
 
274
+ ### resolve responseBody
275
+
276
+ It was mentioned before that `Keq` will automatically parses the response body.
277
+ And we can control the parsing behavior by calling `.resolveWith(method)`.
278
+ There are multiple parsing methods for us to choose from
279
+
280
+ | method | description |
281
+ | :--------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
282
+ | `.resolveWith('intelligent')` | It is the default method of `Keq`. This will returned `context.output` first if it exists. Otherwise return undefined when the response status is 204. Or return parsed response body according to the `Content-Type` of [`Response`][Response MDN]. |
283
+ | `.resolveWith('response')` | Return [`Response`][Response MDN]. |
284
+ | `.resolveWith('text')` | Return `response.text()`. |
285
+ | `.resolveWith('json')` | Return `response.json()`. |
286
+ | `.resolveWith('form-data')` | Return `response.formData()`. |
287
+ | `.resolveWith('blob')` | Return `response.blob()`. |
288
+ | `.resolveWith('array-buffer')` | Return `response.arrayBuffer()` |
289
+
272
290
  ### Request Retry
273
291
 
274
292
  No retry by default, invoke `.retry(retryTimes[, retryDelay[, retryOn]])` to set retry parameters
@@ -328,11 +346,6 @@ import { request } from "keq";
328
346
 
329
347
  const response = await request
330
348
  .get("http://test.com")
331
- /**
332
- * keq will return Response rather than parsed body
333
- * when set resolveWithFullResponse
334
- */
335
- .option("resolveWithFullResponse")
336
349
  .option("middlewareOption", "value");
337
350
  ```
338
351
 
@@ -344,14 +357,12 @@ import { request } from "keq";
344
357
  await request
345
358
  .get("http://test.com")
346
359
  .options({
347
- resolveWithFullResponse: true,
348
360
  middlewareOption: "value",
349
361
  });
350
362
  ```
351
363
 
352
364
  | **Option** | **Description** |
353
365
  | :------------------------ | :------------------------------------------------------------------------------------------------------ |
354
- | `resolveWithFullResponse` | Get the [`Response`][Response MDN] Class. This is the `.clone()` of original [`Response`][Response MDN] |
355
366
  | `fetchAPI` | Replace the defaulted `fetch` function used by `Keq`. |
356
367
 
357
368
  <!-- ###### The options with **DEPRECATED** will be removed in next major version -->
@@ -431,7 +442,7 @@ const middleware = async (context, next) => {
431
442
  const body = await response.json()
432
443
 
433
444
  // custom keq return type
434
- response.output = JSON.stringify(body)
445
+ context.output = JSON.stringify(body)
435
446
  }
436
447
 
437
448
  // Global Middleware
@@ -510,10 +521,10 @@ Keq's context object has many parameters. The following lists all the built-in c
510
521
  | `context.request.referrer` | `referrer` arguments in [Fetch API][Fetch MDN] |
511
522
  | `context.request.referrerPolicy` | `referrerPolicy` arguments in [Fetch API][Fetch MDN] |
512
523
  | `context.request.signal` | `signal` arguments in [Fetch API][Fetch MDN] |
513
- | `context.options` | It is an object includes request options.(example: `context.options.resolveWithFullResponse`). Middleware can get custom options from here. |
524
+ | `context.options` | It is an object includes request options.(example: `context.options.fetchAPI`). Middleware can get custom options from here. |
514
525
  | `context.res` | The origin [`Response`][Response MDN] Class. It will be undefined before run `await next()` or error throwed. |
515
526
  | `context.response` | Cloned from `ctx.res`. |
516
- | `context.output` | The return value of `await request()`. By defaulted, `context.output` is the parsed body of response. `context.output` will be the `ctx.response` When `options.resolveWithFullResponse` is true. **This property is writeonly.** |
527
+ | `context.output` | Custom return value of `await request()`。 It only take effect when `resolveWith` is not set or set to 'intelligent'. **This property is writeonly.** |
517
528
 
518
529
  #### .useRouter()
519
530
 
@@ -13,7 +13,10 @@ export class Core {
13
13
  __global__;
14
14
  __prepend_middlewares__ = [];
15
15
  __append_middlewares__ = [];
16
- __options__ = { resolveWithFullResponse: false };
16
+ __options__ = {
17
+ resolveWithFullResponse: false,
18
+ resolveWith: 'intelligent',
19
+ };
17
20
  constructor(url, init, global = {}) {
18
21
  this.__global__ = global;
19
22
  this.requestContext = {
@@ -69,39 +72,58 @@ export class Core {
69
72
  const middleware = composeMiddleware([...this.__prepend_middlewares__, ...this.__append_middlewares__]);
70
73
  // eslint-disable-next-line @typescript-eslint/no-empty-function
71
74
  await middleware(ctx, async () => { });
72
- let output = ctx[OUTPUT_PROPERTY];
73
- if (ctx.options.resolveWithFullResponse) {
75
+ const output = ctx[OUTPUT_PROPERTY];
76
+ if (ctx.options.resolveWithFullResponse || ctx.options.resolveWith === 'response') {
74
77
  return ctx.response;
75
78
  }
76
- if (!(OUTPUT_PROPERTY in ctx)) {
77
- const response = ctx.response;
78
- if (response?.status === 204) {
79
- // 204: NO CONTENT
80
- output = response && response.body;
79
+ const response = ctx.response;
80
+ if (!response) {
81
+ return (OUTPUT_PROPERTY in ctx) ? output : undefined;
82
+ }
83
+ if (ctx.options.resolveWith === 'text') {
84
+ return await response.text();
85
+ }
86
+ else if (ctx.options.resolveWith === 'json') {
87
+ return await response.json();
88
+ }
89
+ else if (ctx.options.resolveWith === 'form-data') {
90
+ return await response.formData();
91
+ }
92
+ else if (ctx.options.resolveWith === 'blob') {
93
+ return await response.blob();
94
+ }
95
+ else if (ctx.options.resolveWith === 'array-buffer') {
96
+ return await response.arrayBuffer();
97
+ }
98
+ if (OUTPUT_PROPERTY in ctx) {
99
+ return output;
100
+ }
101
+ if (response.status === 204) {
102
+ // 204: NO CONTENT
103
+ return undefined;
104
+ }
105
+ const contentType = response.headers.get('content-type') || '';
106
+ try {
107
+ if (contentType.includes('application/json')) {
108
+ return await response.json();
81
109
  }
82
- else {
83
- const headers = response?.headers;
84
- const contentType = headers?.get('content-type') || '';
85
- try {
86
- if (contentType.includes('application/json')) {
87
- output = ctx.response && await ctx.response.json();
88
- }
89
- else if (contentType.includes('multipart/form-data')) {
90
- output = ctx.response && await ctx.response.formData();
91
- }
92
- else if (contentType.includes('plain/text')) {
93
- output = ctx.response && await ctx.response.text();
94
- }
95
- else {
96
- output = ctx.response && ctx.response.body;
97
- }
98
- }
99
- catch (e) {
100
- console.warn('Failed to auto parse response body', e);
101
- }
110
+ else if (contentType.includes('multipart/form-data')) {
111
+ return await response.formData();
102
112
  }
113
+ else if (contentType.includes('plain/text')) {
114
+ return await response.text();
115
+ }
116
+ }
117
+ catch (e) {
118
+ console.warn('Failed to auto parse response body', e);
103
119
  }
104
- return output;
120
+ /**
121
+ * Unable to parse response body
122
+ * Return undefined
123
+ * Enable users to discover the problem
124
+ * And modify the method of parsing response
125
+ */
126
+ return undefined;
105
127
  }
106
128
  async end() {
107
129
  return this.run();
@@ -71,4 +71,9 @@ export declare class Keq<T> extends Core<T> {
71
71
  mode(mod: RequestMode): this;
72
72
  flowControl(mode: KeqFlowControlMode, signal?: KeqFlowControlSignal): this;
73
73
  timeout(milliseconds: number): this;
74
+ resolveWith(m: 'response'): Keq<Response>;
75
+ resolveWith(m: 'array-buffer'): Keq<ArrayBuffer>;
76
+ resolveWith(m: 'blob'): Keq<Blob>;
77
+ resolveWith(m: 'text'): Keq<string>;
78
+ resolveWith<U = any>(m: 'json' | 'form-data'): Keq<U>;
74
79
  }
@@ -151,7 +151,9 @@ export class Keq extends Core {
151
151
  file = value;
152
152
  }
153
153
  else if (value instanceof Buffer) {
154
- file = new File([value], arg3);
154
+ const formData = new FormData();
155
+ formData.set(key, new Blob([value]), arg3);
156
+ file = formData.get(key);
155
157
  }
156
158
  else {
157
159
  throw new InvalidArgumentsExceptions();
@@ -200,4 +202,9 @@ export class Keq extends Core {
200
202
  this.option('timeout', { millisecond: milliseconds });
201
203
  return this;
202
204
  }
205
+ resolveWith(m) {
206
+ this.option('resolveWith', m);
207
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
208
+ return this;
209
+ }
203
210
  }
@@ -1,4 +1,5 @@
1
1
  import { KeqFlowControl } from './keq-flow-control.js';
2
+ import { KeqResolveMethod } from './keq-resolve-with-mode.js';
2
3
  import { KeqRetryDelay } from './keq-retry-delay';
3
4
  import { KeqRetryOn } from './keq-retry-on';
4
5
  import { KeqTimeout } from './keq-timeout.js';
@@ -8,8 +9,17 @@ export interface KeqBuildInOptions {
8
9
  * default use node-fetch@2 in node and window.fetch in browser
9
10
  */
10
11
  fetchAPI?: typeof fetch;
11
- /** get response object, defaulted `false` */
12
+ /**
13
+ * get response object, defaulted `false`
14
+ * @deprecated use `resolveWith` instead
15
+ * */
12
16
  resolveWithFullResponse?: boolean;
17
+ /**
18
+ * how to resolve the response body
19
+ * @description 如何解析响应体
20
+ * @default 'intelligent'
21
+ */
22
+ resolveWith?: KeqResolveMethod;
13
23
  /**
14
24
  * The request retry times
15
25
  * @description 重试次数
@@ -0,0 +1 @@
1
+ export type KeqResolveMethod = 'intelligent' | 'response' | 'text' | 'json' | 'form-data' | 'blob' | 'array-buffer';
@@ -0,0 +1 @@
1
+ export {};
@@ -25,7 +25,10 @@
25
25
  __global__;
26
26
  __prepend_middlewares__ = [];
27
27
  __append_middlewares__ = [];
28
- __options__ = { resolveWithFullResponse: false };
28
+ __options__ = {
29
+ resolveWithFullResponse: false,
30
+ resolveWith: 'intelligent',
31
+ };
29
32
  constructor(url, init, global = {}) {
30
33
  this.__global__ = global;
31
34
  this.requestContext = {
@@ -81,39 +84,58 @@
81
84
  const middleware = (0, compose_middleware_1.composeMiddleware)([...this.__prepend_middlewares__, ...this.__append_middlewares__]);
82
85
  // eslint-disable-next-line @typescript-eslint/no-empty-function
83
86
  await middleware(ctx, async () => { });
84
- let output = ctx[constant_1.OUTPUT_PROPERTY];
85
- if (ctx.options.resolveWithFullResponse) {
87
+ const output = ctx[constant_1.OUTPUT_PROPERTY];
88
+ if (ctx.options.resolveWithFullResponse || ctx.options.resolveWith === 'response') {
86
89
  return ctx.response;
87
90
  }
88
- if (!(constant_1.OUTPUT_PROPERTY in ctx)) {
89
- const response = ctx.response;
90
- if (response?.status === 204) {
91
- // 204: NO CONTENT
92
- output = response && response.body;
91
+ const response = ctx.response;
92
+ if (!response) {
93
+ return (constant_1.OUTPUT_PROPERTY in ctx) ? output : undefined;
94
+ }
95
+ if (ctx.options.resolveWith === 'text') {
96
+ return await response.text();
97
+ }
98
+ else if (ctx.options.resolveWith === 'json') {
99
+ return await response.json();
100
+ }
101
+ else if (ctx.options.resolveWith === 'form-data') {
102
+ return await response.formData();
103
+ }
104
+ else if (ctx.options.resolveWith === 'blob') {
105
+ return await response.blob();
106
+ }
107
+ else if (ctx.options.resolveWith === 'array-buffer') {
108
+ return await response.arrayBuffer();
109
+ }
110
+ if (constant_1.OUTPUT_PROPERTY in ctx) {
111
+ return output;
112
+ }
113
+ if (response.status === 204) {
114
+ // 204: NO CONTENT
115
+ return undefined;
116
+ }
117
+ const contentType = response.headers.get('content-type') || '';
118
+ try {
119
+ if (contentType.includes('application/json')) {
120
+ return await response.json();
93
121
  }
94
- else {
95
- const headers = response?.headers;
96
- const contentType = headers?.get('content-type') || '';
97
- try {
98
- if (contentType.includes('application/json')) {
99
- output = ctx.response && await ctx.response.json();
100
- }
101
- else if (contentType.includes('multipart/form-data')) {
102
- output = ctx.response && await ctx.response.formData();
103
- }
104
- else if (contentType.includes('plain/text')) {
105
- output = ctx.response && await ctx.response.text();
106
- }
107
- else {
108
- output = ctx.response && ctx.response.body;
109
- }
110
- }
111
- catch (e) {
112
- console.warn('Failed to auto parse response body', e);
113
- }
122
+ else if (contentType.includes('multipart/form-data')) {
123
+ return await response.formData();
114
124
  }
125
+ else if (contentType.includes('plain/text')) {
126
+ return await response.text();
127
+ }
128
+ }
129
+ catch (e) {
130
+ console.warn('Failed to auto parse response body', e);
115
131
  }
116
- return output;
132
+ /**
133
+ * Unable to parse response body
134
+ * Return undefined
135
+ * Enable users to discover the problem
136
+ * And modify the method of parsing response
137
+ */
138
+ return undefined;
117
139
  }
118
140
  async end() {
119
141
  return this.run();
@@ -71,4 +71,9 @@ export declare class Keq<T> extends Core<T> {
71
71
  mode(mod: RequestMode): this;
72
72
  flowControl(mode: KeqFlowControlMode, signal?: KeqFlowControlSignal): this;
73
73
  timeout(milliseconds: number): this;
74
+ resolveWith(m: 'response'): Keq<Response>;
75
+ resolveWith(m: 'array-buffer'): Keq<ArrayBuffer>;
76
+ resolveWith(m: 'blob'): Keq<Blob>;
77
+ resolveWith(m: 'text'): Keq<string>;
78
+ resolveWith<U = any>(m: 'json' | 'form-data'): Keq<U>;
74
79
  }
@@ -163,7 +163,9 @@
163
163
  file = value;
164
164
  }
165
165
  else if (value instanceof Buffer) {
166
- file = new File([value], arg3);
166
+ const formData = new FormData();
167
+ formData.set(key, new Blob([value]), arg3);
168
+ file = formData.get(key);
167
169
  }
168
170
  else {
169
171
  throw new invalid_arguments_exception_1.InvalidArgumentsExceptions();
@@ -212,6 +214,11 @@
212
214
  this.option('timeout', { millisecond: milliseconds });
213
215
  return this;
214
216
  }
217
+ resolveWith(m) {
218
+ this.option('resolveWith', m);
219
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
220
+ return this;
221
+ }
215
222
  }
216
223
  exports.Keq = Keq;
217
224
  });
@@ -1,4 +1,5 @@
1
1
  import { KeqFlowControl } from './keq-flow-control.js';
2
+ import { KeqResolveMethod } from './keq-resolve-with-mode.js';
2
3
  import { KeqRetryDelay } from './keq-retry-delay';
3
4
  import { KeqRetryOn } from './keq-retry-on';
4
5
  import { KeqTimeout } from './keq-timeout.js';
@@ -8,8 +9,17 @@ export interface KeqBuildInOptions {
8
9
  * default use node-fetch@2 in node and window.fetch in browser
9
10
  */
10
11
  fetchAPI?: typeof fetch;
11
- /** get response object, defaulted `false` */
12
+ /**
13
+ * get response object, defaulted `false`
14
+ * @deprecated use `resolveWith` instead
15
+ * */
12
16
  resolveWithFullResponse?: boolean;
17
+ /**
18
+ * how to resolve the response body
19
+ * @description 如何解析响应体
20
+ * @default 'intelligent'
21
+ */
22
+ resolveWith?: KeqResolveMethod;
13
23
  /**
14
24
  * The request retry times
15
25
  * @description 重试次数
@@ -0,0 +1 @@
1
+ export type KeqResolveMethod = 'intelligent' | 'response' | 'text' | 'json' | 'form-data' | 'blob' | 'array-buffer';
@@ -0,0 +1,12 @@
1
+ (function (factory) {
2
+ if (typeof module === "object" && typeof module.exports === "object") {
3
+ var v = factory(require, exports);
4
+ if (v !== undefined) module.exports = v;
5
+ }
6
+ else if (typeof define === "function" && define.amd) {
7
+ define(["require", "exports"], factory);
8
+ }
9
+ })(function (require, exports) {
10
+ "use strict";
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keq",
3
- "version": "2.2.0",
3
+ "version": "2.3.1",
4
4
  "description": "Request API write by Typescript for flexibility, readability, and a low learning curve.",
5
5
  "keywords": [
6
6
  "request",
@@ -33,7 +33,7 @@
33
33
  "build": "npm run clean && ./build/build.sh",
34
34
  "clean": "rm -rf ./dist/*",
35
35
  "dev": "npm run clean && ./build/watch.sh",
36
- "prepare": "ts-patch install -s && is-ci || husky install",
36
+ "prepare": "ts-patch install -s && is-ci || husky",
37
37
  "prepublishOnly": "npm run build",
38
38
  "release": "standard-version",
39
39
  "release:alpha": "standard-version --prerelease alpha",
@@ -41,35 +41,35 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "clone": "^2.1.2",
44
- "fastq": "^1.16.0",
44
+ "fastq": "^1.17.1",
45
45
  "minimatch": "^9.0.3",
46
46
  "object.fromentries": "^2.0.7",
47
47
  "ts-custom-error": "^3.3.1",
48
48
  "whatwg-url": "^14.0.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@buka/eslint-config": "^1.5.0",
52
- "@commitlint/cli": "^18.2.0",
53
- "@commitlint/config-conventional": "^18.1.0",
51
+ "@buka/eslint-config": "^1.6.0",
52
+ "@commitlint/cli": "^18.6.1",
53
+ "@commitlint/config-conventional": "^18.6.2",
54
54
  "@jest/globals": "^29.7.0",
55
- "@rushstack/eslint-patch": "^1.5.1",
55
+ "@rushstack/eslint-patch": "^1.7.2",
56
56
  "@types/clone": "^2.1.4",
57
57
  "@types/minimatch": "^5.1.2",
58
- "@types/node": "^20.9.0",
59
- "@types/whatwg-url": "^11.0.3",
60
- "@typescript-eslint/eslint-plugin": "^6.10.0",
61
- "@typescript-eslint/parser": "^6.10.0",
62
- "eslint": "^8.53.0",
63
- "husky": "^8.0.3",
58
+ "@types/node": "^20.11.20",
59
+ "@types/whatwg-url": "^11.0.4",
60
+ "@typescript-eslint/eslint-plugin": "^7.0.2",
61
+ "@typescript-eslint/parser": "^7.0.2",
62
+ "eslint": "^8.57.0",
63
+ "husky": "^9.0.11",
64
64
  "is-ci": "^3.0.1",
65
65
  "jest": "^29.7.0",
66
66
  "jest-mock": "^29.7.0",
67
67
  "standard-version": "^9.5.0",
68
- "ts-jest": "^29.1.1",
69
- "ts-node": "^10.9.1",
70
- "ts-patch": "^3.0.2",
71
- "typescript": "^5.2.2",
72
- "typescript-transform-paths": "^3.4.6"
68
+ "ts-jest": "^29.1.2",
69
+ "ts-node": "^10.9.2",
70
+ "ts-patch": "^3.1.2",
71
+ "typescript": "^5.3.3",
72
+ "typescript-transform-paths": "^3.4.7"
73
73
  },
74
74
  "packageManager": "pnpm@8.6.4",
75
75
  "engines": {