msw 2.14.7 → 2.15.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/browser/index.js +1 -1
- package/lib/browser/index.mjs +1 -1
- package/lib/core/{HttpResponse-BTyqrRB6.d.mts → HttpResponse-BE_Y2Ak9.d.mts} +18 -0
- package/lib/core/{HttpResponse-BXyXQFMJ.d.ts → HttpResponse-DL-P1EeG.d.ts} +18 -0
- 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/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/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 +88 -23
- package/lib/core/handlers/RequestHandler.js.map +1 -1
- package/lib/core/handlers/RequestHandler.mjs +88 -23
- package/lib/core/handlers/RequestHandler.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 +2 -2
- package/lib/core/sse.d.ts +2 -2
- package/lib/core/sse.js +66 -48
- package/lib/core/sse.js.map +1 -1
- package/lib/core/sse.mjs +65 -47
- package/lib/core/sse.mjs.map +1 -1
- package/lib/core/utils/HttpResponse/decorators.d.mts +8 -3
- package/lib/core/utils/HttpResponse/decorators.d.ts +8 -3
- package/lib/core/utils/HttpResponse/decorators.js +13 -0
- package/lib/core/utils/HttpResponse/decorators.js.map +1 -1
- package/lib/core/utils/HttpResponse/decorators.mjs +13 -0
- 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 +1 -1
- package/lib/core/utils/internal/attachSiblingHandlers.d.ts +1 -1
- 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/observe-response-body-stream.d.mts +18 -0
- package/lib/core/utils/internal/observe-response-body-stream.d.ts +18 -0
- package/lib/core/utils/internal/observe-response-body-stream.js +64 -0
- package/lib/core/utils/internal/observe-response-body-stream.js.map +1 -0
- package/lib/core/utils/internal/observe-response-body-stream.mjs +44 -0
- package/lib/core/utils/internal/observe-response-body-stream.mjs.map +1 -0
- 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/ws/handleWebSocketEvent.d.mts +1 -1
- package/lib/core/ws/handleWebSocketEvent.d.ts +1 -1
- package/lib/iife/index.js +270 -137
- package/lib/iife/index.js.map +1 -1
- package/lib/mockServiceWorker.js +21 -9
- package/package.json +2 -2
- package/src/core/handlers/HttpHandler.test.ts +89 -0
- package/src/core/handlers/RequestHandler.ts +142 -32
- package/src/core/sse.ts +85 -53
- package/src/core/utils/HttpResponse/decorators.ts +23 -2
- package/src/core/utils/internal/observe-response-body-stream.test.ts +64 -0
- package/src/core/utils/internal/observe-response-body-stream.ts +77 -0
- package/src/mockServiceWorker.js +19 -7
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { observeResponseBodyStream } from './observe-response-body-stream'
|
|
2
|
+
|
|
3
|
+
it('returns null for a response without a body', () => {
|
|
4
|
+
expect(observeResponseBodyStream(new Response(null))).toBeNull()
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
it('returns null for a response whose body is already used', async () => {
|
|
8
|
+
const response = new Response('hello')
|
|
9
|
+
await response.text()
|
|
10
|
+
|
|
11
|
+
expect(observeResponseBodyStream(response)).toBeNull()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('returns null for a response whose body is locked', () => {
|
|
15
|
+
const response = new Response('hello')
|
|
16
|
+
response.body?.getReader()
|
|
17
|
+
|
|
18
|
+
expect(observeResponseBodyStream(response)).toBeNull()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('resolves "settled" when the response body is read to completion', async () => {
|
|
22
|
+
const observed = observeResponseBodyStream(new Response('hello'))
|
|
23
|
+
expect(observed).not.toBeNull()
|
|
24
|
+
|
|
25
|
+
await expect(observed?.response.text()).resolves.toBe('hello')
|
|
26
|
+
await expect(observed?.settled).resolves.toBeUndefined()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('resolves "settled" when the response body errors', async () => {
|
|
30
|
+
const stream = new ReadableStream({
|
|
31
|
+
start(controller) {
|
|
32
|
+
controller.error(new Error('stream error'))
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
const observed = observeResponseBodyStream(new Response(stream))
|
|
36
|
+
expect(observed).not.toBeNull()
|
|
37
|
+
|
|
38
|
+
await expect(observed?.response.text()).rejects.toThrow('stream error')
|
|
39
|
+
await expect(observed?.settled).resolves.toBeUndefined()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('resolves "settled" when the response body is canceled', async () => {
|
|
43
|
+
const stream = new ReadableStream({
|
|
44
|
+
pull(controller) {
|
|
45
|
+
controller.enqueue(new TextEncoder().encode('ping'))
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
const observed = observeResponseBodyStream(new Response(stream))
|
|
49
|
+
const reader = observed?.response.body?.getReader()
|
|
50
|
+
expect(reader).toBeDefined()
|
|
51
|
+
|
|
52
|
+
await reader?.read()
|
|
53
|
+
await reader?.cancel(new Error('client disconnected'))
|
|
54
|
+
|
|
55
|
+
await expect(observed?.settled).resolves.toBeUndefined()
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('resolves "settled" when the response body is consumed via piping', async () => {
|
|
59
|
+
const observed = observeResponseBodyStream(new Response('hello'))
|
|
60
|
+
expect(observed).not.toBeNull()
|
|
61
|
+
|
|
62
|
+
await observed?.response.body?.pipeTo(new WritableStream())
|
|
63
|
+
await expect(observed?.settled).resolves.toBeUndefined()
|
|
64
|
+
})
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { DeferredPromise } from '@open-draft/deferred-promise'
|
|
2
|
+
import { FetchResponse } from '@mswjs/interceptors'
|
|
3
|
+
import { copyResponseOwnProperties } from '../HttpResponse/decorators'
|
|
4
|
+
|
|
5
|
+
export interface ObservedResponse {
|
|
6
|
+
response: Response
|
|
7
|
+
/**
|
|
8
|
+
* A promise that resolves once the response body stream has settled:
|
|
9
|
+
* it was closed, errored, or canceled, and will not be used anymore.
|
|
10
|
+
*/
|
|
11
|
+
settled: Promise<void>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Observe the `ReadableStream` body of the given response.
|
|
16
|
+
* Returns a copy of that response whose body reports when it has
|
|
17
|
+
* settled (was read to completion, errored, or canceled by the consumer).
|
|
18
|
+
* Returns `null` for responses whose body cannot be observed
|
|
19
|
+
* (no body, already used, or locked).
|
|
20
|
+
*/
|
|
21
|
+
export function observeResponseBodyStream(
|
|
22
|
+
response: Response,
|
|
23
|
+
): ObservedResponse | null {
|
|
24
|
+
if (response.body == null || response.bodyUsed || response.body.locked) {
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const settled = new DeferredPromise<void>()
|
|
29
|
+
const reader = response.body.getReader()
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @note Relay the body through a manual underlying source instead of
|
|
33
|
+
* `.pipeThrough(new TransformStream({ flush, cancel }))`. The `cancel`
|
|
34
|
+
* transformer callback is not implemented in Chromium, which loses
|
|
35
|
+
* the stream error/cancelation signals there entirely.
|
|
36
|
+
*/
|
|
37
|
+
const observedStream = new ReadableStream<Uint8Array>({
|
|
38
|
+
async pull(controller) {
|
|
39
|
+
try {
|
|
40
|
+
const readResult = await reader.read()
|
|
41
|
+
|
|
42
|
+
if (readResult.done) {
|
|
43
|
+
settled.resolve()
|
|
44
|
+
controller.close()
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
controller.enqueue(readResult.value)
|
|
49
|
+
} catch (error) {
|
|
50
|
+
settled.resolve()
|
|
51
|
+
throw error
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
async cancel(reason) {
|
|
55
|
+
settled.resolve()
|
|
56
|
+
await reader.cancel(reason)
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @note Reconstruct the response because the body of an existing
|
|
62
|
+
* response cannot be replaced. Use `FetchResponse` to support
|
|
63
|
+
* non-standard response status codes (e.g. 101).
|
|
64
|
+
*/
|
|
65
|
+
const observedResponse = new FetchResponse(observedStream, {
|
|
66
|
+
status: response.status,
|
|
67
|
+
statusText: response.statusText,
|
|
68
|
+
headers: response.headers,
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
copyResponseOwnProperties(response, observedResponse)
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
response: observedResponse,
|
|
75
|
+
settled,
|
|
76
|
+
}
|
|
77
|
+
}
|
package/src/mockServiceWorker.js
CHANGED
|
@@ -137,8 +137,18 @@ async function handleRequest(event, requestId, requestInterceptedAt) {
|
|
|
137
137
|
if (client && activeClientIds.has(client.id)) {
|
|
138
138
|
const serializedRequest = await serializeRequest(requestCloneForEvents)
|
|
139
139
|
|
|
140
|
+
// Omit the body of server-sent event stream responses.
|
|
141
|
+
// Cloning such responses would prevent client-side stream cancelations
|
|
142
|
+
// from reaching the original stream (a teed stream only cancels its
|
|
143
|
+
// source once both of its branches cancel) and would buffer the
|
|
144
|
+
// entire stream into the unconsumed clone indefinitely.
|
|
145
|
+
const isEventStreamResponse = response.headers
|
|
146
|
+
.get('content-type')
|
|
147
|
+
?.toLowerCase()
|
|
148
|
+
.startsWith('text/event-stream')
|
|
149
|
+
|
|
140
150
|
// Clone the response so both the client and the library could consume it.
|
|
141
|
-
const responseClone = response.clone()
|
|
151
|
+
const responseClone = isEventStreamResponse ? null : response.clone()
|
|
142
152
|
|
|
143
153
|
sendToClient(
|
|
144
154
|
client,
|
|
@@ -151,15 +161,17 @@ async function handleRequest(event, requestId, requestInterceptedAt) {
|
|
|
151
161
|
...serializedRequest,
|
|
152
162
|
},
|
|
153
163
|
response: {
|
|
154
|
-
type:
|
|
155
|
-
status:
|
|
156
|
-
statusText:
|
|
157
|
-
headers: Object.fromEntries(
|
|
158
|
-
body: responseClone.body,
|
|
164
|
+
type: response.type,
|
|
165
|
+
status: response.status,
|
|
166
|
+
statusText: response.statusText,
|
|
167
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
168
|
+
body: responseClone ? responseClone.body : null,
|
|
159
169
|
},
|
|
160
170
|
},
|
|
161
171
|
},
|
|
162
|
-
responseClone
|
|
172
|
+
responseClone && responseClone.body
|
|
173
|
+
? [serializedRequest.body, responseClone.body]
|
|
174
|
+
: [],
|
|
163
175
|
)
|
|
164
176
|
}
|
|
165
177
|
|