msw 2.14.6 → 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.
Files changed (116) hide show
  1. package/README.md +5 -5
  2. package/lib/browser/index.js +233 -93
  3. package/lib/browser/index.js.map +1 -1
  4. package/lib/browser/index.mjs +233 -93
  5. package/lib/browser/index.mjs.map +1 -1
  6. package/lib/core/{HttpResponse-CQwYpuKo.d.mts → HttpResponse-BE_Y2Ak9.d.mts} +19 -1
  7. package/lib/core/{HttpResponse-BFS34nkx.d.ts → HttpResponse-DL-P1EeG.d.ts} +19 -1
  8. package/lib/core/HttpResponse.d.mts +1 -1
  9. package/lib/core/HttpResponse.d.ts +1 -1
  10. package/lib/core/HttpResponse.js +2 -2
  11. package/lib/core/HttpResponse.js.map +1 -1
  12. package/lib/core/HttpResponse.mjs +2 -2
  13. package/lib/core/HttpResponse.mjs.map +1 -1
  14. package/lib/core/experimental/compat.d.mts +2 -2
  15. package/lib/core/experimental/compat.d.ts +2 -2
  16. package/lib/core/experimental/define-network.d.mts +2 -2
  17. package/lib/core/experimental/define-network.d.ts +2 -2
  18. package/lib/core/experimental/frames/http-frame.d.mts +2 -2
  19. package/lib/core/experimental/frames/http-frame.d.ts +2 -2
  20. package/lib/core/experimental/frames/network-frame.d.mts +2 -2
  21. package/lib/core/experimental/frames/network-frame.d.ts +2 -2
  22. package/lib/core/experimental/frames/network-frame.js +2 -0
  23. package/lib/core/experimental/frames/network-frame.js.map +1 -1
  24. package/lib/core/experimental/frames/network-frame.mjs +2 -0
  25. package/lib/core/experimental/frames/network-frame.mjs.map +1 -1
  26. package/lib/core/experimental/frames/websocket-frame.d.mts +2 -2
  27. package/lib/core/experimental/frames/websocket-frame.d.ts +2 -2
  28. package/lib/core/experimental/handlers-controller.d.mts +1 -1
  29. package/lib/core/experimental/handlers-controller.d.ts +1 -1
  30. package/lib/core/experimental/index.d.mts +2 -2
  31. package/lib/core/experimental/index.d.ts +2 -2
  32. package/lib/core/experimental/on-unhandled-frame.d.mts +2 -2
  33. package/lib/core/experimental/on-unhandled-frame.d.ts +2 -2
  34. package/lib/core/experimental/setup-api.d.mts +1 -1
  35. package/lib/core/experimental/setup-api.d.ts +1 -1
  36. package/lib/core/experimental/sources/interceptor-source.d.mts +2 -2
  37. package/lib/core/experimental/sources/interceptor-source.d.ts +2 -2
  38. package/lib/core/experimental/sources/network-source.d.mts +2 -2
  39. package/lib/core/experimental/sources/network-source.d.ts +2 -2
  40. package/lib/core/getResponse.d.mts +1 -1
  41. package/lib/core/getResponse.d.ts +1 -1
  42. package/lib/core/graphql.d.mts +1 -1
  43. package/lib/core/graphql.d.ts +1 -1
  44. package/lib/core/handlers/GraphQLHandler.d.mts +1 -1
  45. package/lib/core/handlers/GraphQLHandler.d.ts +1 -1
  46. package/lib/core/handlers/HttpHandler.d.mts +1 -1
  47. package/lib/core/handlers/HttpHandler.d.ts +1 -1
  48. package/lib/core/handlers/RequestHandler.d.mts +1 -1
  49. package/lib/core/handlers/RequestHandler.d.ts +1 -1
  50. package/lib/core/handlers/RequestHandler.js +88 -23
  51. package/lib/core/handlers/RequestHandler.js.map +1 -1
  52. package/lib/core/handlers/RequestHandler.mjs +88 -23
  53. package/lib/core/handlers/RequestHandler.mjs.map +1 -1
  54. package/lib/core/handlers/WebSocketHandler.js +7 -6
  55. package/lib/core/handlers/WebSocketHandler.js.map +1 -1
  56. package/lib/core/handlers/WebSocketHandler.mjs +7 -6
  57. package/lib/core/handlers/WebSocketHandler.mjs.map +1 -1
  58. package/lib/core/http.d.mts +1 -1
  59. package/lib/core/http.d.ts +1 -1
  60. package/lib/core/index.d.mts +1 -1
  61. package/lib/core/index.d.ts +1 -1
  62. package/lib/core/{on-unhandled-frame-BBR-P3kV.d.ts → on-unhandled-frame-0KmwUmeV.d.ts} +1 -1
  63. package/lib/core/{on-unhandled-frame-Cr1KOZ0I.d.mts → on-unhandled-frame-gkokW4Rr.d.mts} +1 -1
  64. package/lib/core/passthrough.d.mts +1 -1
  65. package/lib/core/passthrough.d.ts +1 -1
  66. package/lib/core/sse.d.mts +2 -2
  67. package/lib/core/sse.d.ts +2 -2
  68. package/lib/core/sse.js +83 -60
  69. package/lib/core/sse.js.map +1 -1
  70. package/lib/core/sse.mjs +83 -60
  71. package/lib/core/sse.mjs.map +1 -1
  72. package/lib/core/utils/HttpResponse/decorators.d.mts +8 -3
  73. package/lib/core/utils/HttpResponse/decorators.d.ts +8 -3
  74. package/lib/core/utils/HttpResponse/decorators.js +14 -1
  75. package/lib/core/utils/HttpResponse/decorators.js.map +1 -1
  76. package/lib/core/utils/HttpResponse/decorators.mjs +14 -1
  77. package/lib/core/utils/HttpResponse/decorators.mjs.map +1 -1
  78. package/lib/core/utils/executeHandlers.d.mts +1 -1
  79. package/lib/core/utils/executeHandlers.d.ts +1 -1
  80. package/lib/core/utils/handleRequest.d.mts +1 -1
  81. package/lib/core/utils/handleRequest.d.ts +1 -1
  82. package/lib/core/utils/internal/attachSiblingHandlers.d.mts +1 -1
  83. package/lib/core/utils/internal/attachSiblingHandlers.d.ts +1 -1
  84. package/lib/core/utils/internal/attachSiblingHandlers.js +1 -1
  85. package/lib/core/utils/internal/attachSiblingHandlers.js.map +1 -1
  86. package/lib/core/utils/internal/attachSiblingHandlers.mjs +1 -1
  87. package/lib/core/utils/internal/attachSiblingHandlers.mjs.map +1 -1
  88. package/lib/core/utils/internal/isHandlerKind.d.mts +1 -1
  89. package/lib/core/utils/internal/isHandlerKind.d.ts +1 -1
  90. package/lib/core/utils/internal/observe-response-body-stream.d.mts +18 -0
  91. package/lib/core/utils/internal/observe-response-body-stream.d.ts +18 -0
  92. package/lib/core/utils/internal/observe-response-body-stream.js +64 -0
  93. package/lib/core/utils/internal/observe-response-body-stream.js.map +1 -0
  94. package/lib/core/utils/internal/observe-response-body-stream.mjs +44 -0
  95. package/lib/core/utils/internal/observe-response-body-stream.mjs.map +1 -0
  96. package/lib/core/utils/internal/parseGraphQLRequest.d.mts +1 -1
  97. package/lib/core/utils/internal/parseGraphQLRequest.d.ts +1 -1
  98. package/lib/core/utils/internal/parseMultipartData.d.mts +1 -1
  99. package/lib/core/utils/internal/parseMultipartData.d.ts +1 -1
  100. package/lib/core/ws/WebSocketClientManager.js +4 -0
  101. package/lib/core/ws/WebSocketClientManager.js.map +1 -1
  102. package/lib/core/ws/WebSocketClientManager.mjs +4 -0
  103. package/lib/core/ws/WebSocketClientManager.mjs.map +1 -1
  104. package/lib/core/ws/handleWebSocketEvent.d.mts +1 -1
  105. package/lib/core/ws/handleWebSocketEvent.d.ts +1 -1
  106. package/lib/iife/index.js +4668 -1105
  107. package/lib/iife/index.js.map +1 -1
  108. package/lib/mockServiceWorker.js +21 -9
  109. package/package.json +4 -5
  110. package/src/core/handlers/HttpHandler.test.ts +89 -0
  111. package/src/core/handlers/RequestHandler.ts +142 -32
  112. package/src/core/sse.ts +100 -57
  113. package/src/core/utils/HttpResponse/decorators.ts +23 -2
  114. package/src/core/utils/internal/observe-response-body-stream.test.ts +64 -0
  115. package/src/core/utils/internal/observe-response-body-stream.ts +77 -0
  116. package/src/mockServiceWorker.js +19 -7
@@ -1,5 +1,5 @@
1
1
  import statuses from '../../../shims/statuses'
2
- import type { HttpResponseInit } from '../../HttpResponse'
2
+ import type { HttpResponse, HttpResponseInit } from '../../HttpResponse'
3
3
 
4
4
  const { message } = statuses
5
5
 
@@ -27,7 +27,7 @@ export function normalizeResponseInit(
27
27
  }
28
28
 
29
29
  export function decorateResponse(
30
- response: Response,
30
+ response: HttpResponse<any>,
31
31
  init: HttpResponseDecoratedInit,
32
32
  ): Response {
33
33
  // Allow mocking the response type.
@@ -59,3 +59,24 @@ export function decorateResponse(
59
59
  export function getRawSetCookie(response: Response): string | undefined {
60
60
  return Reflect.get(response, kSetCookie)
61
61
  }
62
+
63
+ /**
64
+ * Copy the given response own properties, like internal symbols,
65
+ * onto another response. Used for faithful internal copying of responses.
66
+ */
67
+ export function copyResponseOwnProperties(
68
+ source: Response,
69
+ target: Response,
70
+ ): void {
71
+ for (const propertyName of Reflect.ownKeys(source)) {
72
+ const descriptor = Object.getOwnPropertyDescriptor(source, propertyName)
73
+ const existingDescriptor = Object.getOwnPropertyDescriptor(
74
+ target,
75
+ propertyName,
76
+ )
77
+
78
+ if (descriptor && existingDescriptor == null) {
79
+ Object.defineProperty(target, propertyName, descriptor)
80
+ }
81
+ }
82
+ }
@@ -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
+ }
@@ -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: responseClone.type,
155
- status: responseClone.status,
156
- statusText: responseClone.statusText,
157
- headers: Object.fromEntries(responseClone.headers.entries()),
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.body ? [serializedRequest.body, responseClone.body] : [],
172
+ responseClone && responseClone.body
173
+ ? [serializedRequest.body, responseClone.body]
174
+ : [],
163
175
  )
164
176
  }
165
177