@mswjs/interceptors 0.33.3 → 0.34.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/lib/browser/interceptors/WebSocket/index.d.ts +2 -0
- package/lib/browser/interceptors/WebSocket/index.js +42 -17
- package/lib/browser/interceptors/WebSocket/index.js.map +1 -1
- package/lib/browser/interceptors/WebSocket/index.mjs +42 -17
- package/lib/browser/interceptors/WebSocket/index.mjs.map +1 -1
- package/lib/node/chunk-RTGLFNO3.mjs +142 -0
- package/lib/node/chunk-RTGLFNO3.mjs.map +1 -0
- package/lib/node/chunk-UN335ZTD.js +142 -0
- package/lib/node/chunk-UN335ZTD.js.map +1 -0
- package/lib/node/interceptors/fetch/index.js +5 -136
- package/lib/node/interceptors/fetch/index.js.map +1 -1
- package/lib/node/interceptors/fetch/index.mjs +5 -136
- package/lib/node/interceptors/fetch/index.mjs.map +1 -1
- package/lib/node/presets/node.d.ts +2 -1
- package/lib/node/presets/node.js +5 -1
- package/lib/node/presets/node.js.map +1 -1
- package/lib/node/presets/node.mjs +5 -1
- package/lib/node/presets/node.mjs.map +1 -1
- package/package.json +1 -1
- package/src/interceptors/WebSocket/WebSocketOverride.ts +26 -15
- package/src/interceptors/WebSocket/index.ts +62 -12
- package/src/presets/node.ts +2 -0
|
@@ -1,141 +1,10 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkUN335ZTDjs = require('../../chunk-UN335ZTD.js');
|
|
4
|
+
require('../../chunk-IDEEMJ3F.js');
|
|
5
|
+
require('../../chunk-5WWNCLB3.js');
|
|
6
|
+
require('../../chunk-YGM3BCJU.js');
|
|
4
7
|
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var _chunk5WWNCLB3js = require('../../chunk-5WWNCLB3.js');
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var _chunkYGM3BCJUjs = require('../../chunk-YGM3BCJU.js');
|
|
13
|
-
|
|
14
|
-
// src/interceptors/fetch/index.ts
|
|
15
|
-
var _outvariant = require('outvariant');
|
|
16
|
-
var _deferredpromise = require('@open-draft/deferred-promise');
|
|
17
|
-
|
|
18
|
-
// src/utils/canParseUrl.ts
|
|
19
|
-
function canParseUrl(url) {
|
|
20
|
-
try {
|
|
21
|
-
new URL(url);
|
|
22
|
-
return true;
|
|
23
|
-
} catch (_error) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// src/interceptors/fetch/index.ts
|
|
29
|
-
var _FetchInterceptor = class extends _chunkYGM3BCJUjs.Interceptor {
|
|
30
|
-
constructor() {
|
|
31
|
-
super(_FetchInterceptor.symbol);
|
|
32
|
-
}
|
|
33
|
-
checkEnvironment() {
|
|
34
|
-
return typeof globalThis !== "undefined" && typeof globalThis.fetch !== "undefined";
|
|
35
|
-
}
|
|
36
|
-
async setup() {
|
|
37
|
-
const pureFetch = globalThis.fetch;
|
|
38
|
-
_outvariant.invariant.call(void 0,
|
|
39
|
-
!pureFetch[_chunkIDEEMJ3Fjs.IS_PATCHED_MODULE],
|
|
40
|
-
'Failed to patch the "fetch" module: already patched.'
|
|
41
|
-
);
|
|
42
|
-
globalThis.fetch = async (input, init) => {
|
|
43
|
-
const requestId = _chunkYGM3BCJUjs.createRequestId.call(void 0, );
|
|
44
|
-
const resolvedInput = typeof input === "string" && typeof location !== "undefined" && !canParseUrl(input) ? new URL(input, location.origin) : input;
|
|
45
|
-
const request = new Request(resolvedInput, init);
|
|
46
|
-
const responsePromise = new (0, _deferredpromise.DeferredPromise)();
|
|
47
|
-
const controller = new (0, _chunk5WWNCLB3js.RequestController)(request);
|
|
48
|
-
this.logger.info("[%s] %s", request.method, request.url);
|
|
49
|
-
this.logger.info("awaiting for the mocked response...");
|
|
50
|
-
this.logger.info(
|
|
51
|
-
'emitting the "request" event for %s listener(s)...',
|
|
52
|
-
this.emitter.listenerCount("request")
|
|
53
|
-
);
|
|
54
|
-
const isRequestHandled = await _chunk5WWNCLB3js.handleRequest.call(void 0, {
|
|
55
|
-
request,
|
|
56
|
-
requestId,
|
|
57
|
-
emitter: this.emitter,
|
|
58
|
-
controller,
|
|
59
|
-
onResponse: async (response) => {
|
|
60
|
-
this.logger.info("received mocked response!", {
|
|
61
|
-
response
|
|
62
|
-
});
|
|
63
|
-
if (this.emitter.listenerCount("response") > 0) {
|
|
64
|
-
this.logger.info('emitting the "response" event...');
|
|
65
|
-
await _chunk5WWNCLB3js.emitAsync.call(void 0, this.emitter, "response", {
|
|
66
|
-
// Clone the mocked response for the "response" event listener.
|
|
67
|
-
// This way, the listener can read the response and not lock its body
|
|
68
|
-
// for the actual fetch consumer.
|
|
69
|
-
response: response.clone(),
|
|
70
|
-
isMockedResponse: true,
|
|
71
|
-
request,
|
|
72
|
-
requestId
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
Object.defineProperty(response, "url", {
|
|
76
|
-
writable: false,
|
|
77
|
-
enumerable: true,
|
|
78
|
-
configurable: false,
|
|
79
|
-
value: request.url
|
|
80
|
-
});
|
|
81
|
-
responsePromise.resolve(response);
|
|
82
|
-
},
|
|
83
|
-
onRequestError: (response) => {
|
|
84
|
-
this.logger.info("request has errored!", { response });
|
|
85
|
-
responsePromise.reject(createNetworkError(response));
|
|
86
|
-
},
|
|
87
|
-
onError: (error) => {
|
|
88
|
-
this.logger.info("request has been aborted!", { error });
|
|
89
|
-
responsePromise.reject(error);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
if (isRequestHandled) {
|
|
93
|
-
this.logger.info("request has been handled, returning mock promise...");
|
|
94
|
-
return responsePromise;
|
|
95
|
-
}
|
|
96
|
-
this.logger.info(
|
|
97
|
-
"no mocked response received, performing request as-is..."
|
|
98
|
-
);
|
|
99
|
-
return pureFetch(request).then((response) => {
|
|
100
|
-
this.logger.info("original fetch performed", response);
|
|
101
|
-
if (this.emitter.listenerCount("response") > 0) {
|
|
102
|
-
this.logger.info('emitting the "response" event...');
|
|
103
|
-
const responseClone = response.clone();
|
|
104
|
-
this.emitter.emit("response", {
|
|
105
|
-
response: responseClone,
|
|
106
|
-
isMockedResponse: false,
|
|
107
|
-
request,
|
|
108
|
-
requestId
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
return response;
|
|
112
|
-
});
|
|
113
|
-
};
|
|
114
|
-
Object.defineProperty(globalThis.fetch, _chunkIDEEMJ3Fjs.IS_PATCHED_MODULE, {
|
|
115
|
-
enumerable: true,
|
|
116
|
-
configurable: true,
|
|
117
|
-
value: true
|
|
118
|
-
});
|
|
119
|
-
this.subscriptions.push(() => {
|
|
120
|
-
Object.defineProperty(globalThis.fetch, _chunkIDEEMJ3Fjs.IS_PATCHED_MODULE, {
|
|
121
|
-
value: void 0
|
|
122
|
-
});
|
|
123
|
-
globalThis.fetch = pureFetch;
|
|
124
|
-
this.logger.info(
|
|
125
|
-
'restored native "globalThis.fetch"!',
|
|
126
|
-
globalThis.fetch.name
|
|
127
|
-
);
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
var FetchInterceptor = _FetchInterceptor;
|
|
132
|
-
FetchInterceptor.symbol = Symbol("fetch");
|
|
133
|
-
function createNetworkError(cause) {
|
|
134
|
-
return Object.assign(new TypeError("Failed to fetch"), {
|
|
135
|
-
cause
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
exports.FetchInterceptor = FetchInterceptor;
|
|
9
|
+
exports.FetchInterceptor = _chunkUN335ZTDjs.FetchInterceptor;
|
|
141
10
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":""}
|
|
@@ -1,140 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "../../chunk-
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
handleRequest
|
|
8
|
-
} from "../../chunk-KY3RJ2M3.mjs";
|
|
9
|
-
import {
|
|
10
|
-
Interceptor,
|
|
11
|
-
createRequestId
|
|
12
|
-
} from "../../chunk-BUCULLYM.mjs";
|
|
13
|
-
|
|
14
|
-
// src/interceptors/fetch/index.ts
|
|
15
|
-
import { invariant } from "outvariant";
|
|
16
|
-
import { DeferredPromise } from "@open-draft/deferred-promise";
|
|
17
|
-
|
|
18
|
-
// src/utils/canParseUrl.ts
|
|
19
|
-
function canParseUrl(url) {
|
|
20
|
-
try {
|
|
21
|
-
new URL(url);
|
|
22
|
-
return true;
|
|
23
|
-
} catch (_error) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// src/interceptors/fetch/index.ts
|
|
29
|
-
var _FetchInterceptor = class extends Interceptor {
|
|
30
|
-
constructor() {
|
|
31
|
-
super(_FetchInterceptor.symbol);
|
|
32
|
-
}
|
|
33
|
-
checkEnvironment() {
|
|
34
|
-
return typeof globalThis !== "undefined" && typeof globalThis.fetch !== "undefined";
|
|
35
|
-
}
|
|
36
|
-
async setup() {
|
|
37
|
-
const pureFetch = globalThis.fetch;
|
|
38
|
-
invariant(
|
|
39
|
-
!pureFetch[IS_PATCHED_MODULE],
|
|
40
|
-
'Failed to patch the "fetch" module: already patched.'
|
|
41
|
-
);
|
|
42
|
-
globalThis.fetch = async (input, init) => {
|
|
43
|
-
const requestId = createRequestId();
|
|
44
|
-
const resolvedInput = typeof input === "string" && typeof location !== "undefined" && !canParseUrl(input) ? new URL(input, location.origin) : input;
|
|
45
|
-
const request = new Request(resolvedInput, init);
|
|
46
|
-
const responsePromise = new DeferredPromise();
|
|
47
|
-
const controller = new RequestController(request);
|
|
48
|
-
this.logger.info("[%s] %s", request.method, request.url);
|
|
49
|
-
this.logger.info("awaiting for the mocked response...");
|
|
50
|
-
this.logger.info(
|
|
51
|
-
'emitting the "request" event for %s listener(s)...',
|
|
52
|
-
this.emitter.listenerCount("request")
|
|
53
|
-
);
|
|
54
|
-
const isRequestHandled = await handleRequest({
|
|
55
|
-
request,
|
|
56
|
-
requestId,
|
|
57
|
-
emitter: this.emitter,
|
|
58
|
-
controller,
|
|
59
|
-
onResponse: async (response) => {
|
|
60
|
-
this.logger.info("received mocked response!", {
|
|
61
|
-
response
|
|
62
|
-
});
|
|
63
|
-
if (this.emitter.listenerCount("response") > 0) {
|
|
64
|
-
this.logger.info('emitting the "response" event...');
|
|
65
|
-
await emitAsync(this.emitter, "response", {
|
|
66
|
-
// Clone the mocked response for the "response" event listener.
|
|
67
|
-
// This way, the listener can read the response and not lock its body
|
|
68
|
-
// for the actual fetch consumer.
|
|
69
|
-
response: response.clone(),
|
|
70
|
-
isMockedResponse: true,
|
|
71
|
-
request,
|
|
72
|
-
requestId
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
Object.defineProperty(response, "url", {
|
|
76
|
-
writable: false,
|
|
77
|
-
enumerable: true,
|
|
78
|
-
configurable: false,
|
|
79
|
-
value: request.url
|
|
80
|
-
});
|
|
81
|
-
responsePromise.resolve(response);
|
|
82
|
-
},
|
|
83
|
-
onRequestError: (response) => {
|
|
84
|
-
this.logger.info("request has errored!", { response });
|
|
85
|
-
responsePromise.reject(createNetworkError(response));
|
|
86
|
-
},
|
|
87
|
-
onError: (error) => {
|
|
88
|
-
this.logger.info("request has been aborted!", { error });
|
|
89
|
-
responsePromise.reject(error);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
if (isRequestHandled) {
|
|
93
|
-
this.logger.info("request has been handled, returning mock promise...");
|
|
94
|
-
return responsePromise;
|
|
95
|
-
}
|
|
96
|
-
this.logger.info(
|
|
97
|
-
"no mocked response received, performing request as-is..."
|
|
98
|
-
);
|
|
99
|
-
return pureFetch(request).then((response) => {
|
|
100
|
-
this.logger.info("original fetch performed", response);
|
|
101
|
-
if (this.emitter.listenerCount("response") > 0) {
|
|
102
|
-
this.logger.info('emitting the "response" event...');
|
|
103
|
-
const responseClone = response.clone();
|
|
104
|
-
this.emitter.emit("response", {
|
|
105
|
-
response: responseClone,
|
|
106
|
-
isMockedResponse: false,
|
|
107
|
-
request,
|
|
108
|
-
requestId
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
return response;
|
|
112
|
-
});
|
|
113
|
-
};
|
|
114
|
-
Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {
|
|
115
|
-
enumerable: true,
|
|
116
|
-
configurable: true,
|
|
117
|
-
value: true
|
|
118
|
-
});
|
|
119
|
-
this.subscriptions.push(() => {
|
|
120
|
-
Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {
|
|
121
|
-
value: void 0
|
|
122
|
-
});
|
|
123
|
-
globalThis.fetch = pureFetch;
|
|
124
|
-
this.logger.info(
|
|
125
|
-
'restored native "globalThis.fetch"!',
|
|
126
|
-
globalThis.fetch.name
|
|
127
|
-
);
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
var FetchInterceptor = _FetchInterceptor;
|
|
132
|
-
FetchInterceptor.symbol = Symbol("fetch");
|
|
133
|
-
function createNetworkError(cause) {
|
|
134
|
-
return Object.assign(new TypeError("Failed to fetch"), {
|
|
135
|
-
cause
|
|
136
|
-
});
|
|
137
|
-
}
|
|
2
|
+
FetchInterceptor
|
|
3
|
+
} from "../../chunk-RTGLFNO3.mjs";
|
|
4
|
+
import "../../chunk-BZ3Y7YV5.mjs";
|
|
5
|
+
import "../../chunk-KY3RJ2M3.mjs";
|
|
6
|
+
import "../../chunk-BUCULLYM.mjs";
|
|
138
7
|
export {
|
|
139
8
|
FetchInterceptor
|
|
140
9
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ClientRequestInterceptor } from '../interceptors/ClientRequest/index.js';
|
|
2
2
|
import { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest/index.js';
|
|
3
|
+
import { FetchInterceptor } from '../interceptors/fetch/index.js';
|
|
3
4
|
import '../Interceptor-a31b1217.js';
|
|
4
5
|
import '@open-draft/deferred-promise';
|
|
5
6
|
import '@open-draft/logger';
|
|
@@ -10,6 +11,6 @@ import 'node:net';
|
|
|
10
11
|
* The default preset provisions the interception of requests
|
|
11
12
|
* regardless of their type (http/https/XMLHttpRequest).
|
|
12
13
|
*/
|
|
13
|
-
declare const _default: readonly [ClientRequestInterceptor, XMLHttpRequestInterceptor];
|
|
14
|
+
declare const _default: readonly [ClientRequestInterceptor, XMLHttpRequestInterceptor, FetchInterceptor];
|
|
14
15
|
|
|
15
16
|
export { _default as default };
|
package/lib/node/presets/node.js
CHANGED
|
@@ -5,6 +5,9 @@ var _chunkI57YSVAVjs = require('../chunk-I57YSVAV.js');
|
|
|
5
5
|
|
|
6
6
|
var _chunkT34TGCMRjs = require('../chunk-T34TGCMR.js');
|
|
7
7
|
require('../chunk-LK6DILFK.js');
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
var _chunkUN335ZTDjs = require('../chunk-UN335ZTD.js');
|
|
8
11
|
require('../chunk-IDEEMJ3F.js');
|
|
9
12
|
require('../chunk-5WWNCLB3.js');
|
|
10
13
|
require('../chunk-YGM3BCJU.js');
|
|
@@ -12,7 +15,8 @@ require('../chunk-YGM3BCJU.js');
|
|
|
12
15
|
// src/presets/node.ts
|
|
13
16
|
var node_default = [
|
|
14
17
|
new (0, _chunkI57YSVAVjs.ClientRequestInterceptor)(),
|
|
15
|
-
new (0, _chunkT34TGCMRjs.XMLHttpRequestInterceptor)()
|
|
18
|
+
new (0, _chunkT34TGCMRjs.XMLHttpRequestInterceptor)(),
|
|
19
|
+
new (0, _chunkUN335ZTDjs.FetchInterceptor)()
|
|
16
20
|
];
|
|
17
21
|
|
|
18
22
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/presets/node.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/presets/node.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAQA,IAAO,eAAQ;AAAA,EACb,IAAI,yBAAyB;AAAA,EAC7B,IAAI,0BAA0B;AAAA,EAC9B,IAAI,iBAAiB;AACvB","sourcesContent":["import { ClientRequestInterceptor } from '../interceptors/ClientRequest'\nimport { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest'\nimport { FetchInterceptor } from '../interceptors/fetch'\n\n/**\n * The default preset provisions the interception of requests\n * regardless of their type (http/https/XMLHttpRequest).\n */\nexport default [\n new ClientRequestInterceptor(),\n new XMLHttpRequestInterceptor(),\n new FetchInterceptor(),\n] as const\n"]}
|
|
@@ -5,6 +5,9 @@ import {
|
|
|
5
5
|
XMLHttpRequestInterceptor
|
|
6
6
|
} from "../chunk-RJGP7FQP.mjs";
|
|
7
7
|
import "../chunk-6HYIRFX2.mjs";
|
|
8
|
+
import {
|
|
9
|
+
FetchInterceptor
|
|
10
|
+
} from "../chunk-RTGLFNO3.mjs";
|
|
8
11
|
import "../chunk-BZ3Y7YV5.mjs";
|
|
9
12
|
import "../chunk-KY3RJ2M3.mjs";
|
|
10
13
|
import "../chunk-BUCULLYM.mjs";
|
|
@@ -12,7 +15,8 @@ import "../chunk-BUCULLYM.mjs";
|
|
|
12
15
|
// src/presets/node.ts
|
|
13
16
|
var node_default = [
|
|
14
17
|
new ClientRequestInterceptor(),
|
|
15
|
-
new XMLHttpRequestInterceptor()
|
|
18
|
+
new XMLHttpRequestInterceptor(),
|
|
19
|
+
new FetchInterceptor()
|
|
16
20
|
];
|
|
17
21
|
export {
|
|
18
22
|
node_default as default
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/presets/node.ts"],"sourcesContent":["import { ClientRequestInterceptor } from '../interceptors/ClientRequest'\nimport { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest'\n\n/**\n * The default preset provisions the interception of requests\n * regardless of their type (http/https/XMLHttpRequest).\n */\nexport default [\n new ClientRequestInterceptor(),\n new XMLHttpRequestInterceptor(),\n] as const\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/presets/node.ts"],"sourcesContent":["import { ClientRequestInterceptor } from '../interceptors/ClientRequest'\nimport { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest'\nimport { FetchInterceptor } from '../interceptors/fetch'\n\n/**\n * The default preset provisions the interception of requests\n * regardless of their type (http/https/XMLHttpRequest).\n */\nexport default [\n new ClientRequestInterceptor(),\n new XMLHttpRequestInterceptor(),\n new FetchInterceptor(),\n] as const\n"],"mappings":";;;;;;;;;;;;;;;AAQA,IAAO,eAAQ;AAAA,EACb,IAAI,yBAAyB;AAAA,EAC7B,IAAI,0BAA0B;AAAA,EAC9B,IAAI,iBAAiB;AACvB;","names":[]}
|
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.
|
|
4
|
+
"version": "0.34.1",
|
|
5
5
|
"main": "./lib/node/index.js",
|
|
6
6
|
"module": "./lib/node/index.mjs",
|
|
7
7
|
"types": "./lib/node/index.d.ts",
|
|
@@ -2,6 +2,7 @@ import { invariant } from 'outvariant'
|
|
|
2
2
|
import type { WebSocketData } from './WebSocketTransport'
|
|
3
3
|
import { bindEvent } from './utils/bindEvent'
|
|
4
4
|
import { CloseEvent } from './utils/events'
|
|
5
|
+
import { DeferredPromise } from '@open-draft/deferred-promise'
|
|
5
6
|
|
|
6
7
|
export type WebSocketEventListener<
|
|
7
8
|
EventType extends WebSocketEventMap[keyof WebSocketEventMap] = Event
|
|
@@ -10,6 +11,7 @@ export type WebSocketEventListener<
|
|
|
10
11
|
const WEBSOCKET_CLOSE_CODE_RANGE_ERROR =
|
|
11
12
|
'InvalidAccessError: close code out of user configurable range'
|
|
12
13
|
|
|
14
|
+
export const kPassthroughPromise = Symbol('kPassthroughPromise')
|
|
13
15
|
export const kOnSend = Symbol('kOnSend')
|
|
14
16
|
export const kClose = Symbol('kClose')
|
|
15
17
|
|
|
@@ -37,6 +39,7 @@ export class WebSocketOverride extends EventTarget implements WebSocket {
|
|
|
37
39
|
private _onerror: WebSocketEventListener | null = null
|
|
38
40
|
private _onclose: WebSocketEventListener<CloseEvent> | null = null
|
|
39
41
|
|
|
42
|
+
private [kPassthroughPromise]: DeferredPromise<boolean>
|
|
40
43
|
private [kOnSend]?: (data: WebSocketData) => void
|
|
41
44
|
|
|
42
45
|
constructor(url: string | URL, protocols?: string | Array<string>) {
|
|
@@ -48,9 +51,15 @@ export class WebSocketOverride extends EventTarget implements WebSocket {
|
|
|
48
51
|
this.readyState = this.CONNECTING
|
|
49
52
|
this.bufferedAmount = 0
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
this[kPassthroughPromise] = new DeferredPromise<boolean>()
|
|
55
|
+
|
|
56
|
+
queueMicrotask(async () => {
|
|
57
|
+
if (await this[kPassthroughPromise]) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.readyState = this.OPEN
|
|
62
|
+
|
|
54
63
|
this.protocol =
|
|
55
64
|
typeof protocols === 'string'
|
|
56
65
|
? protocols
|
|
@@ -151,33 +160,35 @@ export class WebSocketOverride extends EventTarget implements WebSocket {
|
|
|
151
160
|
WEBSOCKET_CLOSE_CODE_RANGE_ERROR
|
|
152
161
|
)
|
|
153
162
|
|
|
163
|
+
this[kClose](code, reason)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private [kClose](
|
|
167
|
+
code: number = 1000,
|
|
168
|
+
reason?: string,
|
|
169
|
+
wasClean = true
|
|
170
|
+
): void {
|
|
171
|
+
/**
|
|
172
|
+
* @note Move this check here so that even internall closures,
|
|
173
|
+
* like those triggered by the `server` connection, are not
|
|
174
|
+
* performed twice.
|
|
175
|
+
*/
|
|
154
176
|
if (this.readyState === this.CLOSING || this.readyState === this.CLOSED) {
|
|
155
177
|
return
|
|
156
178
|
}
|
|
157
179
|
|
|
158
|
-
this[kClose](code, reason)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
private [kClose](code: number = 1000, reason?: string): void {
|
|
162
180
|
this.readyState = this.CLOSING
|
|
163
181
|
|
|
164
182
|
queueMicrotask(() => {
|
|
165
183
|
this.readyState = this.CLOSED
|
|
166
184
|
|
|
167
|
-
// Non-user-configurable close status codes
|
|
168
|
-
// represent connection termination and must
|
|
169
|
-
// emit the "error" event before closing.
|
|
170
|
-
if (code > 1000 && code <= 1015) {
|
|
171
|
-
this.dispatchEvent(bindEvent(this, new Event('error')))
|
|
172
|
-
}
|
|
173
|
-
|
|
174
185
|
this.dispatchEvent(
|
|
175
186
|
bindEvent(
|
|
176
187
|
this,
|
|
177
188
|
new CloseEvent('close', {
|
|
178
189
|
code,
|
|
179
190
|
reason,
|
|
180
|
-
wasClean
|
|
191
|
+
wasClean,
|
|
181
192
|
})
|
|
182
193
|
)
|
|
183
194
|
)
|
|
@@ -5,7 +5,12 @@ import {
|
|
|
5
5
|
} from './WebSocketClientConnection'
|
|
6
6
|
import { WebSocketServerConnection } from './WebSocketServerConnection'
|
|
7
7
|
import { WebSocketClassTransport } from './WebSocketClassTransport'
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
kClose,
|
|
10
|
+
kPassthroughPromise,
|
|
11
|
+
WebSocketOverride,
|
|
12
|
+
} from './WebSocketOverride'
|
|
13
|
+
import { bindEvent } from './utils/bindEvent'
|
|
9
14
|
|
|
10
15
|
export { type WebSocketData, WebSocketTransport } from './WebSocketTransport'
|
|
11
16
|
export {
|
|
@@ -82,20 +87,65 @@ export class WebSocketInterceptor extends Interceptor<WebSocketEventMap> {
|
|
|
82
87
|
// so the client can modify WebSocket options, like "binaryType"
|
|
83
88
|
// while the connection is already pending.
|
|
84
89
|
queueMicrotask(() => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// as soon as the WebSocket instance is constructed.
|
|
88
|
-
this.emitter.emit('connection', {
|
|
89
|
-
client: new WebSocketClientConnection(socket, transport),
|
|
90
|
-
server: new WebSocketServerConnection(
|
|
90
|
+
try {
|
|
91
|
+
const server = new WebSocketServerConnection(
|
|
91
92
|
socket,
|
|
92
93
|
transport,
|
|
93
94
|
createConnection
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
// The "globalThis.WebSocket" class stands for
|
|
98
|
+
// the client-side connection. Assume it's established
|
|
99
|
+
// as soon as the WebSocket instance is constructed.
|
|
100
|
+
const hasConnectionListeners = this.emitter.emit('connection', {
|
|
101
|
+
client: new WebSocketClientConnection(socket, transport),
|
|
102
|
+
server,
|
|
103
|
+
info: {
|
|
104
|
+
protocols,
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
if (hasConnectionListeners) {
|
|
109
|
+
socket[kPassthroughPromise].resolve(false)
|
|
110
|
+
} else {
|
|
111
|
+
socket[kPassthroughPromise].resolve(true)
|
|
112
|
+
|
|
113
|
+
server.connect()
|
|
114
|
+
|
|
115
|
+
// Forward the "open" event from the original server
|
|
116
|
+
// to the mock WebSocket client in the case of a passthrough connection.
|
|
117
|
+
server.addEventListener('open', () => {
|
|
118
|
+
socket.dispatchEvent(bindEvent(socket, new Event('open')))
|
|
119
|
+
|
|
120
|
+
// Forward the original connection protocol to the
|
|
121
|
+
// mock WebSocket client.
|
|
122
|
+
if (server['realWebSocket']) {
|
|
123
|
+
socket.protocol = server['realWebSocket'].protocol
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
} catch (error) {
|
|
128
|
+
/**
|
|
129
|
+
* @note Translate unhandled exceptions during the connection
|
|
130
|
+
* handling (i.e. interceptor exceptions) as WebSocket connection
|
|
131
|
+
* closures with error. This prevents from the exceptions occurring
|
|
132
|
+
* in `queueMicrotask` from being process-wide and uncatchable.
|
|
133
|
+
*/
|
|
134
|
+
if (error instanceof Error) {
|
|
135
|
+
socket.dispatchEvent(new Event('error'))
|
|
136
|
+
|
|
137
|
+
// No need to close the connection if it's already being closed.
|
|
138
|
+
// E.g. the interceptor called `client.close()` and then threw an error.
|
|
139
|
+
if (
|
|
140
|
+
socket.readyState !== WebSocket.CLOSING &&
|
|
141
|
+
socket.readyState !== WebSocket.CLOSED
|
|
142
|
+
) {
|
|
143
|
+
socket[kClose](1011, error.message, false)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.error(error)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
99
149
|
})
|
|
100
150
|
|
|
101
151
|
return socket
|
package/src/presets/node.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ClientRequestInterceptor } from '../interceptors/ClientRequest'
|
|
2
2
|
import { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest'
|
|
3
|
+
import { FetchInterceptor } from '../interceptors/fetch'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The default preset provisions the interception of requests
|
|
@@ -8,4 +9,5 @@ import { XMLHttpRequestInterceptor } from '../interceptors/XMLHttpRequest'
|
|
|
8
9
|
export default [
|
|
9
10
|
new ClientRequestInterceptor(),
|
|
10
11
|
new XMLHttpRequestInterceptor(),
|
|
12
|
+
new FetchInterceptor(),
|
|
11
13
|
] as const
|