msw 2.13.5 → 2.14.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/lib/core/{HttpResponse-BMMzfpjG.d.mts → HttpResponse-CxHR1nNN.d.mts} +5 -1
- package/lib/core/{HttpResponse-DPDqE4Pb.d.ts → HttpResponse-aGiIzO91.d.ts} +5 -1
- package/lib/core/HttpResponse.d.mts +1 -1
- package/lib/core/HttpResponse.d.ts +1 -1
- package/lib/core/experimental/compat.d.mts +1 -1
- package/lib/core/experimental/compat.d.ts +1 -1
- package/lib/core/experimental/define-network.d.mts +1 -1
- package/lib/core/experimental/define-network.d.ts +1 -1
- package/lib/core/experimental/frames/http-frame.d.mts +1 -1
- package/lib/core/experimental/frames/http-frame.d.ts +1 -1
- package/lib/core/experimental/frames/http-frame.js +3 -2
- package/lib/core/experimental/frames/http-frame.js.map +1 -1
- package/lib/core/experimental/frames/http-frame.mjs +3 -2
- package/lib/core/experimental/frames/http-frame.mjs.map +1 -1
- package/lib/core/experimental/frames/network-frame.d.mts +1 -1
- package/lib/core/experimental/frames/network-frame.d.ts +1 -1
- package/lib/core/experimental/frames/websocket-frame.d.mts +1 -1
- package/lib/core/experimental/frames/websocket-frame.d.ts +1 -1
- package/lib/core/experimental/handlers-controller.d.mts +1 -1
- package/lib/core/experimental/handlers-controller.d.ts +1 -1
- package/lib/core/experimental/handlers-controller.js +16 -5
- package/lib/core/experimental/handlers-controller.js.map +1 -1
- package/lib/core/experimental/handlers-controller.mjs +16 -5
- package/lib/core/experimental/handlers-controller.mjs.map +1 -1
- package/lib/core/experimental/index.d.mts +1 -1
- package/lib/core/experimental/index.d.ts +1 -1
- package/lib/core/experimental/on-unhandled-frame.d.mts +1 -1
- package/lib/core/experimental/on-unhandled-frame.d.ts +1 -1
- package/lib/core/experimental/setup-api.d.mts +1 -1
- package/lib/core/experimental/setup-api.d.ts +1 -1
- package/lib/core/experimental/sources/interceptor-source.d.mts +1 -1
- package/lib/core/experimental/sources/interceptor-source.d.ts +1 -1
- package/lib/core/experimental/sources/network-source.d.mts +1 -1
- package/lib/core/experimental/sources/network-source.d.ts +1 -1
- package/lib/core/getResponse.d.mts +1 -1
- package/lib/core/getResponse.d.ts +1 -1
- package/lib/core/graphql.d.mts +1 -1
- package/lib/core/graphql.d.ts +1 -1
- package/lib/core/handlers/GraphQLHandler.d.mts +1 -1
- package/lib/core/handlers/GraphQLHandler.d.ts +1 -1
- package/lib/core/handlers/HttpHandler.d.mts +1 -1
- package/lib/core/handlers/HttpHandler.d.ts +1 -1
- package/lib/core/handlers/RequestHandler.d.mts +1 -1
- package/lib/core/handlers/RequestHandler.d.ts +1 -1
- package/lib/core/handlers/RequestHandler.js +22 -1
- package/lib/core/handlers/RequestHandler.js.map +1 -1
- package/lib/core/handlers/RequestHandler.mjs +22 -1
- package/lib/core/handlers/RequestHandler.mjs.map +1 -1
- package/lib/core/handlers/WebSocketHandler.d.mts +5 -2
- package/lib/core/handlers/WebSocketHandler.d.ts +5 -2
- package/lib/core/handlers/WebSocketHandler.js +23 -6
- package/lib/core/handlers/WebSocketHandler.js.map +1 -1
- package/lib/core/handlers/WebSocketHandler.mjs +23 -6
- package/lib/core/handlers/WebSocketHandler.mjs.map +1 -1
- package/lib/core/http.d.mts +1 -1
- package/lib/core/http.d.ts +1 -1
- package/lib/core/index.d.mts +1 -1
- package/lib/core/index.d.ts +1 -1
- package/lib/core/passthrough.d.mts +1 -1
- package/lib/core/passthrough.d.ts +1 -1
- package/lib/core/sse.d.mts +1 -1
- package/lib/core/sse.d.ts +1 -1
- package/lib/core/utils/HttpResponse/decorators.d.mts +3 -3
- package/lib/core/utils/HttpResponse/decorators.d.ts +3 -3
- package/lib/core/utils/HttpResponse/decorators.js +4 -10
- package/lib/core/utils/HttpResponse/decorators.js.map +1 -1
- package/lib/core/utils/HttpResponse/decorators.mjs +4 -10
- package/lib/core/utils/HttpResponse/decorators.mjs.map +1 -1
- package/lib/core/utils/executeHandlers.d.mts +1 -1
- package/lib/core/utils/executeHandlers.d.ts +1 -1
- package/lib/core/utils/handleRequest.d.mts +1 -1
- package/lib/core/utils/handleRequest.d.ts +1 -1
- package/lib/core/utils/internal/attachSiblingHandlers.d.mts +15 -0
- package/lib/core/utils/internal/attachSiblingHandlers.d.ts +15 -0
- package/lib/core/utils/internal/attachSiblingHandlers.js +44 -0
- package/lib/core/utils/internal/attachSiblingHandlers.js.map +1 -0
- package/lib/core/utils/internal/attachSiblingHandlers.mjs +24 -0
- package/lib/core/utils/internal/attachSiblingHandlers.mjs.map +1 -0
- package/lib/core/utils/internal/isHandlerKind.d.mts +1 -1
- package/lib/core/utils/internal/isHandlerKind.d.ts +1 -1
- package/lib/core/utils/internal/parseGraphQLRequest.d.mts +1 -1
- package/lib/core/utils/internal/parseGraphQLRequest.d.ts +1 -1
- package/lib/core/utils/internal/parseMultipartData.d.mts +1 -1
- package/lib/core/utils/internal/parseMultipartData.d.ts +1 -1
- package/lib/core/utils/request/storeResponseCookies.js +1 -1
- package/lib/core/utils/request/storeResponseCookies.js.map +1 -1
- package/lib/core/utils/request/storeResponseCookies.mjs +2 -2
- package/lib/core/utils/request/storeResponseCookies.mjs.map +1 -1
- package/lib/core/ws/handleWebSocketEvent.d.mts +1 -1
- package/lib/core/ws/handleWebSocketEvent.d.ts +1 -1
- package/lib/core/ws.d.mts +17 -4
- package/lib/core/ws.d.ts +17 -4
- package/lib/core/ws.js +30 -5
- package/lib/core/ws.js.map +1 -1
- package/lib/core/ws.mjs +34 -6
- package/lib/core/ws.mjs.map +1 -1
- package/lib/iife/index.js +1241 -1158
- package/lib/iife/index.js.map +1 -1
- package/lib/mockServiceWorker.js +1 -1
- package/package.json +2 -2
- package/src/core/experimental/frames/http-frame.test.ts +6 -1
- package/src/core/experimental/frames/http-frame.ts +6 -2
- package/src/core/experimental/handlers-controller.test.ts +139 -5
- package/src/core/experimental/handlers-controller.ts +24 -9
- package/src/core/handlers/RequestHandler.ts +36 -1
- package/src/core/handlers/WebSocketHandler.test.ts +58 -0
- package/src/core/handlers/WebSocketHandler.ts +37 -8
- package/src/core/utils/HttpResponse/decorators.ts +6 -21
- package/src/core/utils/internal/attachSiblingHandlers.ts +28 -0
- package/src/core/utils/request/storeResponseCookies.ts +2 -4
- package/src/core/ws.ts +65 -6
package/lib/mockServiceWorker.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - Please do NOT modify this file.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const PACKAGE_VERSION = '2.
|
|
10
|
+
const PACKAGE_VERSION = '2.14.0'
|
|
11
11
|
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
|
|
12
12
|
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
|
|
13
13
|
const activeClientIds = new Set()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "msw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "Seamless REST/GraphQL API mocking library for browser and Node.js.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./lib/core/index.js",
|
|
@@ -286,7 +286,7 @@
|
|
|
286
286
|
"vitest-environment-miniflare": "^2.14.4",
|
|
287
287
|
"webpack": "^5.106.2",
|
|
288
288
|
"webpack-http-server": "^0.5.0",
|
|
289
|
-
"msw": "^2.
|
|
289
|
+
"msw": "^2.14.0"
|
|
290
290
|
},
|
|
291
291
|
"peerDependencies": {
|
|
292
292
|
"typescript": ">= 4.8.x"
|
|
@@ -4,7 +4,8 @@ import { ws } from '../../ws'
|
|
|
4
4
|
import { bypass } from '../../bypass'
|
|
5
5
|
import type { HttpNetworkFrameEventMap } from './http-frame'
|
|
6
6
|
import { HttpNetworkFrame } from './http-frame'
|
|
7
|
-
import { InMemoryHandlersController } from '
|
|
7
|
+
import { InMemoryHandlersController } from '../../experimental/handlers-controller'
|
|
8
|
+
import { getSiblingHandlers } from '../../utils/internal/attachSiblingHandlers'
|
|
8
9
|
|
|
9
10
|
beforeAll(() => {
|
|
10
11
|
vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
@@ -46,6 +47,9 @@ it('filters only request type handlers', async () => {
|
|
|
46
47
|
const webSocketHandlers = [
|
|
47
48
|
ws.link('ws://localhost').addEventListener('connection', () => {}),
|
|
48
49
|
]
|
|
50
|
+
const webSocketSiblingHandlers = webSocketHandlers.flatMap((handler) =>
|
|
51
|
+
getSiblingHandlers(handler),
|
|
52
|
+
)
|
|
49
53
|
|
|
50
54
|
const controller = new InMemoryHandlersController([
|
|
51
55
|
...httpHandlers,
|
|
@@ -55,6 +59,7 @@ it('filters only request type handlers', async () => {
|
|
|
55
59
|
|
|
56
60
|
expect(frame.getHandlers(controller)).toEqual([
|
|
57
61
|
...httpHandlers,
|
|
62
|
+
...webSocketSiblingHandlers,
|
|
58
63
|
...graphqlHandlers,
|
|
59
64
|
])
|
|
60
65
|
expect(frame.getHandlers(new InMemoryHandlersController([]))).toEqual([])
|
|
@@ -251,9 +251,13 @@ export abstract class HttpNetworkFrame extends NetworkFrame<
|
|
|
251
251
|
return null
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
const responseCloneForLogs = resolutionContext?.quiet
|
|
255
|
+
? null
|
|
256
|
+
: response.clone()
|
|
257
|
+
|
|
254
258
|
await storeResponseCookies(request, response)
|
|
255
259
|
|
|
256
|
-
this.respondWith(response
|
|
260
|
+
this.respondWith(response)
|
|
257
261
|
|
|
258
262
|
this.events.emit(
|
|
259
263
|
new RequestEvent('request:end', {
|
|
@@ -265,7 +269,7 @@ export abstract class HttpNetworkFrame extends NetworkFrame<
|
|
|
265
269
|
if (!resolutionContext?.quiet) {
|
|
266
270
|
handler.log({
|
|
267
271
|
request: requestCloneForLogs!,
|
|
268
|
-
response
|
|
272
|
+
response: responseCloneForLogs!,
|
|
269
273
|
parsedResult,
|
|
270
274
|
})
|
|
271
275
|
}
|
|
@@ -1,8 +1,68 @@
|
|
|
1
1
|
import { http } from '../http'
|
|
2
2
|
import { graphql } from '../graphql'
|
|
3
3
|
import { ws } from '../ws'
|
|
4
|
+
import { getSiblingHandlers } from '../utils/internal/attachSiblingHandlers'
|
|
4
5
|
import { InMemoryHandlersController } from './handlers-controller'
|
|
5
6
|
|
|
7
|
+
describe('constructor', () => {
|
|
8
|
+
it('places the sibling in its own kind bucket', () => {
|
|
9
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
10
|
+
const [upgradeHandler] = getSiblingHandlers(wsHandler)
|
|
11
|
+
|
|
12
|
+
const controller = new InMemoryHandlersController([wsHandler])
|
|
13
|
+
|
|
14
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsHandler])
|
|
15
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('interleaves the sibling at the owner position when grouping by kind', () => {
|
|
19
|
+
const httpOne = http.get('/', () => {})
|
|
20
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
21
|
+
const [upgradeHandler] = getSiblingHandlers(wsHandler)
|
|
22
|
+
const httpTwo = http.get('/', () => {})
|
|
23
|
+
|
|
24
|
+
const controller = new InMemoryHandlersController([
|
|
25
|
+
httpOne,
|
|
26
|
+
wsHandler,
|
|
27
|
+
httpTwo,
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
expect(controller.getHandlersByKind('request')).toEqual([
|
|
31
|
+
httpOne,
|
|
32
|
+
upgradeHandler,
|
|
33
|
+
httpTwo,
|
|
34
|
+
])
|
|
35
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsHandler])
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('extracts siblings from every owner in the input list', () => {
|
|
39
|
+
const wsOne = ws.link('*').addEventListener('connection', () => {})
|
|
40
|
+
const wsTwo = ws.link('*').addEventListener('connection', () => {})
|
|
41
|
+
const [upgradeOne] = getSiblingHandlers(wsOne)
|
|
42
|
+
const [upgradeTwo] = getSiblingHandlers(wsTwo)
|
|
43
|
+
|
|
44
|
+
const controller = new InMemoryHandlersController([wsOne, wsTwo])
|
|
45
|
+
|
|
46
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsOne, wsTwo])
|
|
47
|
+
expect(controller.getHandlersByKind('request')).toEqual([
|
|
48
|
+
upgradeOne,
|
|
49
|
+
upgradeTwo,
|
|
50
|
+
])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('dedupes the shared upgrade sibling across multiple handlers from the same link', () => {
|
|
54
|
+
const chat = ws.link('*')
|
|
55
|
+
const wsOne = chat.addEventListener('connection', () => {})
|
|
56
|
+
const wsTwo = chat.addEventListener('connection', () => {})
|
|
57
|
+
const [upgradeHandler] = getSiblingHandlers(wsOne)
|
|
58
|
+
|
|
59
|
+
const controller = new InMemoryHandlersController([wsOne, wsTwo])
|
|
60
|
+
|
|
61
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsOne, wsTwo])
|
|
62
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
6
66
|
describe(InMemoryHandlersController.prototype.use, () => {
|
|
7
67
|
it('prepends a handler to an empty controller', () => {
|
|
8
68
|
const controller = new InMemoryHandlersController([])
|
|
@@ -51,6 +111,44 @@ describe(InMemoryHandlersController.prototype.use, () => {
|
|
|
51
111
|
|
|
52
112
|
expect(controller.currentHandlers()).toEqual([graphqlOne, httpTwo, httpOne])
|
|
53
113
|
})
|
|
114
|
+
|
|
115
|
+
it('propagates siblings to their kind buckets at runtime', () => {
|
|
116
|
+
const controller = new InMemoryHandlersController([])
|
|
117
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
118
|
+
const [upgradeHandler] = getSiblingHandlers(wsHandler)
|
|
119
|
+
|
|
120
|
+
controller.use([wsHandler])
|
|
121
|
+
|
|
122
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsHandler])
|
|
123
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('prepends incoming siblings before existing handlers of the same kind', () => {
|
|
127
|
+
const existingHttp = http.get('/existing', () => {})
|
|
128
|
+
const controller = new InMemoryHandlersController([existingHttp])
|
|
129
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
130
|
+
const [upgradeHandler] = getSiblingHandlers(wsHandler)
|
|
131
|
+
|
|
132
|
+
controller.use([wsHandler])
|
|
133
|
+
|
|
134
|
+
expect(controller.getHandlersByKind('request')).toEqual([
|
|
135
|
+
upgradeHandler,
|
|
136
|
+
existingHttp,
|
|
137
|
+
])
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('dedupes the shared upgrade sibling when called with multiple handlers from the same link', () => {
|
|
141
|
+
const chat = ws.link('*')
|
|
142
|
+
const wsOne = chat.addEventListener('connection', () => {})
|
|
143
|
+
const wsTwo = chat.addEventListener('connection', () => {})
|
|
144
|
+
const [upgradeHandler] = getSiblingHandlers(wsOne)
|
|
145
|
+
|
|
146
|
+
const controller = new InMemoryHandlersController([])
|
|
147
|
+
controller.use([wsOne, wsTwo])
|
|
148
|
+
|
|
149
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsOne, wsTwo])
|
|
150
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
151
|
+
})
|
|
54
152
|
})
|
|
55
153
|
|
|
56
154
|
describe(InMemoryHandlersController.prototype.reset, () => {
|
|
@@ -96,6 +194,42 @@ describe(InMemoryHandlersController.prototype.reset, () => {
|
|
|
96
194
|
*/
|
|
97
195
|
expect(controller.currentHandlers()).toEqual([httpTwo])
|
|
98
196
|
})
|
|
197
|
+
|
|
198
|
+
it('places siblings into their kind buckets when resetting to next handlers', () => {
|
|
199
|
+
const controller = new InMemoryHandlersController([])
|
|
200
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
201
|
+
const [upgradeHandler] = getSiblingHandlers(wsHandler)
|
|
202
|
+
|
|
203
|
+
controller.reset([wsHandler])
|
|
204
|
+
|
|
205
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsHandler])
|
|
206
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
it('restores siblings when resetting to the initial handlers', () => {
|
|
210
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
211
|
+
const [upgradeHandler] = getSiblingHandlers(wsHandler)
|
|
212
|
+
const controller = new InMemoryHandlersController([wsHandler])
|
|
213
|
+
|
|
214
|
+
controller.use([http.get('/runtime', () => {})])
|
|
215
|
+
controller.reset([])
|
|
216
|
+
|
|
217
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsHandler])
|
|
218
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it('dedupes the shared upgrade sibling when reset with multiple handlers from the same link', () => {
|
|
222
|
+
const chat = ws.link('*')
|
|
223
|
+
const wsOne = chat.addEventListener('connection', () => {})
|
|
224
|
+
const wsTwo = chat.addEventListener('connection', () => {})
|
|
225
|
+
const [upgradeHandler] = getSiblingHandlers(wsOne)
|
|
226
|
+
|
|
227
|
+
const controller = new InMemoryHandlersController([])
|
|
228
|
+
controller.reset([wsOne, wsTwo])
|
|
229
|
+
|
|
230
|
+
expect(controller.getHandlersByKind('websocket')).toEqual([wsOne, wsTwo])
|
|
231
|
+
expect(controller.getHandlersByKind('request')).toEqual([upgradeHandler])
|
|
232
|
+
})
|
|
99
233
|
})
|
|
100
234
|
|
|
101
235
|
describe(InMemoryHandlersController.prototype.getHandlersByKind, () => {
|
|
@@ -112,11 +246,10 @@ describe(InMemoryHandlersController.prototype.getHandlersByKind, () => {
|
|
|
112
246
|
]).getHandlersByKind('websocket'),
|
|
113
247
|
).toEqual([])
|
|
114
248
|
|
|
249
|
+
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
115
250
|
expect(
|
|
116
|
-
new InMemoryHandlersController([
|
|
117
|
-
|
|
118
|
-
]).getHandlersByKind('request'),
|
|
119
|
-
).toEqual([])
|
|
251
|
+
new InMemoryHandlersController([wsHandler]).getHandlersByKind('request'),
|
|
252
|
+
).toEqual(getSiblingHandlers(wsHandler))
|
|
120
253
|
})
|
|
121
254
|
|
|
122
255
|
it('returns all handlers if they all match', () => {
|
|
@@ -142,6 +275,7 @@ describe(InMemoryHandlersController.prototype.getHandlersByKind, () => {
|
|
|
142
275
|
const httpHandler = http.get('/', () => {})
|
|
143
276
|
const graphqlHandler = graphql.query('', () => {})
|
|
144
277
|
const wsHandler = ws.link('*').addEventListener('connection', () => {})
|
|
278
|
+
const wsHandlerSiblings = getSiblingHandlers(wsHandler)
|
|
145
279
|
|
|
146
280
|
expect(
|
|
147
281
|
new InMemoryHandlersController([
|
|
@@ -149,7 +283,7 @@ describe(InMemoryHandlersController.prototype.getHandlersByKind, () => {
|
|
|
149
283
|
graphqlHandler,
|
|
150
284
|
wsHandler,
|
|
151
285
|
]).getHandlersByKind('request'),
|
|
152
|
-
).toEqual([httpHandler, graphqlHandler])
|
|
286
|
+
).toEqual([httpHandler, graphqlHandler, ...wsHandlerSiblings])
|
|
153
287
|
|
|
154
288
|
expect(
|
|
155
289
|
new InMemoryHandlersController([
|
|
@@ -2,6 +2,7 @@ import { invariant } from 'outvariant'
|
|
|
2
2
|
import { type RequestHandler } from '../handlers/RequestHandler'
|
|
3
3
|
import { type WebSocketHandler } from '../handlers/WebSocketHandler'
|
|
4
4
|
import { devUtils } from '../utils/internal/devUtils'
|
|
5
|
+
import { getSiblingHandlers } from '../utils/internal/attachSiblingHandlers'
|
|
5
6
|
|
|
6
7
|
export type AnyHandler = RequestHandler | WebSocketHandler
|
|
7
8
|
export type HandlersMap = Partial<Record<AnyHandler['kind'], Array<AnyHandler>>>
|
|
@@ -9,11 +10,23 @@ export type HandlersMap = Partial<Record<AnyHandler['kind'], Array<AnyHandler>>>
|
|
|
9
10
|
export function groupHandlersByKind(handlers: Array<AnyHandler>): HandlersMap {
|
|
10
11
|
const groups: HandlersMap = {}
|
|
11
12
|
|
|
13
|
+
const pushUnique = (kind: AnyHandler['kind'], handler: AnyHandler) => {
|
|
14
|
+
const bucket = (groups[kind] ||= [])
|
|
15
|
+
|
|
16
|
+
if (!bucket.includes(handler)) {
|
|
17
|
+
bucket.push(handler)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
/**
|
|
13
22
|
* @note `Object.groupBy` is not implemented in Node.js v20.
|
|
14
23
|
*/
|
|
15
24
|
for (const handler of handlers) {
|
|
16
|
-
|
|
25
|
+
pushUnique(handler.kind, handler)
|
|
26
|
+
|
|
27
|
+
for (const sibling of getSiblingHandlers(handler)) {
|
|
28
|
+
pushUnique(sibling.kind, sibling)
|
|
29
|
+
}
|
|
17
30
|
}
|
|
18
31
|
|
|
19
32
|
return groups
|
|
@@ -69,14 +82,16 @@ export abstract class HandlersController {
|
|
|
69
82
|
}
|
|
70
83
|
|
|
71
84
|
const { handlers } = this.getState()
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
const overrides = groupHandlersByKind(nextHandlers)
|
|
86
|
+
|
|
87
|
+
// Prepend overrides to their respective kind buckets so they take
|
|
88
|
+
// priority over existing handlers while preserving input order.
|
|
89
|
+
for (const kind in overrides) {
|
|
90
|
+
const overridesForKind = overrides[kind as AnyHandler['kind']]!
|
|
91
|
+
const existingForKind = handlers[kind as AnyHandler['kind']]
|
|
92
|
+
handlers[kind as AnyHandler['kind']] = existingForKind
|
|
93
|
+
? [...overridesForKind, ...existingForKind]
|
|
94
|
+
: overridesForKind
|
|
80
95
|
}
|
|
81
96
|
|
|
82
97
|
this.setState({ handlers })
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Headers as HeadersPolyfill } from 'headers-polyfill'
|
|
1
2
|
import { getCallFrame } from '../utils/internal/getCallFrame'
|
|
2
3
|
import {
|
|
3
4
|
isIterable,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
type DefaultUnsafeFetchResponse,
|
|
13
14
|
} from '../HttpResponse'
|
|
14
15
|
import type { GraphQLRequestBody } from './GraphQLHandler'
|
|
16
|
+
import { getRawSetCookie } from '../utils/HttpResponse/decorators'
|
|
15
17
|
|
|
16
18
|
export type DefaultRequestMultipartBody = Record<
|
|
17
19
|
string,
|
|
@@ -335,7 +337,7 @@ export abstract class RequestHandler<
|
|
|
335
337
|
...resolverExtras,
|
|
336
338
|
requestId: args.requestId,
|
|
337
339
|
request: args.request,
|
|
338
|
-
}) as Promise<Response>
|
|
340
|
+
}) as Promise<Response | undefined>
|
|
339
341
|
).catch((errorOrResponse) => {
|
|
340
342
|
// Allow throwing a Response instance in a response resolver.
|
|
341
343
|
if (errorOrResponse instanceof Response) {
|
|
@@ -348,6 +350,10 @@ export abstract class RequestHandler<
|
|
|
348
350
|
|
|
349
351
|
const mockedResponse = await mockedResponsePromise
|
|
350
352
|
|
|
353
|
+
if (mockedResponse) {
|
|
354
|
+
forwardResponseCookies(mockedResponse)
|
|
355
|
+
}
|
|
356
|
+
|
|
351
357
|
const executionResult = this.createExecutionResult({
|
|
352
358
|
// Pass the cloned request to the result so that logging
|
|
353
359
|
// and other consumers could read its body once more.
|
|
@@ -416,3 +422,32 @@ export abstract class RequestHandler<
|
|
|
416
422
|
}
|
|
417
423
|
}
|
|
418
424
|
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Forwards the cookies from the given response to `document.cookie`.
|
|
428
|
+
*/
|
|
429
|
+
export function forwardResponseCookies(response: Response): void {
|
|
430
|
+
// Cookie forwarding is only relevant in the browser.
|
|
431
|
+
if (typeof document === 'undefined') {
|
|
432
|
+
return
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const responseCookies = getRawSetCookie(response)
|
|
436
|
+
|
|
437
|
+
if (!responseCookies) {
|
|
438
|
+
return
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Write the mocked response cookies to the document.
|
|
442
|
+
// Use `headers-polyfill` to get the Set-Cookie header value correctly.
|
|
443
|
+
// This is an alternative until TypeScript 5.2
|
|
444
|
+
// and Node.js v20 become the minimum supported versions
|
|
445
|
+
// and "Headers.prototype.getSetCookie" can be used directly.
|
|
446
|
+
const allResponseCookies = HeadersPolyfill.prototype.getSetCookie.call(
|
|
447
|
+
new Headers([['set-cookie', responseCookies]]),
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
for (const cookieString of allResponseCookies) {
|
|
451
|
+
document.cookie = cookieString
|
|
452
|
+
}
|
|
453
|
+
}
|
|
@@ -125,3 +125,61 @@ describe('parse', () => {
|
|
|
125
125
|
})
|
|
126
126
|
})
|
|
127
127
|
})
|
|
128
|
+
|
|
129
|
+
describe('test', () => {
|
|
130
|
+
it('returns true for a matching string', () => {
|
|
131
|
+
expect(
|
|
132
|
+
new WebSocketHandler('ws://localhost/ws').test('ws://localhost/ws'),
|
|
133
|
+
).toBe(true)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('returns false for a non-matching string', () => {
|
|
137
|
+
expect(
|
|
138
|
+
new WebSocketHandler('ws://localhost/ws').test('ws://localhost/other'),
|
|
139
|
+
).toBe(false)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('returns true for a relative matching string', () => {
|
|
143
|
+
expect(
|
|
144
|
+
new WebSocketHandler('ws://localhost/ws').test('/ws', {
|
|
145
|
+
baseUrl: 'ws://localhost',
|
|
146
|
+
}),
|
|
147
|
+
).toBe(true)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('returns false for a relative non-matching string', () => {
|
|
151
|
+
expect(
|
|
152
|
+
new WebSocketHandler('ws://localhost/ws').test('/other', {
|
|
153
|
+
baseUrl: 'ws://localhost',
|
|
154
|
+
}),
|
|
155
|
+
).toBe(false)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('returns true for a matching URL', () => {
|
|
159
|
+
expect(
|
|
160
|
+
new WebSocketHandler('ws://localhost/ws').test(
|
|
161
|
+
new URL('ws://localhost/ws'),
|
|
162
|
+
),
|
|
163
|
+
).toBe(true)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('returns false for a non-matching URL', () => {
|
|
167
|
+
expect(
|
|
168
|
+
new WebSocketHandler('ws://localhost/ws').test(
|
|
169
|
+
new URL('ws://localhost/other'),
|
|
170
|
+
),
|
|
171
|
+
).toBe(false)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('returns true for a matching HTTP url string', () => {
|
|
175
|
+
expect(
|
|
176
|
+
new WebSocketHandler('ws://localhost/ws').test('http://localhost/ws'),
|
|
177
|
+
).toBe(true)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('returns false for a non-matching HTTP url string', () => {
|
|
181
|
+
expect(
|
|
182
|
+
new WebSocketHandler('ws://localhost/ws').test('http://localhost/other'),
|
|
183
|
+
).toBe(false)
|
|
184
|
+
})
|
|
185
|
+
})
|
|
@@ -57,7 +57,7 @@ export class WebSocketHandler {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
public parse(args: {
|
|
60
|
-
url: URL
|
|
60
|
+
url: string | URL
|
|
61
61
|
resolutionContext?: WebSocketResolutionContext
|
|
62
62
|
}): WebSocketHandlerParsedResult {
|
|
63
63
|
const clientUrl = new URL(args.url)
|
|
@@ -90,22 +90,26 @@ export class WebSocketHandler {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
public predicate(args: {
|
|
93
|
-
url: URL
|
|
93
|
+
url: string | URL
|
|
94
94
|
parsedResult: WebSocketHandlerParsedResult
|
|
95
95
|
}): boolean {
|
|
96
96
|
return args.parsedResult.match.matches
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
public test(
|
|
100
|
+
url: string | URL,
|
|
101
|
+
resolutionContext?: WebSocketResolutionContext & { strict?: boolean },
|
|
102
|
+
): boolean {
|
|
103
|
+
return this.#match(url, resolutionContext) != null
|
|
104
|
+
}
|
|
105
|
+
|
|
99
106
|
public async run(
|
|
100
107
|
connection: WebSocketConnectionData,
|
|
101
108
|
resolutionContext?: WebSocketResolutionContext,
|
|
102
109
|
): Promise<WebSocketHandlerConnection | null> {
|
|
103
|
-
const parsedResult = this.
|
|
104
|
-
url: connection.client.url,
|
|
105
|
-
resolutionContext,
|
|
106
|
-
})
|
|
110
|
+
const parsedResult = this.#match(connection.client.url, resolutionContext)
|
|
107
111
|
|
|
108
|
-
if (
|
|
112
|
+
if (parsedResult == null) {
|
|
109
113
|
return null
|
|
110
114
|
}
|
|
111
115
|
|
|
@@ -114,7 +118,7 @@ export class WebSocketHandler {
|
|
|
114
118
|
params: parsedResult.match.params || {},
|
|
115
119
|
}
|
|
116
120
|
|
|
117
|
-
if (resolutionContext?.[kAutoConnect]) {
|
|
121
|
+
if (resolutionContext?.[kAutoConnect] ?? true) {
|
|
118
122
|
if (this[kConnect](resolvedConnection)) {
|
|
119
123
|
return resolvedConnection
|
|
120
124
|
}
|
|
@@ -125,6 +129,31 @@ export class WebSocketHandler {
|
|
|
125
129
|
return resolvedConnection
|
|
126
130
|
}
|
|
127
131
|
|
|
132
|
+
#match(
|
|
133
|
+
url: string | URL,
|
|
134
|
+
resolutionContext?: WebSocketResolutionContext & { strict?: boolean },
|
|
135
|
+
): WebSocketHandlerParsedResult | null {
|
|
136
|
+
const resolvedUrl = this.#resolveWebSocketUrl(
|
|
137
|
+
url.toString(),
|
|
138
|
+
resolutionContext?.baseUrl,
|
|
139
|
+
)
|
|
140
|
+
const parsedResult = this.parse({
|
|
141
|
+
url: resolvedUrl,
|
|
142
|
+
resolutionContext,
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
if (
|
|
146
|
+
this.predicate({
|
|
147
|
+
url,
|
|
148
|
+
parsedResult,
|
|
149
|
+
})
|
|
150
|
+
) {
|
|
151
|
+
return parsedResult
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return null
|
|
155
|
+
}
|
|
156
|
+
|
|
128
157
|
protected [kConnect](connection: WebSocketHandlerConnection): boolean {
|
|
129
158
|
// Support `event.stopPropagation()` for various client/server events.
|
|
130
159
|
connection.client.addEventListener(
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import statuses from '../../../shims/statuses'
|
|
2
|
-
import { Headers as HeadersPolyfill } from 'headers-polyfill'
|
|
3
2
|
import type { HttpResponseInit } from '../../HttpResponse'
|
|
4
3
|
|
|
5
4
|
const { message } = statuses
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
const kSetCookie = Symbol('kSetCookie')
|
|
8
7
|
|
|
9
8
|
export interface HttpResponseDecoratedInit extends HttpResponseInit {
|
|
10
9
|
status: number
|
|
@@ -31,7 +30,7 @@ export function decorateResponse(
|
|
|
31
30
|
response: Response,
|
|
32
31
|
init: HttpResponseDecoratedInit,
|
|
33
32
|
): Response {
|
|
34
|
-
// Allow
|
|
33
|
+
// Allow mocking the response type.
|
|
35
34
|
if (init.type) {
|
|
36
35
|
Object.defineProperty(response, 'type', {
|
|
37
36
|
value: init.type,
|
|
@@ -52,25 +51,11 @@ export function decorateResponse(
|
|
|
52
51
|
enumerable: false,
|
|
53
52
|
writable: false,
|
|
54
53
|
})
|
|
55
|
-
|
|
56
|
-
// Cookie forwarding is only relevant in the browser.
|
|
57
|
-
if (typeof document !== 'undefined') {
|
|
58
|
-
// Write the mocked response cookies to the document.
|
|
59
|
-
// Use `headers-polyfill` to get the Set-Cookie header value correctly.
|
|
60
|
-
// This is an alternative until TypeScript 5.2
|
|
61
|
-
// and Node.js v20 become the minimum supported version
|
|
62
|
-
// and getSetCookie in Headers can be used directly.
|
|
63
|
-
const responseCookiePairs = HeadersPolyfill.prototype.getSetCookie.call(
|
|
64
|
-
init.headers,
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
for (const cookieString of responseCookiePairs) {
|
|
68
|
-
// No need to parse the cookie headers because it's defined
|
|
69
|
-
// as the valid cookie string to begin with.
|
|
70
|
-
document.cookie = cookieString
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
54
|
}
|
|
74
55
|
|
|
75
56
|
return response
|
|
76
57
|
}
|
|
58
|
+
|
|
59
|
+
export function getRawSetCookie(response: Response): string | undefined {
|
|
60
|
+
return Reflect.get(response, kSetCookie)
|
|
61
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { invariant } from 'outvariant'
|
|
2
|
+
import type { AnyHandler } from '../../experimental/handlers-controller'
|
|
3
|
+
|
|
4
|
+
const kSiblingHandlers = Symbol('kSiblingHandlers')
|
|
5
|
+
|
|
6
|
+
export function attachSiblingHandlers<T extends AnyHandler>(
|
|
7
|
+
owner: T,
|
|
8
|
+
siblings: Array<AnyHandler>,
|
|
9
|
+
): T {
|
|
10
|
+
invariant(
|
|
11
|
+
getSiblingHandlers(owner).length === 0,
|
|
12
|
+
'Failed to merge handlers: the owner "%s" handler is already merged',
|
|
13
|
+
owner.kind,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
Object.defineProperty(owner, kSiblingHandlers, {
|
|
17
|
+
value: siblings,
|
|
18
|
+
enumerable: false,
|
|
19
|
+
writable: false,
|
|
20
|
+
configurable: false,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
return owner
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getSiblingHandlers(owner: AnyHandler): Array<AnyHandler> {
|
|
27
|
+
return Reflect.get(owner, kSiblingHandlers) || []
|
|
28
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cookieStore } from '../cookieStore'
|
|
2
|
-
import {
|
|
2
|
+
import { getRawSetCookie } from '../HttpResponse/decorators'
|
|
3
3
|
|
|
4
4
|
export async function storeResponseCookies(
|
|
5
5
|
request: Request,
|
|
@@ -7,9 +7,7 @@ export async function storeResponseCookies(
|
|
|
7
7
|
): Promise<void> {
|
|
8
8
|
// Grab the raw "Set-Cookie" response header provided
|
|
9
9
|
// in the HeadersInit for this mocked response.
|
|
10
|
-
const responseCookies =
|
|
11
|
-
| string
|
|
12
|
-
| undefined
|
|
10
|
+
const responseCookies = getRawSetCookie(response)
|
|
13
11
|
|
|
14
12
|
if (responseCookies) {
|
|
15
13
|
await cookieStore.setCookie(responseCookies, request.url)
|