@fatcherjs/middleware-aborter 1.3.0 → 1.5.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/README.md CHANGED
@@ -67,6 +67,22 @@ Aborted request will throw a DOMException which can use `isAbortError` to confir
67
67
 
68
68
  A callback when aborting this request.
69
69
 
70
+ ### concurrency
71
+
72
+ - Type: `boolean`
73
+ - DefaultValue: `false`
74
+ - Description:
75
+
76
+ Request concurrency restrictions
77
+
78
+ ### groupBy
79
+
80
+ - Type: `(context: Readonly<Context>) => string;`
81
+ - DefaultValue: `${context.url}_${context.method}_${new URLSearchParams(context.params).toString()}`
82
+ - Description:
83
+
84
+ Concurrency key.
85
+
70
86
  ## License
71
87
 
72
88
  [MIT](https://github.com/fatcherjs/fatcher/blob/master/LICENSE)
package/dist/aborter.d.ts CHANGED
@@ -1,5 +1,13 @@
1
- import { Middleware } from 'fatcher';
1
+ import { Context, Middleware } from 'fatcher';
2
2
 
3
+ declare type AbortReason = 'concurrency' | 'timeout' | 'manual';
4
+ declare type AbortEventHandler = (type: AbortReason) => void;
5
+ declare type RoadSign = {
6
+ abort: (type: AbortReason) => void;
7
+ timer: NodeJS.Timeout | null;
8
+ signal: AbortSignal;
9
+ };
10
+ declare type RoadMap = Record<string, RoadSign[]>;
3
11
  interface AborterOptions {
4
12
  /**
5
13
  * Request timeout
@@ -14,7 +22,17 @@ interface AborterOptions {
14
22
  *
15
23
  * @default null
16
24
  */
17
- onAbort?: (() => void) | null;
25
+ onAbort?: AbortEventHandler | null;
26
+ /**
27
+ * Request concurrency restrictions
28
+ *
29
+ * @default false
30
+ */
31
+ concurrency?: boolean;
32
+ /**
33
+ * Concurrency key.
34
+ */
35
+ groupBy?: (context: Readonly<Context>) => string;
18
36
  }
19
37
 
20
38
  /**
@@ -24,4 +42,11 @@ interface AborterOptions {
24
42
  */
25
43
  declare function aborter(options?: AborterOptions): Middleware;
26
44
 
27
- export { AborterOptions, aborter };
45
+ /**
46
+ * Confirm an error whether is DOMException
47
+ * @param error
48
+ * @returns
49
+ */
50
+ declare function isAbortError(error: unknown): error is DOMException;
51
+
52
+ export { AbortEventHandler, AbortReason, AborterOptions, RoadMap, RoadSign, aborter, isAbortError };
@@ -1,29 +1,55 @@
1
+ const roadMap = {};
1
2
  function aborter(options = {}) {
2
- const { timeout = 0, onAbort = null } = options;
3
+ const { timeout = 0, onAbort = null, concurrency, groupBy } = options;
3
4
  let _timeout = timeout;
4
- if (isNaN(timeout) || ~~timeout <= 0) {
5
+ if (isNaN(timeout) || ~~timeout < 0) {
5
6
  console.warn("[fatcher-middleware-aborter] Timeout is not a valid number.");
6
7
  _timeout = 0;
7
8
  }
8
9
  return {
9
10
  name: "fatcher-middleware-aborter",
10
11
  async use(context, next) {
12
+ var _a, _b, _c;
11
13
  const abortController = new AbortController();
12
- const requestTask = next({
13
- signal: abortController.signal
14
- });
15
- if (!_timeout) {
16
- return requestTask;
14
+ const { abort, signal } = abortController;
15
+ const requestTask = next({ signal });
16
+ const group = (_a = groupBy == null ? void 0 : groupBy(context)) != null ? _a : `${context.url}_${context.method}_${new URLSearchParams(context.params).toString()}`;
17
+ if (((_b = roadMap[group]) == null ? void 0 : _b.length) && concurrency) {
18
+ roadMap[group].forEach((item) => {
19
+ item.abort("concurrency");
20
+ });
17
21
  }
18
- const timer = setTimeout(() => {
19
- abortController.abort();
20
- onAbort == null ? void 0 : onAbort();
21
- }, _timeout);
22
- const response = await requestTask;
23
- clearTimeout(timer);
24
- return response;
22
+ (_c = roadMap[group]) != null ? _c : roadMap[group] = [];
23
+ const trigger = (reason) => {
24
+ abort.call(abortController);
25
+ onAbort == null ? void 0 : onAbort(reason);
26
+ };
27
+ roadMap[group].push({
28
+ abort: trigger,
29
+ timer: _timeout ? setTimeout(() => trigger("timeout"), _timeout) : null,
30
+ signal
31
+ });
32
+ signal.addEventListener("abort", () => {
33
+ roadMap[group] = roadMap[group].filter((item) => {
34
+ if (item.signal === signal) {
35
+ if (item.timer) {
36
+ clearTimeout(item.timer);
37
+ }
38
+ return false;
39
+ }
40
+ return true;
41
+ });
42
+ if (!roadMap[group].length) {
43
+ delete roadMap[group];
44
+ }
45
+ });
46
+ return requestTask;
25
47
  }
26
48
  };
27
49
  }
28
50
 
29
- export { aborter };
51
+ function isAbortError(error) {
52
+ return error instanceof DOMException && error.name === "AbortError";
53
+ }
54
+
55
+ export { aborter, isAbortError };
package/dist/aborter.js CHANGED
@@ -2,32 +2,59 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ const roadMap = {};
5
6
  function aborter(options = {}) {
6
- const { timeout = 0, onAbort = null } = options;
7
+ const { timeout = 0, onAbort = null, concurrency, groupBy } = options;
7
8
  let _timeout = timeout;
8
- if (isNaN(timeout) || ~~timeout <= 0) {
9
+ if (isNaN(timeout) || ~~timeout < 0) {
9
10
  console.warn("[fatcher-middleware-aborter] Timeout is not a valid number.");
10
11
  _timeout = 0;
11
12
  }
12
13
  return {
13
14
  name: "fatcher-middleware-aborter",
14
15
  async use(context, next) {
16
+ var _a, _b, _c;
15
17
  const abortController = new AbortController();
16
- const requestTask = next({
17
- signal: abortController.signal
18
- });
19
- if (!_timeout) {
20
- return requestTask;
18
+ const { abort, signal } = abortController;
19
+ const requestTask = next({ signal });
20
+ const group = (_a = groupBy == null ? void 0 : groupBy(context)) != null ? _a : `${context.url}_${context.method}_${new URLSearchParams(context.params).toString()}`;
21
+ if (((_b = roadMap[group]) == null ? void 0 : _b.length) && concurrency) {
22
+ roadMap[group].forEach((item) => {
23
+ item.abort("concurrency");
24
+ });
21
25
  }
22
- const timer = setTimeout(() => {
23
- abortController.abort();
24
- onAbort == null ? void 0 : onAbort();
25
- }, _timeout);
26
- const response = await requestTask;
27
- clearTimeout(timer);
28
- return response;
26
+ (_c = roadMap[group]) != null ? _c : roadMap[group] = [];
27
+ const trigger = (reason) => {
28
+ abort.call(abortController);
29
+ onAbort == null ? void 0 : onAbort(reason);
30
+ };
31
+ roadMap[group].push({
32
+ abort: trigger,
33
+ timer: _timeout ? setTimeout(() => trigger("timeout"), _timeout) : null,
34
+ signal
35
+ });
36
+ signal.addEventListener("abort", () => {
37
+ roadMap[group] = roadMap[group].filter((item) => {
38
+ if (item.signal === signal) {
39
+ if (item.timer) {
40
+ clearTimeout(item.timer);
41
+ }
42
+ return false;
43
+ }
44
+ return true;
45
+ });
46
+ if (!roadMap[group].length) {
47
+ delete roadMap[group];
48
+ }
49
+ });
50
+ return requestTask;
29
51
  }
30
52
  };
31
53
  }
32
54
 
55
+ function isAbortError(error) {
56
+ return error instanceof DOMException && error.name === "AbortError";
57
+ }
58
+
33
59
  exports.aborter = aborter;
60
+ exports.isAbortError = isAbortError;
@@ -1 +1 @@
1
- (function(e,t){typeof exports=="object"&&typeof module<"u"?t(exports):typeof define=="function"&&define.amd?define(["exports"],t):(e=typeof globalThis<"u"?globalThis:e||self,t(e.FatcherMiddlewareAborter={}))})(this,function(e){"use strict";function t(a={}){const{timeout:o=0,onAbort:r=null}=a;let n=o;return(isNaN(o)||~~o<=0)&&(console.warn("[fatcher-middleware-aborter] Timeout is not a valid number."),n=0),{name:"fatcher-middleware-aborter",async use(c,u){const i=new AbortController,s=u({signal:i.signal});if(!n)return s;const l=setTimeout(()=>{i.abort(),r?.()},n),d=await s;return clearTimeout(l),d}}}e.aborter=t,Object.defineProperty(e,"__esModule",{value:!0})});
1
+ (function(t,e){typeof exports=="object"&&typeof module!="undefined"?e(exports):typeof define=="function"&&define.amd?define(["exports"],e):(t=typeof globalThis!="undefined"?globalThis:t||self,e(t.FatcherMiddlewareAborter={}))})(this,function(t){"use strict";const e={};function p(o={}){const{timeout:u=0,onAbort:s=null,concurrency:_,groupBy:c}=o;let l=u;return(isNaN(u)||~~u<0)&&(console.warn("[fatcher-middleware-aborter] Timeout is not a valid number."),l=0),{name:"fatcher-middleware-aborter",async use(i,v){var d,f,b;const m=new AbortController,{abort:y,signal:a}=m,w=v({signal:a}),r=(d=c==null?void 0:c(i))!=null?d:`${i.url}_${i.method}_${new URLSearchParams(i.params).toString()}`;((f=e[r])==null?void 0:f.length)&&_&&e[r].forEach(n=>{n.abort("concurrency")}),(b=e[r])!=null||(e[r]=[]);const h=n=>{y.call(m),s==null||s(n)};return e[r].push({abort:h,timer:l?setTimeout(()=>h("timeout"),l):null,signal:a}),a.addEventListener("abort",()=>{e[r]=e[r].filter(n=>n.signal===a?(n.timer&&clearTimeout(n.timer),!1):!0),e[r].length||delete e[r]}),w}}}function g(o){return o instanceof DOMException&&o.name==="AbortError"}t.aborter=p,t.isAbortError=g,Object.defineProperty(t,"__esModule",{value:!0})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fatcherjs/middleware-aborter",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "main": "dist/aborter.js",
5
5
  "module": "dist/aborter.esm.js",
6
6
  "browser": "dist/aborter.min.js",