keq 2.1.2 → 2.3.0

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.0](https://github.com/keq-request/keq/compare/v2.2.0...v2.3.0) (2024-02-24)
6
+
7
+
8
+ ### Features
9
+
10
+ * add .resolveWith method add deprecated resolveWithFullResponse ([1e01e7f](https://github.com/keq-request/keq/commit/1e01e7fe07c146d9f122f0f52778b45258797c68))
11
+
12
+ ## [2.2.0](https://github.com/keq-request/keq/compare/v2.1.2...v2.2.0) (2024-02-04)
13
+
14
+
15
+ ### Features
16
+
17
+ * add .timeout(millisecond) ([b99009b](https://github.com/keq-request/keq/commit/b99009b6eb4a017d648a29a8b34b478dc22320ee))
18
+
5
19
  ## [2.1.2](https://github.com/keq-request/keq/compare/v2.1.1...v2.1.2) (2024-01-17)
6
20
 
7
21
 
package/README.md CHANGED
@@ -6,10 +6,12 @@
6
6
  <h1 align="center" style="text-align: center">KEQ</h1>
7
7
  <!-- title -->
8
8
 
9
- [![version](https://img.shields.io/npm/v/keq.svg?logo=npm&style=for-the-badge)](https://www.npmjs.com/package/keq)
10
- [![downloads](https://img.shields.io/npm/dm/keq.svg?logo=npm&style=for-the-badge)](https://www.npmjs.com/package/keq)
11
- [![dependencies](https://img.shields.io/librariesio/release/npm/keq?logo=npm&style=for-the-badge)](https://www.npmjs.com/package/keq)
12
- [![license](https://img.shields.io/npm/l/keq.svg?logo=github&style=for-the-badge)](https://www.npmjs.com/package/keq)
9
+ [npm]: https://www.npmjs.com/package/keq
10
+
11
+ [![version](https://img.shields.io/npm/v/keq.svg?logo=npm&style=for-the-badge)][npm]
12
+ [![downloads](https://img.shields.io/npm/dm/keq.svg?logo=npm&style=for-the-badge)][npm]
13
+ [![dependencies](https://img.shields.io/librariesio/release/npm/keq?logo=npm&style=for-the-badge)][npm]
14
+ [![license](https://img.shields.io/npm/l/keq.svg?logo=github&style=for-the-badge)][npm]
13
15
  [![Codecov](https://img.shields.io/codecov/c/gh/keq-request/keq?logo=codecov&token=PLF0DT6869&style=for-the-badge)](https://codecov.io/gh/keq-request/keq)
14
16
 
15
17
  <!-- description -->
@@ -80,20 +82,22 @@ await request.del("https://example.com/search");
80
82
  > `.del()` is the alias of `.delete()`.
81
83
 
82
84
  `Keq` will parse `body` according to the `Content-Type` of [`Response`][Response MDN]
83
- and return `body` of [`Response`][Response MDN] by defaulted.
84
- 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.
85
87
 
86
88
  ```javascript
87
89
  import { request } from "keq";
88
90
 
89
91
  const response = await request
90
92
  .get("http://test.com")
91
- .option("resolveWithFullResponse");
93
+ .resolve('response')
92
94
 
93
95
  const body = await response.json();
94
96
  ```
95
97
 
96
- ###### `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
97
101
 
98
102
  ### Setting header fields
99
103
 
@@ -267,6 +271,22 @@ await request
267
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 |
268
272
  | svg | image/svg+xml |
269
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
+
270
290
  ### Request Retry
271
291
 
272
292
  No retry by default, invoke `.retry(retryTimes[, retryDelay[, retryOn]])` to set retry parameters
@@ -326,11 +346,6 @@ import { request } from "keq";
326
346
 
327
347
  const response = await request
328
348
  .get("http://test.com")
329
- /**
330
- * keq will return Response rather than parsed body
331
- * when set resolveWithFullResponse
332
- */
333
- .option("resolveWithFullResponse")
334
349
  .option("middlewareOption", "value");
335
350
  ```
336
351
 
@@ -342,17 +357,26 @@ import { request } from "keq";
342
357
  await request
343
358
  .get("http://test.com")
344
359
  .options({
345
- resolveWithFullResponse: true,
346
360
  middlewareOption: "value",
347
361
  });
348
362
  ```
349
363
 
350
364
  | **Option** | **Description** |
351
365
  | :------------------------ | :------------------------------------------------------------------------------------------------------ |
352
- | `resolveWithFullResponse` | Get the [`Response`][Response MDN] Class. This is the `.clone()` of original [`Response`][Response MDN] |
353
366
  | `fetchAPI` | Replace the defaulted `fetch` function used by `Keq`. |
354
367
 
355
368
  <!-- ###### The options with **DEPRECATED** will be removed in next major version -->
369
+
370
+ ### Timeout
371
+
372
+ Keq has built-in timeout function.
373
+
374
+ ```typescript
375
+ await request
376
+ .get("http://test.com")
377
+ // 5000 milliseconds
378
+ .timeout(5000)
379
+ ```
356
380
 
357
381
  ### Flow Control
358
382
 
@@ -399,7 +423,6 @@ request
399
423
  .followControl("serial", 'animal')
400
424
  .end()
401
425
  ```
402
-
403
426
 
404
427
  ### Middleware
405
428
 
@@ -419,7 +442,7 @@ const middleware = async (context, next) => {
419
442
  const body = await response.json()
420
443
 
421
444
  // custom keq return type
422
- response.output = JSON.stringify(body)
445
+ context.output = JSON.stringify(body)
423
446
  }
424
447
 
425
448
  // Global Middleware
@@ -498,10 +521,10 @@ Keq's context object has many parameters. The following lists all the built-in c
498
521
  | `context.request.referrer` | `referrer` arguments in [Fetch API][Fetch MDN] |
499
522
  | `context.request.referrerPolicy` | `referrerPolicy` arguments in [Fetch API][Fetch MDN] |
500
523
  | `context.request.signal` | `signal` arguments in [Fetch API][Fetch MDN] |
501
- | `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. |
502
525
  | `context.res` | The origin [`Response`][Response MDN] Class. It will be undefined before run `await next()` or error throwed. |
503
526
  | `context.response` | Cloned from `ctx.res`. |
504
- | `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.** |
505
528
 
506
529
  #### .useRouter()
507
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();
@@ -9,6 +9,7 @@ import { proxyResponseMiddleware } from './middlewares/proxy-response-middleware
9
9
  import { retryMiddleware } from './middlewares/retry-middleware';
10
10
  import { serialFlowControlMiddleware } from './middlewares/serial-flow-control-middleware.js';
11
11
  import { KeqRouter } from './router/keq-router.js';
12
+ import { timeoutMiddleware } from './middlewares/timeout-middleware.js';
12
13
  export function createRequest(options) {
13
14
  let baseOrigin = options?.baseOrigin;
14
15
  if (isBrowser() && !baseOrigin)
@@ -16,6 +17,7 @@ export function createRequest(options) {
16
17
  const appendMiddlewares = options?.initMiddlewares ? [...options.initMiddlewares] : [
17
18
  serialFlowControlMiddleware(),
18
19
  abortFlowControlMiddleware(),
20
+ timeoutMiddleware(),
19
21
  proxyResponseMiddleware(),
20
22
  fetchArgumentsMiddleware(),
21
23
  retryMiddleware(),
@@ -70,4 +70,10 @@ export declare class Keq<T> extends Core<T> {
70
70
  credentials(mod: RequestCredentials): this;
71
71
  mode(mod: RequestMode): this;
72
72
  flowControl(mode: KeqFlowControlMode, signal?: KeqFlowControlSignal): this;
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>;
73
79
  }
@@ -196,4 +196,13 @@ export class Keq extends Core {
196
196
  this.option('flowControl', flowControl);
197
197
  return this;
198
198
  }
199
+ timeout(milliseconds) {
200
+ this.option('timeout', { millisecond: milliseconds });
201
+ return this;
202
+ }
203
+ resolveWith(m) {
204
+ this.option('resolveWith', m);
205
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
206
+ return this;
207
+ }
199
208
  }
@@ -0,0 +1,2 @@
1
+ import { KeqMiddleware } from "../types/keq-middleware.js";
2
+ export declare function timeoutMiddleware(): KeqMiddleware;
@@ -0,0 +1,19 @@
1
+ export function timeoutMiddleware() {
2
+ return async (ctx, next) => {
3
+ if (!ctx.options.timeout || ctx.options.timeout.millisecond <= 0) {
4
+ await next();
5
+ return;
6
+ }
7
+ if (ctx.request.signal) {
8
+ console.warn('[keq] request signal had be set manual, abort follow control will not take effect');
9
+ await next();
10
+ return;
11
+ }
12
+ const timeoutSignal = new AbortController();
13
+ ctx.request.signal = timeoutSignal.signal;
14
+ setTimeout(() => {
15
+ timeoutSignal.abort('timeout');
16
+ }, ctx.options.timeout.millisecond);
17
+ await next();
18
+ };
19
+ }
@@ -1,14 +1,25 @@
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';
5
+ import { KeqTimeout } from './keq-timeout.js';
4
6
  export interface KeqBuildInOptions {
5
7
  /**
6
8
  * replace the default fetch api
7
9
  * default use node-fetch@2 in node and window.fetch in browser
8
10
  */
9
11
  fetchAPI?: typeof fetch;
10
- /** get response object, defaulted `false` */
12
+ /**
13
+ * get response object, defaulted `false`
14
+ * @deprecated use `resolveWith` instead
15
+ * */
11
16
  resolveWithFullResponse?: boolean;
17
+ /**
18
+ * how to resolve the response body
19
+ * @description 如何解析响应体
20
+ * @default 'intelligent'
21
+ */
22
+ resolveWith?: KeqResolveMethod;
12
23
  /**
13
24
  * The request retry times
14
25
  * @description 重试次数
@@ -29,6 +40,7 @@ export interface KeqBuildInOptions {
29
40
  pathname: string;
30
41
  };
31
42
  flowControl?: KeqFlowControl;
43
+ timeout?: KeqTimeout;
32
44
  }
33
45
  export interface KeqOptionsWithFullResponse extends KeqBuildInOptions {
34
46
  resolveWithFullResponse: true;
@@ -0,0 +1 @@
1
+ export type KeqResolveMethod = 'intelligent' | 'response' | 'text' | 'json' | 'form-data' | 'blob' | 'array-buffer';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export interface KeqTimeout {
2
+ millisecond: number;
3
+ }
@@ -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();
@@ -4,7 +4,7 @@
4
4
  if (v !== undefined) module.exports = v;
5
5
  }
6
6
  else if (typeof define === "function" && define.amd) {
7
- define(["require", "exports", "whatwg-url", "./is/is-browser", "./keq", "./middlewares/abort-flow-control-middleware.js", "./middlewares/fetch-arguments-middleware", "./middlewares/fetch-middleware", "./middlewares/proxy-response-middleware", "./middlewares/retry-middleware", "./middlewares/serial-flow-control-middleware.js", "./router/keq-router.js"], factory);
7
+ define(["require", "exports", "whatwg-url", "./is/is-browser", "./keq", "./middlewares/abort-flow-control-middleware.js", "./middlewares/fetch-arguments-middleware", "./middlewares/fetch-middleware", "./middlewares/proxy-response-middleware", "./middlewares/retry-middleware", "./middlewares/serial-flow-control-middleware.js", "./router/keq-router.js", "./middlewares/timeout-middleware.js"], factory);
8
8
  }
9
9
  })(function (require, exports) {
10
10
  "use strict";
@@ -21,6 +21,7 @@
21
21
  const retry_middleware_1 = require("./middlewares/retry-middleware");
22
22
  const serial_flow_control_middleware_js_1 = require("./middlewares/serial-flow-control-middleware.js");
23
23
  const keq_router_js_1 = require("./router/keq-router.js");
24
+ const timeout_middleware_js_1 = require("./middlewares/timeout-middleware.js");
24
25
  function createRequest(options) {
25
26
  let baseOrigin = options?.baseOrigin;
26
27
  if ((0, is_browser_1.isBrowser)() && !baseOrigin)
@@ -28,6 +29,7 @@
28
29
  const appendMiddlewares = options?.initMiddlewares ? [...options.initMiddlewares] : [
29
30
  (0, serial_flow_control_middleware_js_1.serialFlowControlMiddleware)(),
30
31
  (0, abort_flow_control_middleware_js_1.abortFlowControlMiddleware)(),
32
+ (0, timeout_middleware_js_1.timeoutMiddleware)(),
31
33
  (0, proxy_response_middleware_1.proxyResponseMiddleware)(),
32
34
  (0, fetch_arguments_middleware_1.fetchArgumentsMiddleware)(),
33
35
  (0, retry_middleware_1.retryMiddleware)(),
@@ -70,4 +70,10 @@ export declare class Keq<T> extends Core<T> {
70
70
  credentials(mod: RequestCredentials): this;
71
71
  mode(mod: RequestMode): this;
72
72
  flowControl(mode: KeqFlowControlMode, signal?: KeqFlowControlSignal): this;
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>;
73
79
  }
@@ -208,6 +208,15 @@
208
208
  this.option('flowControl', flowControl);
209
209
  return this;
210
210
  }
211
+ timeout(milliseconds) {
212
+ this.option('timeout', { millisecond: milliseconds });
213
+ return this;
214
+ }
215
+ resolveWith(m) {
216
+ this.option('resolveWith', m);
217
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
218
+ return this;
219
+ }
211
220
  }
212
221
  exports.Keq = Keq;
213
222
  });
@@ -0,0 +1,2 @@
1
+ import { KeqMiddleware } from "../types/keq-middleware.js";
2
+ export declare function timeoutMiddleware(): KeqMiddleware;
@@ -0,0 +1,33 @@
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
+ exports.timeoutMiddleware = void 0;
13
+ function timeoutMiddleware() {
14
+ return async (ctx, next) => {
15
+ if (!ctx.options.timeout || ctx.options.timeout.millisecond <= 0) {
16
+ await next();
17
+ return;
18
+ }
19
+ if (ctx.request.signal) {
20
+ console.warn('[keq] request signal had be set manual, abort follow control will not take effect');
21
+ await next();
22
+ return;
23
+ }
24
+ const timeoutSignal = new AbortController();
25
+ ctx.request.signal = timeoutSignal.signal;
26
+ setTimeout(() => {
27
+ timeoutSignal.abort('timeout');
28
+ }, ctx.options.timeout.millisecond);
29
+ await next();
30
+ };
31
+ }
32
+ exports.timeoutMiddleware = timeoutMiddleware;
33
+ });
@@ -1,14 +1,25 @@
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';
5
+ import { KeqTimeout } from './keq-timeout.js';
4
6
  export interface KeqBuildInOptions {
5
7
  /**
6
8
  * replace the default fetch api
7
9
  * default use node-fetch@2 in node and window.fetch in browser
8
10
  */
9
11
  fetchAPI?: typeof fetch;
10
- /** get response object, defaulted `false` */
12
+ /**
13
+ * get response object, defaulted `false`
14
+ * @deprecated use `resolveWith` instead
15
+ * */
11
16
  resolveWithFullResponse?: boolean;
17
+ /**
18
+ * how to resolve the response body
19
+ * @description 如何解析响应体
20
+ * @default 'intelligent'
21
+ */
22
+ resolveWith?: KeqResolveMethod;
12
23
  /**
13
24
  * The request retry times
14
25
  * @description 重试次数
@@ -29,6 +40,7 @@ export interface KeqBuildInOptions {
29
40
  pathname: string;
30
41
  };
31
42
  flowControl?: KeqFlowControl;
43
+ timeout?: KeqTimeout;
32
44
  }
33
45
  export interface KeqOptionsWithFullResponse extends KeqBuildInOptions {
34
46
  resolveWithFullResponse: true;
@@ -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
+ });
@@ -0,0 +1,3 @@
1
+ export interface KeqTimeout {
2
+ millisecond: number;
3
+ }
@@ -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.1.2",
3
+ "version": "2.3.0",
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": {