@mswjs/interceptors 0.25.0 → 0.25.2

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 (77) hide show
  1. package/lib/browser/Interceptor-8d5fd4c6.d.ts +86 -0
  2. package/lib/browser/{chunk-4CFMDU7Z.js → chunk-CWVY2E3W.js} +20 -134
  3. package/lib/browser/{chunk-VMXB5F2J.mjs → chunk-HXJPKJY3.mjs} +25 -15
  4. package/lib/browser/{chunk-DBFLI5DJ.js → chunk-KITNLK66.js} +30 -20
  5. package/lib/browser/chunk-KK6APRON.mjs +58 -0
  6. package/lib/browser/{chunk-OSIUQA4X.js → chunk-NMG5MQJJ.js} +32 -29
  7. package/lib/browser/{chunk-GXJLJMOT.mjs → chunk-QPMXOLDO.mjs} +21 -135
  8. package/lib/browser/{chunk-ANLPTCZ5.mjs → chunk-TYEVJTWH.mjs} +27 -24
  9. package/lib/browser/chunk-X3NRJIZW.js +58 -0
  10. package/lib/browser/index.d.ts +7 -3
  11. package/lib/browser/index.js +24 -5
  12. package/lib/browser/index.mjs +22 -3
  13. package/lib/browser/interceptors/XMLHttpRequest/index.d.ts +4 -3
  14. package/lib/browser/interceptors/XMLHttpRequest/index.js +4 -4
  15. package/lib/browser/interceptors/XMLHttpRequest/index.mjs +3 -3
  16. package/lib/browser/interceptors/fetch/index.d.ts +2 -1
  17. package/lib/browser/interceptors/fetch/index.js +4 -4
  18. package/lib/browser/interceptors/fetch/index.mjs +3 -3
  19. package/lib/browser/presets/browser.d.ts +2 -1
  20. package/lib/browser/presets/browser.js +6 -6
  21. package/lib/browser/presets/browser.mjs +4 -4
  22. package/lib/node/{BatchInterceptor-c841b068.d.ts → BatchInterceptor-9785c567.d.ts} +5 -2
  23. package/lib/node/Interceptor-7a701c1f.d.ts +86 -0
  24. package/lib/node/RemoteHttpInterceptor.d.ts +3 -2
  25. package/lib/node/RemoteHttpInterceptor.js +18 -18
  26. package/lib/node/RemoteHttpInterceptor.mjs +14 -14
  27. package/lib/node/{chunk-XYZRP5S2.js → chunk-3XFLRXRY.js} +20 -134
  28. package/lib/node/chunk-5PTPJLB7.js +58 -0
  29. package/lib/node/{chunk-E6YC337Q.js → chunk-5YAV7CXX.js} +29 -26
  30. package/lib/node/{chunk-HSCXCLVT.mjs → chunk-7RGC35CC.mjs} +27 -24
  31. package/lib/node/{chunk-3MYUI4B2.js → chunk-B2CIOP5B.js} +22 -16
  32. package/lib/node/{chunk-RGYCLCLK.mjs → chunk-GM3YBSM3.mjs} +21 -135
  33. package/lib/node/{chunk-OL7OR4RL.mjs → chunk-OMRBBJT7.mjs} +20 -14
  34. package/lib/node/{chunk-VS3GJPUE.mjs → chunk-UBEFEZXT.mjs} +22 -3
  35. package/lib/node/{chunk-MVPEJK4V.js → chunk-UF7QIAQ5.js} +23 -4
  36. package/lib/node/chunk-YQGTMMOZ.mjs +58 -0
  37. package/lib/node/index.d.ts +3 -2
  38. package/lib/node/index.js +3 -3
  39. package/lib/node/index.mjs +2 -2
  40. package/lib/node/interceptors/ClientRequest/index.d.ts +4 -3
  41. package/lib/node/interceptors/ClientRequest/index.js +4 -4
  42. package/lib/node/interceptors/ClientRequest/index.mjs +3 -3
  43. package/lib/node/interceptors/XMLHttpRequest/index.d.ts +4 -3
  44. package/lib/node/interceptors/XMLHttpRequest/index.js +4 -4
  45. package/lib/node/interceptors/XMLHttpRequest/index.mjs +3 -3
  46. package/lib/node/interceptors/fetch/index.d.ts +2 -1
  47. package/lib/node/interceptors/fetch/index.js +27 -17
  48. package/lib/node/interceptors/fetch/index.mjs +25 -15
  49. package/lib/node/presets/node.d.ts +3 -2
  50. package/lib/node/presets/node.js +6 -6
  51. package/lib/node/presets/node.mjs +4 -4
  52. package/package.json +2 -2
  53. package/src/BatchInterceptor.test.ts +141 -0
  54. package/src/BatchInterceptor.ts +38 -4
  55. package/src/Interceptor.test.ts +46 -0
  56. package/src/Interceptor.ts +35 -16
  57. package/src/RemoteHttpInterceptor.ts +11 -9
  58. package/src/interceptors/ClientRequest/NodeClientRequest.test.ts +10 -10
  59. package/src/interceptors/ClientRequest/NodeClientRequest.ts +35 -18
  60. package/src/interceptors/ClientRequest/index.test.ts +2 -3
  61. package/src/interceptors/ClientRequest/index.ts +2 -2
  62. package/src/interceptors/ClientRequest/utils/createRequest.test.ts +2 -2
  63. package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +29 -25
  64. package/src/interceptors/XMLHttpRequest/index.ts +2 -2
  65. package/src/interceptors/fetch/index.ts +26 -13
  66. package/src/utils/RequestController.ts +21 -0
  67. package/src/utils/emitAsync.ts +25 -0
  68. package/src/utils/toInteractiveRequest.ts +17 -23
  69. package/lib/browser/Interceptor-0a020bc4.d.ts +0 -116
  70. package/lib/browser/chunk-PCFJD76X.js +0 -64
  71. package/lib/browser/chunk-RT3ATOJH.mjs +0 -64
  72. package/lib/node/Interceptor-738f79c5.d.ts +0 -116
  73. package/lib/node/chunk-STA6QBYM.mjs +0 -64
  74. package/lib/node/chunk-ZJOF5MEZ.js +0 -64
  75. package/src/utils/AsyncEventEmitter.test.ts +0 -102
  76. package/src/utils/AsyncEventEmitter.ts +0 -193
  77. package/src/utils/createLazyCallback.ts +0 -49
@@ -1,8 +1,8 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunk3MYUI4B2js = require('../../chunk-3MYUI4B2.js');
4
- require('../../chunk-ZJOF5MEZ.js');
5
- require('../../chunk-XYZRP5S2.js');
3
+ var _chunkB2CIOP5Bjs = require('../../chunk-B2CIOP5B.js');
4
+ require('../../chunk-5PTPJLB7.js');
5
+ require('../../chunk-3XFLRXRY.js');
6
6
 
7
7
 
8
- exports.ClientRequestInterceptor = _chunk3MYUI4B2js.ClientRequestInterceptor;
8
+ exports.ClientRequestInterceptor = _chunkB2CIOP5Bjs.ClientRequestInterceptor;
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  ClientRequestInterceptor
3
- } from "../../chunk-OL7OR4RL.mjs";
4
- import "../../chunk-STA6QBYM.mjs";
5
- import "../../chunk-RGYCLCLK.mjs";
3
+ } from "../../chunk-OMRBBJT7.mjs";
4
+ import "../../chunk-YQGTMMOZ.mjs";
5
+ import "../../chunk-GM3YBSM3.mjs";
6
6
  export {
7
7
  ClientRequestInterceptor
8
8
  };
@@ -1,12 +1,13 @@
1
- import { f as InteractiveRequest, A as AsyncEventEmitter, H as HttpRequestEventMap, e as Interceptor } from '../../Interceptor-738f79c5.js';
1
+ import { Emitter } from 'strict-event-emitter';
2
+ import { f as InteractiveRequest, H as HttpRequestEventMap, e as Interceptor } from '../../Interceptor-7a701c1f.js';
3
+ import '@open-draft/deferred-promise';
2
4
  import '@open-draft/logger';
3
- import 'strict-event-emitter';
4
5
 
5
6
  type XMLHttpRequestEventListener = (args: {
6
7
  request: InteractiveRequest;
7
8
  requestId: string;
8
9
  }) => Promise<void> | void;
9
- type XMLHttpRequestEmitter = AsyncEventEmitter<HttpRequestEventMap>;
10
+ type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>;
10
11
  declare class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {
11
12
  static interceptorSymbol: symbol;
12
13
  constructor();
@@ -1,10 +1,10 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkE6YC337Qjs = require('../../chunk-E6YC337Q.js');
3
+ var _chunk5YAV7CXXjs = require('../../chunk-5YAV7CXX.js');
4
4
  require('../../chunk-3LFH2WCF.js');
5
5
  require('../../chunk-VQ4DZOBB.js');
6
- require('../../chunk-ZJOF5MEZ.js');
7
- require('../../chunk-XYZRP5S2.js');
6
+ require('../../chunk-5PTPJLB7.js');
7
+ require('../../chunk-3XFLRXRY.js');
8
8
 
9
9
 
10
- exports.XMLHttpRequestInterceptor = _chunkE6YC337Qjs.XMLHttpRequestInterceptor;
10
+ exports.XMLHttpRequestInterceptor = _chunk5YAV7CXXjs.XMLHttpRequestInterceptor;
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  XMLHttpRequestInterceptor
3
- } from "../../chunk-HSCXCLVT.mjs";
3
+ } from "../../chunk-7RGC35CC.mjs";
4
4
  import "../../chunk-7II4SWKS.mjs";
5
5
  import "../../chunk-GFH37L5D.mjs";
6
- import "../../chunk-STA6QBYM.mjs";
7
- import "../../chunk-RGYCLCLK.mjs";
6
+ import "../../chunk-YQGTMMOZ.mjs";
7
+ import "../../chunk-GM3YBSM3.mjs";
8
8
  export {
9
9
  XMLHttpRequestInterceptor
10
10
  };
@@ -1,4 +1,5 @@
1
- import { e as Interceptor, H as HttpRequestEventMap } from '../../Interceptor-738f79c5.js';
1
+ import { e as Interceptor, H as HttpRequestEventMap } from '../../Interceptor-7a701c1f.js';
2
+ import '@open-draft/deferred-promise';
2
3
  import '@open-draft/logger';
3
4
  import 'strict-event-emitter';
4
5
 
@@ -4,16 +4,17 @@ var _chunkVQ4DZOBBjs = require('../../chunk-VQ4DZOBB.js');
4
4
 
5
5
 
6
6
 
7
- var _chunkZJOF5MEZjs = require('../../chunk-ZJOF5MEZ.js');
8
7
 
8
+ var _chunk5PTPJLB7js = require('../../chunk-5PTPJLB7.js');
9
9
 
10
- var _chunkXYZRP5S2js = require('../../chunk-XYZRP5S2.js');
10
+
11
+ var _chunk3XFLRXRYjs = require('../../chunk-3XFLRXRY.js');
11
12
 
12
13
  // src/interceptors/fetch/index.ts
13
- var _deferredpromise = require('@open-draft/deferred-promise');
14
14
  var _outvariant = require('outvariant');
15
+ var _deferredpromise = require('@open-draft/deferred-promise');
15
16
  var _until = require('@open-draft/until');
16
- var _FetchInterceptor = class extends _chunkXYZRP5S2js.Interceptor {
17
+ var _FetchInterceptor = class extends _chunk3XFLRXRYjs.Interceptor {
17
18
  constructor() {
18
19
  super(_FetchInterceptor.symbol);
19
20
  }
@@ -28,17 +29,21 @@ var _FetchInterceptor = class extends _chunkXYZRP5S2js.Interceptor {
28
29
  );
29
30
  globalThis.fetch = async (input, init) => {
30
31
  var _a;
31
- const requestId = _chunkZJOF5MEZjs.uuidv4.call(void 0, );
32
+ const requestId = _chunk5PTPJLB7js.uuidv4.call(void 0, );
32
33
  const request = new Request(input, init);
33
34
  this.logger.info("[%s] %s", request.method, request.url);
34
- const interactiveRequest = _chunkZJOF5MEZjs.toInteractiveRequest.call(void 0, request);
35
+ const { interactiveRequest, requestController } = _chunk5PTPJLB7js.toInteractiveRequest.call(void 0, request);
35
36
  this.logger.info(
36
37
  'emitting the "request" event for %d listener(s)...',
37
38
  this.emitter.listenerCount("request")
38
39
  );
39
- this.emitter.emit("request", {
40
- request: interactiveRequest,
41
- requestId
40
+ this.emitter.once("request", ({ requestId: pendingRequestId }) => {
41
+ if (pendingRequestId !== requestId) {
42
+ return;
43
+ }
44
+ if (requestController.responsePromise.state === "pending") {
45
+ requestController.responsePromise.resolve(void 0);
46
+ }
42
47
  });
43
48
  this.logger.info("awaiting for the mocked response...");
44
49
  const signal = interactiveRequest.signal;
@@ -51,15 +56,20 @@ var _FetchInterceptor = class extends _chunkXYZRP5S2js.Interceptor {
51
56
  { once: true }
52
57
  );
53
58
  const resolverResult = await _until.until.call(void 0, async () => {
54
- const allListenersResolved = this.emitter.untilIdle(
55
- "request",
56
- ({ args: [{ requestId: pendingRequestId }] }) => {
57
- return pendingRequestId === requestId;
58
- }
59
- );
60
- await Promise.race([requestAborted, allListenersResolved]);
59
+ const listenersFinished = _chunk5PTPJLB7js.emitAsync.call(void 0, this.emitter, "request", {
60
+ request: interactiveRequest,
61
+ requestId
62
+ });
63
+ await Promise.race([
64
+ requestAborted,
65
+ // Put the listeners invocation Promise in the same race condition
66
+ // with the request abort Promise because otherwise awaiting the listeners
67
+ // would always yield some response (or undefined).
68
+ listenersFinished,
69
+ requestController.responsePromise
70
+ ]);
61
71
  this.logger.info("all request listeners have been resolved!");
62
- const [mockedResponse2] = await interactiveRequest.respondWith.invoked();
72
+ const mockedResponse2 = await requestController.responsePromise;
63
73
  this.logger.info("event.respondWith called with:", mockedResponse2);
64
74
  return mockedResponse2;
65
75
  });
@@ -2,16 +2,17 @@ import {
2
2
  IS_PATCHED_MODULE
3
3
  } from "../../chunk-GFH37L5D.mjs";
4
4
  import {
5
+ emitAsync,
5
6
  toInteractiveRequest,
6
7
  uuidv4
7
- } from "../../chunk-STA6QBYM.mjs";
8
+ } from "../../chunk-YQGTMMOZ.mjs";
8
9
  import {
9
10
  Interceptor
10
- } from "../../chunk-RGYCLCLK.mjs";
11
+ } from "../../chunk-GM3YBSM3.mjs";
11
12
 
12
13
  // src/interceptors/fetch/index.ts
13
- import { DeferredPromise } from "@open-draft/deferred-promise";
14
14
  import { invariant } from "outvariant";
15
+ import { DeferredPromise } from "@open-draft/deferred-promise";
15
16
  import { until } from "@open-draft/until";
16
17
  var _FetchInterceptor = class extends Interceptor {
17
18
  constructor() {
@@ -31,14 +32,18 @@ var _FetchInterceptor = class extends Interceptor {
31
32
  const requestId = uuidv4();
32
33
  const request = new Request(input, init);
33
34
  this.logger.info("[%s] %s", request.method, request.url);
34
- const interactiveRequest = toInteractiveRequest(request);
35
+ const { interactiveRequest, requestController } = toInteractiveRequest(request);
35
36
  this.logger.info(
36
37
  'emitting the "request" event for %d listener(s)...',
37
38
  this.emitter.listenerCount("request")
38
39
  );
39
- this.emitter.emit("request", {
40
- request: interactiveRequest,
41
- requestId
40
+ this.emitter.once("request", ({ requestId: pendingRequestId }) => {
41
+ if (pendingRequestId !== requestId) {
42
+ return;
43
+ }
44
+ if (requestController.responsePromise.state === "pending") {
45
+ requestController.responsePromise.resolve(void 0);
46
+ }
42
47
  });
43
48
  this.logger.info("awaiting for the mocked response...");
44
49
  const signal = interactiveRequest.signal;
@@ -51,15 +56,20 @@ var _FetchInterceptor = class extends Interceptor {
51
56
  { once: true }
52
57
  );
53
58
  const resolverResult = await until(async () => {
54
- const allListenersResolved = this.emitter.untilIdle(
55
- "request",
56
- ({ args: [{ requestId: pendingRequestId }] }) => {
57
- return pendingRequestId === requestId;
58
- }
59
- );
60
- await Promise.race([requestAborted, allListenersResolved]);
59
+ const listenersFinished = emitAsync(this.emitter, "request", {
60
+ request: interactiveRequest,
61
+ requestId
62
+ });
63
+ await Promise.race([
64
+ requestAborted,
65
+ // Put the listeners invocation Promise in the same race condition
66
+ // with the request abort Promise because otherwise awaiting the listeners
67
+ // would always yield some response (or undefined).
68
+ listenersFinished,
69
+ requestController.responsePromise
70
+ ]);
61
71
  this.logger.info("all request listeners have been resolved!");
62
- const [mockedResponse2] = await interactiveRequest.respondWith.invoked();
72
+ const mockedResponse2 = await requestController.responsePromise;
63
73
  this.logger.info("event.respondWith called with:", mockedResponse2);
64
74
  return mockedResponse2;
65
75
  });
@@ -2,9 +2,10 @@ import { ClientRequestInterceptor } from '../interceptors/ClientRequest/index.js
2
2
  import { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest/index.js';
3
3
  import 'http';
4
4
  import 'https';
5
- import '../Interceptor-738f79c5.js';
6
- import '@open-draft/logger';
7
5
  import 'strict-event-emitter';
6
+ import '../Interceptor-7a701c1f.js';
7
+ import '@open-draft/deferred-promise';
8
+ import '@open-draft/logger';
8
9
 
9
10
  /**
10
11
  * The default preset provisions the interception of requests
@@ -1,18 +1,18 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunk3MYUI4B2js = require('../chunk-3MYUI4B2.js');
3
+ var _chunkB2CIOP5Bjs = require('../chunk-B2CIOP5B.js');
4
4
 
5
5
 
6
- var _chunkE6YC337Qjs = require('../chunk-E6YC337Q.js');
6
+ var _chunk5YAV7CXXjs = require('../chunk-5YAV7CXX.js');
7
7
  require('../chunk-3LFH2WCF.js');
8
8
  require('../chunk-VQ4DZOBB.js');
9
- require('../chunk-ZJOF5MEZ.js');
10
- require('../chunk-XYZRP5S2.js');
9
+ require('../chunk-5PTPJLB7.js');
10
+ require('../chunk-3XFLRXRY.js');
11
11
 
12
12
  // src/presets/node.ts
13
13
  var node_default = [
14
- new (0, _chunk3MYUI4B2js.ClientRequestInterceptor)(),
15
- new (0, _chunkE6YC337Qjs.XMLHttpRequestInterceptor)()
14
+ new (0, _chunkB2CIOP5Bjs.ClientRequestInterceptor)(),
15
+ new (0, _chunk5YAV7CXXjs.XMLHttpRequestInterceptor)()
16
16
  ];
17
17
 
18
18
 
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  ClientRequestInterceptor
3
- } from "../chunk-OL7OR4RL.mjs";
3
+ } from "../chunk-OMRBBJT7.mjs";
4
4
  import {
5
5
  XMLHttpRequestInterceptor
6
- } from "../chunk-HSCXCLVT.mjs";
6
+ } from "../chunk-7RGC35CC.mjs";
7
7
  import "../chunk-7II4SWKS.mjs";
8
8
  import "../chunk-GFH37L5D.mjs";
9
- import "../chunk-STA6QBYM.mjs";
10
- import "../chunk-RGYCLCLK.mjs";
9
+ import "../chunk-YQGTMMOZ.mjs";
10
+ import "../chunk-GM3YBSM3.mjs";
11
11
 
12
12
  // src/presets/node.ts
13
13
  var node_default = [
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mswjs/interceptors",
3
3
  "description": "Low-level HTTP/HTTPS/XHR/fetch request interception library.",
4
- "version": "0.25.0",
4
+ "version": "0.25.2",
5
5
  "main": "./lib/node/index.js",
6
6
  "module": "./lib/node/index.mjs",
7
7
  "types": "./lib/node/index.d.ts",
@@ -123,7 +123,7 @@
123
123
  "webpack-http-server": "^0.5.0"
124
124
  },
125
125
  "dependencies": {
126
- "@open-draft/deferred-promise": "^2.1.0",
126
+ "@open-draft/deferred-promise": "^2.2.0",
127
127
  "@open-draft/logger": "^0.3.0",
128
128
  "@open-draft/until": "^2.0.0",
129
129
  "is-node-process": "^1.2.0",
@@ -112,3 +112,144 @@ it('disposes of child interceptors', async () => {
112
112
  expect(primaryDisposeSpy).toHaveBeenCalledTimes(1)
113
113
  expect(secondaryDisposeSpy).toHaveBeenCalledTimes(1)
114
114
  })
115
+
116
+ it('forwards listeners added via "on()"', () => {
117
+ class FirstInterceptor extends Interceptor<any> {
118
+ constructor() {
119
+ super(Symbol('first'))
120
+ }
121
+ }
122
+ class SecondaryInterceptor extends Interceptor<any> {
123
+ constructor() {
124
+ super(Symbol('second'))
125
+ }
126
+ }
127
+
128
+ const firstInterceptor = new FirstInterceptor()
129
+ const secondInterceptor = new SecondaryInterceptor()
130
+
131
+ const interceptor = new BatchInterceptor({
132
+ name: 'batch',
133
+ interceptors: [firstInterceptor, secondInterceptor],
134
+ })
135
+
136
+ const listener = vi.fn()
137
+ interceptor.on('foo', listener)
138
+
139
+ expect(firstInterceptor['emitter'].listenerCount('foo')).toBe(1)
140
+ expect(secondInterceptor['emitter'].listenerCount('foo')).toBe(1)
141
+ expect(interceptor['emitter'].listenerCount('foo')).toBe(0)
142
+ })
143
+
144
+ it('forwards listeners removal via "off()"', () => {
145
+ type Events = {
146
+ foo: []
147
+ }
148
+
149
+ class FirstInterceptor extends Interceptor<Events> {
150
+ constructor() {
151
+ super(Symbol('first'))
152
+ }
153
+ }
154
+ class SecondaryInterceptor extends Interceptor<Events> {
155
+ constructor() {
156
+ super(Symbol('second'))
157
+ }
158
+ }
159
+
160
+ const firstInterceptor = new FirstInterceptor()
161
+ const secondInterceptor = new SecondaryInterceptor()
162
+
163
+ const interceptor = new BatchInterceptor({
164
+ name: 'batch',
165
+ interceptors: [firstInterceptor, secondInterceptor],
166
+ })
167
+
168
+ const listener = vi.fn()
169
+ interceptor.on('foo', listener)
170
+ interceptor.off('foo', listener)
171
+
172
+ expect(firstInterceptor['emitter'].listenerCount('foo')).toBe(0)
173
+ expect(secondInterceptor['emitter'].listenerCount('foo')).toBe(0)
174
+ })
175
+
176
+ it('forwards removal of all listeners by name via ".removeAllListeners()"', () => {
177
+ type Events = {
178
+ foo: []
179
+ bar: []
180
+ }
181
+
182
+ class FirstInterceptor extends Interceptor<Events> {
183
+ constructor() {
184
+ super(Symbol('first'))
185
+ }
186
+ }
187
+ class SecondaryInterceptor extends Interceptor<Events> {
188
+ constructor() {
189
+ super(Symbol('second'))
190
+ }
191
+ }
192
+
193
+ const firstInterceptor = new FirstInterceptor()
194
+ const secondInterceptor = new SecondaryInterceptor()
195
+
196
+ const interceptor = new BatchInterceptor({
197
+ name: 'batch',
198
+ interceptors: [firstInterceptor, secondInterceptor],
199
+ })
200
+
201
+ const listener = vi.fn()
202
+ interceptor.on('foo', listener)
203
+ interceptor.on('foo', listener)
204
+ interceptor.on('bar', listener)
205
+
206
+ expect(firstInterceptor['emitter'].listenerCount('foo')).toBe(2)
207
+ expect(secondInterceptor['emitter'].listenerCount('foo')).toBe(2)
208
+ expect(firstInterceptor['emitter'].listenerCount('bar')).toBe(1)
209
+ expect(secondInterceptor['emitter'].listenerCount('bar')).toBe(1)
210
+
211
+ interceptor.removeAllListeners('foo')
212
+
213
+ expect(firstInterceptor['emitter'].listenerCount('foo')).toBe(0)
214
+ expect(secondInterceptor['emitter'].listenerCount('foo')).toBe(0)
215
+ expect(firstInterceptor['emitter'].listenerCount('bar')).toBe(1)
216
+ expect(secondInterceptor['emitter'].listenerCount('bar')).toBe(1)
217
+ })
218
+
219
+ it('forwards removal of all listeners via ".removeAllListeners()"', () => {
220
+ class FirstInterceptor extends Interceptor<any> {
221
+ constructor() {
222
+ super(Symbol('first'))
223
+ }
224
+ }
225
+ class SecondaryInterceptor extends Interceptor<any> {
226
+ constructor() {
227
+ super(Symbol('second'))
228
+ }
229
+ }
230
+
231
+ const firstInterceptor = new FirstInterceptor()
232
+ const secondInterceptor = new SecondaryInterceptor()
233
+
234
+ const interceptor = new BatchInterceptor({
235
+ name: 'batch',
236
+ interceptors: [firstInterceptor, secondInterceptor],
237
+ })
238
+
239
+ const listener = vi.fn()
240
+ interceptor.on('foo', listener)
241
+ interceptor.on('foo', listener)
242
+ interceptor.on('bar', listener)
243
+
244
+ expect(firstInterceptor['emitter'].listenerCount('foo')).toBe(2)
245
+ expect(secondInterceptor['emitter'].listenerCount('foo')).toBe(2)
246
+ expect(firstInterceptor['emitter'].listenerCount('bar')).toBe(1)
247
+ expect(secondInterceptor['emitter'].listenerCount('bar')).toBe(1)
248
+
249
+ interceptor.removeAllListeners()
250
+
251
+ expect(firstInterceptor['emitter'].listenerCount('foo')).toBe(0)
252
+ expect(secondInterceptor['emitter'].listenerCount('foo')).toBe(0)
253
+ expect(firstInterceptor['emitter'].listenerCount('bar')).toBe(0)
254
+ expect(secondInterceptor['emitter'].listenerCount('bar')).toBe(0)
255
+ })
@@ -1,5 +1,5 @@
1
1
  import { EventMap, Listener } from 'strict-event-emitter'
2
- import { ExtractEventNames, Interceptor } from './Interceptor'
2
+ import { Interceptor, ExtractEventNames } from './Interceptor'
3
3
 
4
4
  export interface BatchInterceptorOptions<
5
5
  InterceptorList extends ReadonlyArray<Interceptor<any>>
@@ -51,11 +51,45 @@ export class BatchInterceptor<
51
51
  public on<EventName extends ExtractEventNames<Events>>(
52
52
  event: EventName,
53
53
  listener: Listener<Events[EventName]>
54
- ) {
54
+ ): this {
55
55
  // Instead of adding a listener to the batch interceptor,
56
56
  // propagate the listener to each of the individual interceptors.
57
- this.interceptors.forEach((interceptor) => {
57
+ for (const interceptor of this.interceptors) {
58
58
  interceptor.on(event, listener)
59
- })
59
+ }
60
+
61
+ return this
62
+ }
63
+
64
+ public once<EventName extends ExtractEventNames<Events>>(
65
+ event: EventName,
66
+ listener: Listener<Events[EventName]>
67
+ ): this {
68
+ for (const interceptor of this.interceptors) {
69
+ interceptor.once(event, listener)
70
+ }
71
+
72
+ return this
73
+ }
74
+
75
+ public off<EventName extends ExtractEventNames<Events>>(
76
+ event: EventName,
77
+ listener: Listener<Events[EventName]>
78
+ ): this {
79
+ for (const interceptor of this.interceptors) {
80
+ interceptor.off(event, listener)
81
+ }
82
+
83
+ return this
84
+ }
85
+
86
+ public removeAllListeners<EventName extends ExtractEventNames<Events>>(
87
+ event?: EventName | undefined
88
+ ): this {
89
+ for (const interceptors of this.interceptors) {
90
+ interceptors.removeAllListeners(event)
91
+ }
92
+
93
+ return this
60
94
  }
61
95
  }
@@ -18,6 +18,52 @@ it('does not set a maximum listeners limit', () => {
18
18
  expect(interceptor['emitter'].getMaxListeners()).toBe(0)
19
19
  })
20
20
 
21
+ describe('on()', () => {
22
+ it('adds a new listener using "on()"', () => {
23
+ const interceptor = new Interceptor(symbol)
24
+ expect(interceptor['emitter'].listenerCount('event')).toBe(0)
25
+
26
+ const listener = vi.fn()
27
+ interceptor.on('event', listener)
28
+ expect(interceptor['emitter'].listenerCount('event')).toBe(1)
29
+ })
30
+ })
31
+
32
+ describe('once()', () => {
33
+ it('calls the listener only once', () => {
34
+ const interceptor = new Interceptor(symbol)
35
+ const listener = vi.fn()
36
+
37
+ interceptor.once('foo', listener)
38
+ expect(listener).not.toHaveBeenCalled()
39
+
40
+ interceptor['emitter'].emit('foo', 'bar')
41
+
42
+ expect(listener).toHaveBeenCalledTimes(1)
43
+ expect(listener).toHaveBeenCalledWith('bar')
44
+
45
+ listener.mockReset()
46
+
47
+ interceptor['emitter'].emit('foo', 'baz')
48
+ interceptor['emitter'].emit('foo', 'xyz')
49
+ expect(listener).toHaveBeenCalledTimes(0)
50
+ })
51
+ })
52
+
53
+ describe('off()', () => {
54
+ it('removes a listener using "off()"', () => {
55
+ const interceptor = new Interceptor(symbol)
56
+ expect(interceptor['emitter'].listenerCount('event')).toBe(0)
57
+
58
+ const listener = vi.fn()
59
+ interceptor.on('event', listener)
60
+ expect(interceptor['emitter'].listenerCount('event')).toBe(1)
61
+
62
+ interceptor.off('event', listener)
63
+ expect(interceptor['emitter'].listenerCount('event')).toBe(0)
64
+ })
65
+ })
66
+
21
67
  describe('persistence', () => {
22
68
  it('stores global reference to the applied interceptor', () => {
23
69
  const interceptor = new Interceptor(symbol)