@depup/msw 2.13.2-depup.0 → 2.15.0-depup.6

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 (282) hide show
  1. package/README.md +9 -12
  2. package/changes.json +14 -26
  3. package/cli/init.js +1 -1
  4. package/lib/browser/index.js +547 -177
  5. package/lib/browser/index.js.map +1 -1
  6. package/lib/browser/index.mjs +547 -177
  7. package/lib/browser/index.mjs.map +1 -1
  8. package/lib/core/{HttpResponse-DlRR1D-f.d.mts → HttpResponse-BE_Y2Ak9.d.mts} +52 -1
  9. package/lib/core/{HttpResponse-CksOMVAa.d.ts → HttpResponse-DL-P1EeG.d.ts} +52 -1
  10. package/lib/core/HttpResponse.d.mts +1 -1
  11. package/lib/core/HttpResponse.d.ts +1 -1
  12. package/lib/core/HttpResponse.js +2 -2
  13. package/lib/core/HttpResponse.js.map +1 -1
  14. package/lib/core/HttpResponse.mjs +2 -2
  15. package/lib/core/HttpResponse.mjs.map +1 -1
  16. package/lib/core/experimental/compat.d.mts +2 -2
  17. package/lib/core/experimental/compat.d.ts +2 -2
  18. package/lib/core/experimental/compat.js +1 -0
  19. package/lib/core/experimental/compat.js.map +1 -1
  20. package/lib/core/experimental/compat.mjs +1 -0
  21. package/lib/core/experimental/compat.mjs.map +1 -1
  22. package/lib/core/experimental/define-network.d.mts +2 -2
  23. package/lib/core/experimental/define-network.d.ts +2 -2
  24. package/lib/core/experimental/define-network.js +21 -11
  25. package/lib/core/experimental/define-network.js.map +1 -1
  26. package/lib/core/experimental/define-network.mjs +23 -11
  27. package/lib/core/experimental/define-network.mjs.map +1 -1
  28. package/lib/core/experimental/frames/http-frame.d.mts +2 -2
  29. package/lib/core/experimental/frames/http-frame.d.ts +2 -2
  30. package/lib/core/experimental/frames/http-frame.js +5 -2
  31. package/lib/core/experimental/frames/http-frame.js.map +1 -1
  32. package/lib/core/experimental/frames/http-frame.mjs +8 -3
  33. package/lib/core/experimental/frames/http-frame.mjs.map +1 -1
  34. package/lib/core/experimental/frames/network-frame.d.mts +2 -2
  35. package/lib/core/experimental/frames/network-frame.d.ts +2 -2
  36. package/lib/core/experimental/frames/network-frame.js +2 -0
  37. package/lib/core/experimental/frames/network-frame.js.map +1 -1
  38. package/lib/core/experimental/frames/network-frame.mjs +2 -0
  39. package/lib/core/experimental/frames/network-frame.mjs.map +1 -1
  40. package/lib/core/experimental/frames/websocket-frame.d.mts +2 -2
  41. package/lib/core/experimental/frames/websocket-frame.d.ts +2 -2
  42. package/lib/core/experimental/frames/websocket-frame.js +2 -0
  43. package/lib/core/experimental/frames/websocket-frame.js.map +1 -1
  44. package/lib/core/experimental/frames/websocket-frame.mjs +2 -0
  45. package/lib/core/experimental/frames/websocket-frame.mjs.map +1 -1
  46. package/lib/core/experimental/handlers-controller.d.mts +2 -1
  47. package/lib/core/experimental/handlers-controller.d.ts +2 -1
  48. package/lib/core/experimental/handlers-controller.js +31 -6
  49. package/lib/core/experimental/handlers-controller.js.map +1 -1
  50. package/lib/core/experimental/handlers-controller.mjs +31 -6
  51. package/lib/core/experimental/handlers-controller.mjs.map +1 -1
  52. package/lib/core/experimental/index.d.mts +5 -5
  53. package/lib/core/experimental/index.d.ts +5 -5
  54. package/lib/core/experimental/index.js +3 -1
  55. package/lib/core/experimental/index.js.map +1 -1
  56. package/lib/core/experimental/index.mjs +9 -2
  57. package/lib/core/experimental/index.mjs.map +1 -1
  58. package/lib/core/experimental/on-unhandled-frame.d.mts +2 -2
  59. package/lib/core/experimental/on-unhandled-frame.d.ts +2 -2
  60. package/lib/core/experimental/on-unhandled-frame.js +1 -0
  61. package/lib/core/experimental/on-unhandled-frame.js.map +1 -1
  62. package/lib/core/experimental/on-unhandled-frame.mjs +1 -0
  63. package/lib/core/experimental/on-unhandled-frame.mjs.map +1 -1
  64. package/lib/core/experimental/setup-api.d.mts +1 -1
  65. package/lib/core/experimental/setup-api.d.ts +1 -1
  66. package/lib/core/experimental/setup-api.js +2 -5
  67. package/lib/core/experimental/setup-api.js.map +1 -1
  68. package/lib/core/experimental/setup-api.mjs +2 -5
  69. package/lib/core/experimental/setup-api.mjs.map +1 -1
  70. package/lib/core/experimental/sources/interceptor-source.d.mts +2 -2
  71. package/lib/core/experimental/sources/interceptor-source.d.ts +2 -2
  72. package/lib/core/experimental/sources/interceptor-source.js +23 -10
  73. package/lib/core/experimental/sources/interceptor-source.js.map +1 -1
  74. package/lib/core/experimental/sources/interceptor-source.mjs +24 -13
  75. package/lib/core/experimental/sources/interceptor-source.mjs.map +1 -1
  76. package/lib/core/experimental/sources/network-source.d.mts +3 -3
  77. package/lib/core/experimental/sources/network-source.d.ts +3 -3
  78. package/lib/core/experimental/sources/network-source.js +1 -0
  79. package/lib/core/experimental/sources/network-source.js.map +1 -1
  80. package/lib/core/experimental/sources/network-source.mjs +2 -0
  81. package/lib/core/experimental/sources/network-source.mjs.map +1 -1
  82. package/lib/core/getResponse.d.mts +1 -1
  83. package/lib/core/getResponse.d.ts +1 -1
  84. package/lib/core/graphql.d.mts +1 -1
  85. package/lib/core/graphql.d.ts +1 -1
  86. package/lib/core/graphql.js +1 -0
  87. package/lib/core/graphql.js.map +1 -1
  88. package/lib/core/graphql.mjs +2 -0
  89. package/lib/core/graphql.mjs.map +1 -1
  90. package/lib/core/handlers/GraphQLHandler.d.mts +1 -1
  91. package/lib/core/handlers/GraphQLHandler.d.ts +1 -1
  92. package/lib/core/handlers/GraphQLHandler.js +1 -0
  93. package/lib/core/handlers/GraphQLHandler.js.map +1 -1
  94. package/lib/core/handlers/GraphQLHandler.mjs +4 -1
  95. package/lib/core/handlers/GraphQLHandler.mjs.map +1 -1
  96. package/lib/core/handlers/HttpHandler.d.mts +1 -1
  97. package/lib/core/handlers/HttpHandler.d.ts +1 -1
  98. package/lib/core/handlers/HttpHandler.js +1 -0
  99. package/lib/core/handlers/HttpHandler.js.map +1 -1
  100. package/lib/core/handlers/HttpHandler.mjs +1 -0
  101. package/lib/core/handlers/HttpHandler.mjs.map +1 -1
  102. package/lib/core/handlers/RequestHandler.d.mts +1 -1
  103. package/lib/core/handlers/RequestHandler.d.ts +1 -1
  104. package/lib/core/handlers/RequestHandler.js +197 -4
  105. package/lib/core/handlers/RequestHandler.js.map +1 -1
  106. package/lib/core/handlers/RequestHandler.mjs +198 -4
  107. package/lib/core/handlers/RequestHandler.mjs.map +1 -1
  108. package/lib/core/handlers/WebSocketHandler.d.mts +5 -2
  109. package/lib/core/handlers/WebSocketHandler.d.ts +5 -2
  110. package/lib/core/handlers/WebSocketHandler.js +30 -12
  111. package/lib/core/handlers/WebSocketHandler.js.map +1 -1
  112. package/lib/core/handlers/WebSocketHandler.mjs +30 -12
  113. package/lib/core/handlers/WebSocketHandler.mjs.map +1 -1
  114. package/lib/core/http.d.mts +1 -1
  115. package/lib/core/http.d.ts +1 -1
  116. package/lib/core/http.js +1 -0
  117. package/lib/core/http.js.map +1 -1
  118. package/lib/core/http.mjs +2 -0
  119. package/lib/core/http.mjs.map +1 -1
  120. package/lib/core/index.d.mts +1 -1
  121. package/lib/core/index.d.ts +1 -1
  122. package/lib/core/{network-frame-usYiHS0K.d.ts → on-unhandled-frame-0KmwUmeV.d.ts} +13 -13
  123. package/lib/core/{network-frame-B7A0ggXE.d.mts → on-unhandled-frame-gkokW4Rr.d.mts} +13 -13
  124. package/lib/core/passthrough.d.mts +1 -1
  125. package/lib/core/passthrough.d.ts +1 -1
  126. package/lib/core/sse.d.mts +4 -18
  127. package/lib/core/sse.d.ts +4 -18
  128. package/lib/core/sse.js +149 -66
  129. package/lib/core/sse.js.map +1 -1
  130. package/lib/core/sse.mjs +149 -66
  131. package/lib/core/sse.mjs.map +1 -1
  132. package/lib/core/utils/HttpResponse/decorators.d.mts +9 -4
  133. package/lib/core/utils/HttpResponse/decorators.d.ts +9 -4
  134. package/lib/core/utils/HttpResponse/decorators.js +18 -11
  135. package/lib/core/utils/HttpResponse/decorators.js.map +1 -1
  136. package/lib/core/utils/HttpResponse/decorators.mjs +18 -11
  137. package/lib/core/utils/HttpResponse/decorators.mjs.map +1 -1
  138. package/lib/core/utils/cookieStore.js.map +1 -1
  139. package/lib/core/utils/cookieStore.mjs.map +1 -1
  140. package/lib/core/utils/executeHandlers.d.mts +1 -1
  141. package/lib/core/utils/executeHandlers.d.ts +1 -1
  142. package/lib/core/utils/executeHandlers.js +1 -0
  143. package/lib/core/utils/executeHandlers.js.map +1 -1
  144. package/lib/core/utils/executeHandlers.mjs +1 -0
  145. package/lib/core/utils/executeHandlers.mjs.map +1 -1
  146. package/lib/core/utils/handleRequest.d.mts +1 -1
  147. package/lib/core/utils/handleRequest.d.ts +1 -1
  148. package/lib/core/utils/handleRequest.js.map +1 -1
  149. package/lib/core/utils/handleRequest.mjs.map +1 -1
  150. package/lib/core/utils/internal/Disposable.js +19 -1
  151. package/lib/core/utils/internal/Disposable.js.map +1 -1
  152. package/lib/core/utils/internal/Disposable.mjs +19 -1
  153. package/lib/core/utils/internal/Disposable.mjs.map +1 -1
  154. package/lib/core/utils/internal/attachSiblingHandlers.d.mts +15 -0
  155. package/lib/core/utils/internal/attachSiblingHandlers.d.ts +15 -0
  156. package/lib/core/utils/internal/attachSiblingHandlers.js +44 -0
  157. package/lib/core/utils/internal/attachSiblingHandlers.js.map +1 -0
  158. package/lib/core/utils/internal/attachSiblingHandlers.mjs +24 -0
  159. package/lib/core/utils/internal/attachSiblingHandlers.mjs.map +1 -0
  160. package/lib/core/utils/internal/isHandlerKind.d.mts +1 -1
  161. package/lib/core/utils/internal/isHandlerKind.d.ts +1 -1
  162. package/lib/core/utils/internal/observe-response-body-stream.d.mts +18 -0
  163. package/lib/core/utils/internal/observe-response-body-stream.d.ts +18 -0
  164. package/lib/core/utils/internal/observe-response-body-stream.js +64 -0
  165. package/lib/core/utils/internal/observe-response-body-stream.js.map +1 -0
  166. package/lib/core/utils/internal/observe-response-body-stream.mjs +44 -0
  167. package/lib/core/utils/internal/observe-response-body-stream.mjs.map +1 -0
  168. package/lib/core/utils/internal/parseGraphQLRequest.d.mts +1 -1
  169. package/lib/core/utils/internal/parseGraphQLRequest.d.ts +1 -1
  170. package/lib/core/utils/internal/parseMultipartData.d.mts +1 -1
  171. package/lib/core/utils/internal/parseMultipartData.d.ts +1 -1
  172. package/lib/core/utils/internal/parseMultipartData.js +1 -0
  173. package/lib/core/utils/internal/parseMultipartData.js.map +1 -1
  174. package/lib/core/utils/internal/parseMultipartData.mjs +1 -0
  175. package/lib/core/utils/internal/parseMultipartData.mjs.map +1 -1
  176. package/lib/core/utils/internal/pipeEvents.js +1 -0
  177. package/lib/core/utils/internal/pipeEvents.js.map +1 -1
  178. package/lib/core/utils/internal/pipeEvents.mjs +1 -0
  179. package/lib/core/utils/internal/pipeEvents.mjs.map +1 -1
  180. package/lib/core/utils/request/storeResponseCookies.js +1 -1
  181. package/lib/core/utils/request/storeResponseCookies.js.map +1 -1
  182. package/lib/core/utils/request/storeResponseCookies.mjs +2 -2
  183. package/lib/core/utils/request/storeResponseCookies.mjs.map +1 -1
  184. package/lib/core/ws/WebSocketClientManager.js +4 -0
  185. package/lib/core/ws/WebSocketClientManager.js.map +1 -1
  186. package/lib/core/ws/WebSocketClientManager.mjs +4 -0
  187. package/lib/core/ws/WebSocketClientManager.mjs.map +1 -1
  188. package/lib/core/ws/WebSocketIndexedDBClientStore.js +1 -0
  189. package/lib/core/ws/WebSocketIndexedDBClientStore.js.map +1 -1
  190. package/lib/core/ws/WebSocketIndexedDBClientStore.mjs +1 -0
  191. package/lib/core/ws/WebSocketIndexedDBClientStore.mjs.map +1 -1
  192. package/lib/core/ws/WebSocketMemoryClientStore.js +1 -0
  193. package/lib/core/ws/WebSocketMemoryClientStore.js.map +1 -1
  194. package/lib/core/ws/WebSocketMemoryClientStore.mjs +1 -0
  195. package/lib/core/ws/WebSocketMemoryClientStore.mjs.map +1 -1
  196. package/lib/core/ws/handleWebSocketEvent.d.mts +1 -1
  197. package/lib/core/ws/handleWebSocketEvent.d.ts +1 -1
  198. package/lib/core/ws/handleWebSocketEvent.js.map +1 -1
  199. package/lib/core/ws/handleWebSocketEvent.mjs.map +1 -1
  200. package/lib/core/ws.d.mts +17 -4
  201. package/lib/core/ws.d.ts +17 -4
  202. package/lib/core/ws.js +30 -5
  203. package/lib/core/ws.js.map +1 -1
  204. package/lib/core/ws.mjs +34 -6
  205. package/lib/core/ws.mjs.map +1 -1
  206. package/lib/iife/index.js +10138 -6034
  207. package/lib/iife/index.js.map +1 -1
  208. package/lib/mockServiceWorker.js +21 -9
  209. package/lib/native/index.js.map +1 -1
  210. package/lib/native/index.mjs.map +1 -1
  211. package/lib/node/index.d.mts +1 -1
  212. package/lib/node/index.d.ts +1 -1
  213. package/lib/node/index.js +2 -0
  214. package/lib/node/index.js.map +1 -1
  215. package/lib/node/index.mjs +1 -0
  216. package/lib/node/index.mjs.map +1 -1
  217. package/lib/shims/cookie.js +152 -62
  218. package/lib/shims/cookie.mjs +152 -62
  219. package/package.json +50 -69
  220. package/src/browser/glossary.ts +1 -1
  221. package/src/browser/setup-worker.ts +2 -2
  222. package/src/browser/sources/service-worker-source.ts +142 -40
  223. package/src/browser/utils/deserializeRequest.ts +2 -2
  224. package/src/browser/utils/pruneGetRequestBody.ts +3 -3
  225. package/src/browser/utils/should-invalidate-worker.test.ts +122 -0
  226. package/src/browser/utils/should-invalidate-worker.ts +13 -0
  227. package/src/browser/utils/workerChannel.ts +43 -21
  228. package/src/core/experimental/define-network.ts +44 -15
  229. package/src/core/experimental/frames/http-frame.test.ts +8 -2
  230. package/src/core/experimental/frames/http-frame.ts +12 -4
  231. package/src/core/experimental/frames/websocket-frame.test.ts +2 -4
  232. package/src/core/experimental/frames/websocket-frame.ts +3 -2
  233. package/src/core/experimental/handlers-controller.test.ts +139 -5
  234. package/src/core/experimental/handlers-controller.ts +39 -10
  235. package/src/core/experimental/index.ts +11 -1
  236. package/src/core/experimental/on-unhandled-frame.test.ts +2 -4
  237. package/src/core/experimental/setup-api.ts +4 -8
  238. package/src/core/experimental/sources/interceptor-source.ts +36 -16
  239. package/src/core/experimental/sources/network-source.ts +1 -1
  240. package/src/core/graphql.ts +8 -8
  241. package/src/core/handlers/GraphQLHandler.test.ts +3 -4
  242. package/src/core/handlers/GraphQLHandler.ts +15 -11
  243. package/src/core/handlers/HttpHandler.test.ts +92 -2
  244. package/src/core/handlers/HttpHandler.ts +7 -7
  245. package/src/core/handlers/RequestHandler.ts +329 -22
  246. package/src/core/handlers/WebSocketHandler.test.ts +58 -0
  247. package/src/core/handlers/WebSocketHandler.ts +37 -8
  248. package/src/core/http.ts +5 -5
  249. package/src/core/sse.ts +217 -73
  250. package/src/core/utils/HttpResponse/decorators.ts +28 -22
  251. package/src/core/utils/cookieStore.ts +1 -1
  252. package/src/core/utils/executeHandlers.ts +2 -4
  253. package/src/core/utils/handleRequest.test.ts +5 -4
  254. package/src/core/utils/handleRequest.ts +3 -3
  255. package/src/core/utils/internal/Disposable.ts +22 -1
  256. package/src/core/utils/internal/attachSiblingHandlers.ts +28 -0
  257. package/src/core/utils/internal/observe-response-body-stream.test.ts +64 -0
  258. package/src/core/utils/internal/observe-response-body-stream.ts +77 -0
  259. package/src/core/utils/internal/parseGraphQLRequest.test.ts +2 -4
  260. package/src/core/utils/internal/parseMultipartData.ts +1 -1
  261. package/src/core/utils/internal/pipeEvents.ts +2 -1
  262. package/src/core/utils/request/onUnhandledRequest.test.ts +2 -4
  263. package/src/core/utils/request/storeResponseCookies.ts +2 -4
  264. package/src/core/ws/WebSocketClientManager.test.ts +2 -4
  265. package/src/core/ws/WebSocketClientManager.ts +1 -1
  266. package/src/core/ws/WebSocketIndexedDBClientStore.ts +3 -5
  267. package/src/core/ws/WebSocketMemoryClientStore.ts +3 -5
  268. package/src/core/ws/handleWebSocketEvent.ts +3 -3
  269. package/src/core/ws.ts +66 -7
  270. package/src/mockServiceWorker.js +19 -7
  271. package/src/native/index.ts +2 -2
  272. package/src/node/async-handlers-controller.ts +2 -2
  273. package/src/node/index.ts +1 -0
  274. package/src/node/setup-server-common.ts +4 -4
  275. package/src/node/setup-server.ts +2 -2
  276. package/lib/core/utils/internal/requestHandlerUtils.d.mts +0 -12
  277. package/lib/core/utils/internal/requestHandlerUtils.d.ts +0 -12
  278. package/lib/core/utils/internal/requestHandlerUtils.js +0 -37
  279. package/lib/core/utils/internal/requestHandlerUtils.js.map +0 -1
  280. package/lib/core/utils/internal/requestHandlerUtils.mjs +0 -17
  281. package/lib/core/utils/internal/requestHandlerUtils.mjs.map +0 -1
  282. package/src/core/utils/internal/requestHandlerUtils.ts +0 -21
@@ -1,16 +1,16 @@
1
1
  import type { OperationTypeNode } from 'graphql'
2
2
  import {
3
- ResponseResolver,
4
- RequestHandlerOptions,
3
+ type ResponseResolver,
4
+ type RequestHandlerOptions,
5
5
  } from './handlers/RequestHandler'
6
6
  import {
7
7
  GraphQLHandler,
8
- GraphQLVariables,
9
- GraphQLOperationType,
10
- GraphQLResolverExtras,
11
- GraphQLResponseBody,
12
- GraphQLQuery,
13
- GraphQLPredicate,
8
+ type GraphQLVariables,
9
+ type GraphQLOperationType,
10
+ type GraphQLResolverExtras,
11
+ type GraphQLResponseBody,
12
+ type GraphQLQuery,
13
+ type GraphQLPredicate,
14
14
  } from './handlers/GraphQLHandler'
15
15
  import type { Path } from './utils/matching/matchRequestUrl'
16
16
 
@@ -1,14 +1,13 @@
1
1
  // @vitest-environment jsdom
2
2
  import { createRequestId, encodeBuffer } from '@mswjs/interceptors'
3
3
  import { OperationTypeNode, parse } from 'graphql'
4
- import {
5
- GraphQLHandler,
4
+ import type {
6
5
  GraphQLRequestBody,
7
6
  GraphQLResolverExtras,
8
- isDocumentNode,
9
7
  } from './GraphQLHandler'
8
+ import { GraphQLHandler, isDocumentNode } from './GraphQLHandler'
10
9
  import { HttpResponse } from '../HttpResponse'
11
- import { ResponseResolver } from './RequestHandler'
10
+ import type { ResponseResolver } from './RequestHandler'
12
11
 
13
12
  const resolver: ResponseResolver<GraphQLResolverExtras<{ userId: string }>> = ({
14
13
  variables,
@@ -6,30 +6,34 @@ import {
6
6
  type OperationTypeNode,
7
7
  } from 'graphql'
8
8
  import {
9
- DefaultBodyType,
10
9
  RequestHandler,
11
- RequestHandlerDefaultInfo,
12
- RequestHandlerExecutionResult,
13
- RequestHandlerOptions,
14
- ResponseResolver,
10
+ type DefaultBodyType,
11
+ type RequestHandlerDefaultInfo,
12
+ type RequestHandlerExecutionResult,
13
+ type RequestHandlerOptions,
14
+ type ResponseResolver,
15
15
  } from './RequestHandler'
16
16
  import { getTimestamp } from '../utils/logging/getTimestamp'
17
17
  import { getStatusCodeColor } from '../utils/logging/getStatusCodeColor'
18
18
  import { serializeRequest } from '../utils/logging/serializeRequest'
19
19
  import { serializeResponse } from '../utils/logging/serializeResponse'
20
- import { Match, matchRequestUrl, Path } from '../utils/matching/matchRequestUrl'
21
20
  import {
22
- ParsedGraphQLRequest,
23
- GraphQLMultipartRequestBody,
21
+ type Match,
22
+ matchRequestUrl,
23
+ type Path,
24
+ } from '../utils/matching/matchRequestUrl'
25
+ import {
26
+ type ParsedGraphQLRequest,
27
+ type GraphQLMultipartRequestBody,
24
28
  parseGraphQLRequest,
25
29
  parseDocumentNode,
26
- ParsedGraphQLQuery,
30
+ type ParsedGraphQLQuery,
27
31
  } from '../utils/internal/parseGraphQLRequest'
28
32
  import { toPublicUrl } from '../utils/request/toPublicUrl'
29
33
  import { devUtils } from '../utils/internal/devUtils'
30
34
  import { getAllRequestCookies } from '../utils/request/getRequestCookies'
31
- import { ResponseResolutionContext } from 'src/iife'
32
- import { kDefaultContentType, StrictRequest } from '../HttpResponse'
35
+ import { type ResponseResolutionContext } from '../utils/executeHandlers'
36
+ import { kDefaultContentType, type StrictRequest } from '../HttpResponse'
33
37
  import { getAllAcceptedMimeTypes } from '../utils/request/getAllAcceptedMimeTypes'
34
38
 
35
39
  export interface DocumentTypeDecoration<
@@ -1,8 +1,9 @@
1
1
  // @vitest-environment jsdom
2
2
  import { createRequestId } from '@mswjs/interceptors'
3
- import { HttpHandler, HttpRequestResolverExtras } from './HttpHandler'
3
+ import type { HttpRequestResolverExtras } from './HttpHandler'
4
+ import { HttpHandler } from './HttpHandler'
4
5
  import { HttpResponse } from '..'
5
- import { ResponseResolver } from './RequestHandler'
6
+ import type { ResponseResolver } from './RequestHandler'
6
7
 
7
8
  const resolver: ResponseResolver<
8
9
  HttpRequestResolverExtras<{ userId: string }>
@@ -251,3 +252,92 @@ describe('run', () => {
251
252
  await expect(run()).resolves.toBe('complete')
252
253
  })
253
254
  })
255
+
256
+ describe('finalize', () => {
257
+ it('returns the exact response instance if the resolver never accesses finalize', async () => {
258
+ const response = new HttpResponse(
259
+ new ReadableStream({
260
+ start(controller) {
261
+ controller.enqueue(new TextEncoder().encode('hello'))
262
+ controller.close()
263
+ },
264
+ }),
265
+ )
266
+ const handler = new HttpHandler('GET', '/resource', () => {
267
+ return response
268
+ })
269
+
270
+ const result = await handler.run({
271
+ request: new Request(new URL('/resource', location.href)),
272
+ requestId: createRequestId(),
273
+ })
274
+
275
+ expect(result?.response).toBe(response)
276
+ })
277
+
278
+ it('returns the exact response instance if finalize is accessed but never called', async () => {
279
+ const response = new HttpResponse(
280
+ new ReadableStream({
281
+ start(controller) {
282
+ controller.close()
283
+ },
284
+ }),
285
+ )
286
+ const handler = new HttpHandler('GET', '/resource', ({ finalize }) => {
287
+ expect(finalize).toBeInstanceOf(Function)
288
+ return response
289
+ })
290
+
291
+ const result = await handler.run({
292
+ request: new Request(new URL('/resource', location.href)),
293
+ requestId: createRequestId(),
294
+ })
295
+
296
+ expect(result?.response).toBe(response)
297
+ })
298
+
299
+ it('defers the cleanup until the response stream settles', async () => {
300
+ const cleanup = vi.fn()
301
+ const response = new HttpResponse(
302
+ new ReadableStream({
303
+ start(controller) {
304
+ controller.enqueue(new TextEncoder().encode('hello'))
305
+ controller.close()
306
+ },
307
+ }),
308
+ )
309
+ const handler = new HttpHandler('GET', '/resource', ({ finalize }) => {
310
+ finalize(cleanup)
311
+ return response
312
+ })
313
+
314
+ const result = await handler.run({
315
+ request: new Request(new URL('/resource', location.href)),
316
+ requestId: createRequestId(),
317
+ })
318
+
319
+ // The observed response is a copy of the original response.
320
+ expect(result?.response).not.toBe(response)
321
+ expect(cleanup).not.toHaveBeenCalled()
322
+
323
+ await expect(result?.response?.text()).resolves.toBe('hello')
324
+ await vi.waitFor(() => {
325
+ expect(cleanup).toHaveBeenCalledOnce()
326
+ })
327
+ })
328
+
329
+ it('runs the cleanup immediately for responses without a body', async () => {
330
+ const cleanup = vi.fn()
331
+ const handler = new HttpHandler('GET', '/resource', ({ finalize }) => {
332
+ finalize(cleanup)
333
+ return new HttpResponse(null)
334
+ })
335
+
336
+ await handler.run({
337
+ request: new Request(new URL('/resource', location.href)),
338
+ requestId: createRequestId(),
339
+ })
340
+
341
+ expect(cleanup).toHaveBeenCalledOnce()
342
+ })
343
+ })
@@ -1,4 +1,4 @@
1
- import { ResponseResolutionContext } from '../utils/executeHandlers'
1
+ import { type ResponseResolutionContext } from '../utils/executeHandlers'
2
2
  import { devUtils } from '../utils/internal/devUtils'
3
3
  import { isStringEqual } from '../utils/internal/isStringEqual'
4
4
  import { getStatusCodeColor } from '../utils/logging/getStatusCodeColor'
@@ -7,18 +7,18 @@ import { serializeRequest } from '../utils/logging/serializeRequest'
7
7
  import { serializeResponse } from '../utils/logging/serializeResponse'
8
8
  import {
9
9
  matchRequestUrl,
10
- Match,
11
- Path,
12
- PathParams,
10
+ type Match,
11
+ type Path,
12
+ type PathParams,
13
13
  } from '../utils/matching/matchRequestUrl'
14
14
  import { toPublicUrl } from '../utils/request/toPublicUrl'
15
15
  import { getAllRequestCookies } from '../utils/request/getRequestCookies'
16
16
  import { cleanUrl } from '../utils/url/cleanUrl'
17
17
  import {
18
18
  RequestHandler,
19
- RequestHandlerDefaultInfo,
20
- RequestHandlerOptions,
21
- ResponseResolver,
19
+ type RequestHandlerDefaultInfo,
20
+ type RequestHandlerOptions,
21
+ type ResponseResolver,
22
22
  } from './RequestHandler'
23
23
 
24
24
  export type HttpHandlerMethod = string | RegExp
@@ -1,17 +1,21 @@
1
+ import { Headers as HeadersPolyfill } from 'headers-polyfill'
1
2
  import { getCallFrame } from '../utils/internal/getCallFrame'
2
3
  import {
3
- AsyncIterable,
4
- Iterable,
5
4
  isIterable,
5
+ type AsyncIterable,
6
+ type Iterable,
6
7
  } from '../utils/internal/isIterable'
7
8
  import type { ResponseResolutionContext } from '../utils/executeHandlers'
8
9
  import type { MaybePromise } from '../typeUtils'
10
+ import type { HttpResponse } from '../HttpResponse'
9
11
  import {
10
- StrictRequest,
11
- HttpResponse,
12
- DefaultUnsafeFetchResponse,
12
+ type StrictRequest,
13
+ type DefaultUnsafeFetchResponse,
13
14
  } from '../HttpResponse'
14
15
  import type { GraphQLRequestBody } from './GraphQLHandler'
16
+ import { devUtils } from '../utils/internal/devUtils'
17
+ import { getRawSetCookie } from '../utils/HttpResponse/decorators'
18
+ import { observeResponseBodyStream } from '../utils/internal/observe-response-body-stream'
15
19
 
16
20
  export type DefaultRequestMultipartBody = Record<
17
21
  string,
@@ -28,12 +32,7 @@ export type DefaultBodyType =
28
32
  | undefined
29
33
 
30
34
  export type JsonBodyType =
31
- | Record<string, any>
32
- | string
33
- | number
34
- | boolean
35
- | null
36
- | undefined
35
+ Record<string, any> | string | number | boolean | null | undefined
37
36
 
38
37
  export interface RequestHandlerDefaultInfo {
39
38
  header: string
@@ -88,8 +87,26 @@ export type ResponseResolverInfo<
88
87
  > = {
89
88
  request: StrictRequest<RequestBodyType>
90
89
  requestId: string
90
+ /**
91
+ * Schedule a callback to run after this response resolver completes.
92
+ * Handy for cleaning up the side effects introduced in the resolver.
93
+ *
94
+ * For responses with a `ReadableStream` body (including `sse()` handlers),
95
+ * the callback runs once the response stream settles: it is read to
96
+ * completion, errored, or canceled, or the request is aborted.
97
+ * @example
98
+ * sse('/', ({ client, finalize }) => {
99
+ * const interval = setInterval(() => client.send({ data: 'ping' }))
100
+ * finalize(() => clearInterval(interval))
101
+ * })
102
+ */
103
+ finalize: ResponseResolverFinalizeFunction
91
104
  } & ResolverExtraInfo
92
105
 
106
+ type ResponseResolverFinalizeFunction = (
107
+ callback: () => MaybePromise<void>,
108
+ ) => void
109
+
93
110
  export type ResponseResolver<
94
111
  ResolverExtraInfo extends Record<string, unknown> = Record<string, unknown>,
95
112
  RequestBodyType extends DefaultBodyType = DefaultBodyType,
@@ -147,7 +164,9 @@ export abstract class RequestHandler<
147
164
  MaybeAsyncResponseResolverReturnType<any>
148
165
  >
149
166
  private resolverIteratorResult?: Response | HttpResponse<any>
167
+ private resolverIteratorCleanups?: Array<() => MaybePromise<void>>
150
168
  private options?: HandlerOptions
169
+ private scheduledCleanups: Map<string, Array<() => MaybePromise<void>>>
151
170
 
152
171
  public info: HandlerInfo & RequestHandlerInternalInfo
153
172
 
@@ -160,6 +179,7 @@ export abstract class RequestHandler<
160
179
  constructor(args: RequestHandlerArgs<HandlerInfo, HandlerOptions>) {
161
180
  this.resolver = args.resolver
162
181
  this.options = args.options
182
+ this.scheduledCleanups = new Map()
163
183
 
164
184
  const callFrame = getCallFrame(new Error())
165
185
 
@@ -171,6 +191,37 @@ export abstract class RequestHandler<
171
191
  this.isUsed = false
172
192
  }
173
193
 
194
+ /**
195
+ * Reset the runtime state accumulated during response resolution,
196
+ * such as generator iterator progress. Called when this handler is
197
+ * removed from the active handlers list so re-adding it later starts
198
+ * from a clean state.
199
+ */
200
+ protected reset(): void {
201
+ this.scheduledCleanups.clear()
202
+
203
+ const iterator = this.resolverIterator
204
+ this.resolverIterator = undefined
205
+ this.resolverIteratorResult = undefined
206
+ this.resolverIteratorCleanups = undefined
207
+
208
+ if (typeof iterator?.return === 'function') {
209
+ void Promise.resolve(iterator.return())
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Restore this handler so it can match requests again after being
215
+ * exhausted (e.g. via `{ once: true }`). Also clears any accumulated
216
+ * resolution state.
217
+ */
218
+ protected restore(): void {
219
+ if (this.options?.once) {
220
+ this.reset()
221
+ this.isUsed = false
222
+ }
223
+ }
224
+
174
225
  /**
175
226
  * Determine if the intercepted request should be mocked.
176
227
  */
@@ -302,24 +353,80 @@ export abstract class RequestHandler<
302
353
  parsedResult,
303
354
  })
304
355
 
356
+ const listenerController = new AbortController()
357
+
358
+ /**
359
+ * @note Initialize the `finalize` machinery lazily, on the first
360
+ * access of the `finalize` property by the resolver. If the resolver
361
+ * never accesses it, the handler behaves as if `finalize` never
362
+ * existed: no abort listeners, no scheduled cleanups, and no response
363
+ * body stream observation (see `this.complete()`).
364
+ */
365
+ let finalizeFunction: ResponseResolverFinalizeFunction | undefined
366
+
367
+ const getFinalize = (): ResponseResolverFinalizeFunction => {
368
+ if (finalizeFunction == null) {
369
+ // Run any scheduled cleanups if the request gets aborted
370
+ // while the resolver is still executing.
371
+ if (!args.request.signal.aborted) {
372
+ args.request.signal.addEventListener(
373
+ 'abort',
374
+ () => this.runScheduledCleanups(args.requestId),
375
+ {
376
+ once: true,
377
+ signal: listenerController.signal,
378
+ },
379
+ )
380
+ }
381
+
382
+ finalizeFunction = (callback) => {
383
+ this.scheduleCleanup(args.requestId, callback)
384
+
385
+ /**
386
+ * @note Run the cleanup immediately if the request has already
387
+ * been aborted. The "abort" listener above never fires for an
388
+ * already-aborted signal (and fires at most once), while
389
+ * long-lived resolvers (streams, generators) may never settle
390
+ * to run the cleanups on completion.
391
+ */
392
+ if (args.request.signal.aborted) {
393
+ void this.runScheduledCleanups(args.requestId)
394
+ }
395
+ }
396
+ }
397
+
398
+ return finalizeFunction
399
+ }
400
+
305
401
  const mockedResponsePromise = (
306
402
  executeResolver({
307
403
  ...resolverExtras,
404
+ get finalize(): ResponseResolverFinalizeFunction {
405
+ return getFinalize()
406
+ },
308
407
  requestId: args.requestId,
309
408
  request: args.request,
310
409
  }) as Promise<Response>
311
- ).catch((errorOrResponse) => {
312
- // Allow throwing a Response instance in a response resolver.
313
- if (errorOrResponse instanceof Response) {
314
- return errorOrResponse
315
- }
410
+ )
411
+ .catch((errorOrResponse) => {
412
+ // Allow throwing a Response instance in a response resolver.
413
+ if (errorOrResponse instanceof Response) {
414
+ return errorOrResponse
415
+ }
316
416
 
317
- // Otherwise, throw the error as-is.
318
- throw errorOrResponse
319
- })
417
+ // Otherwise, throw the error as-is.
418
+ throw errorOrResponse
419
+ })
420
+ .finally(() => {
421
+ listenerController.abort()
422
+ })
320
423
 
321
424
  const mockedResponse = await mockedResponsePromise
322
425
 
426
+ if (mockedResponse) {
427
+ forwardResponseCookies(mockedResponse)
428
+ }
429
+
323
430
  const executionResult = this.createExecutionResult({
324
431
  // Pass the cloned request to the result so that logging
325
432
  // and other consumers could read its body once more.
@@ -337,10 +444,42 @@ export abstract class RequestHandler<
337
444
  ): ResponseResolver<ResolverExtras> {
338
445
  return async (info): Promise<ResponseResolverReturnType<any>> => {
339
446
  if (!this.resolverIterator) {
340
- const result = await resolver(info)
447
+ let result
448
+
449
+ try {
450
+ result = await resolver(info)
451
+ } catch (error) {
452
+ // Ensure cleanup if the resolver throws, regardless of the returned type.
453
+ await this.runScheduledCleanups(info.requestId)
454
+
455
+ // Re-throw the error for the "run()" method to handle (e.g. thrown responses).
456
+ throw error
457
+ }
341
458
 
342
459
  if (!isIterable(result)) {
343
- return result
460
+ // Otherwise, run the cleanups if it's a plain response resolver
461
+ // (deferred until the response stream settles for streamed responses).
462
+ return this.complete({
463
+ request: info.request,
464
+ requestId: info.requestId,
465
+ response: result,
466
+ })
467
+ }
468
+
469
+ /**
470
+ * @note Carry over any previously registered cleanups onto the iterator cleanups.
471
+ * This is only relevant if "finalize()" is called in a regular resolver that
472
+ * returns an iterator.
473
+ * @example
474
+ * http.get('/', async ({ finalize }) => {
475
+ * finalize(cleanup)
476
+ * return (async function*() {})()
477
+ * })
478
+ */
479
+ const existingCleanups = this.scheduledCleanups.get(info.requestId)
480
+ if (existingCleanups != null && existingCleanups.length > 0) {
481
+ this.resolverIteratorCleanups = existingCleanups
482
+ this.scheduledCleanups.delete(info.requestId)
344
483
  }
345
484
 
346
485
  this.resolverIterator =
@@ -366,7 +505,11 @@ export abstract class RequestHandler<
366
505
 
367
506
  // Clone the previously stored response so it can be read
368
507
  // when receiving it repeatedly from the "done" generator.
369
- return this.resolverIteratorResult?.clone()
508
+ return this.complete({
509
+ request: info.request,
510
+ requestId: info.requestId,
511
+ response: this.resolverIteratorResult?.clone(),
512
+ })
370
513
  }
371
514
 
372
515
  return nextResponse
@@ -387,4 +530,168 @@ export abstract class RequestHandler<
387
530
  parsedResult: args.parsedResult,
388
531
  }
389
532
  }
533
+
534
+ private scheduleCleanup(
535
+ requestId: string,
536
+ callback: () => MaybePromise<void>,
537
+ ): void {
538
+ if (this.resolverIterator) {
539
+ ;(this.resolverIteratorCleanups ||= []).unshift(callback)
540
+ return
541
+ }
542
+
543
+ const cleanups = this.scheduledCleanups.get(requestId) || []
544
+ cleanups.unshift(callback)
545
+ this.scheduledCleanups.set(requestId, cleanups)
546
+ }
547
+
548
+ private async exhaustCleanups(
549
+ cleanups: Array<() => MaybePromise<void>>,
550
+ ): Promise<void> {
551
+ const errors: Array<Error> = []
552
+
553
+ for (const cleanup of cleanups) {
554
+ try {
555
+ await cleanup()
556
+ } catch (error) {
557
+ if (error instanceof Error) {
558
+ errors.push(error)
559
+ }
560
+ }
561
+ }
562
+
563
+ if (errors.length > 0) {
564
+ devUtils.error(
565
+ 'Failed to execute cleanup for request handler "%s"',
566
+ this.info.header,
567
+ new AggregateError(
568
+ errors,
569
+ `Failed to execute cleanup for request handler "${this.info.header}"`,
570
+ ),
571
+ )
572
+ }
573
+ }
574
+
575
+ /**
576
+ * Remove and return the cleanups scheduled for the given request
577
+ * (or the pending iterator cleanups for generator resolvers).
578
+ */
579
+ private takeScheduledCleanups(
580
+ requestId: string,
581
+ ): Array<() => MaybePromise<void>> | undefined {
582
+ if (
583
+ this.resolverIterator &&
584
+ this.resolverIteratorCleanups != null &&
585
+ this.resolverIteratorCleanups.length > 0
586
+ ) {
587
+ const cleanups = this.resolverIteratorCleanups
588
+ this.resolverIteratorCleanups = undefined
589
+ return cleanups
590
+ }
591
+
592
+ const cleanups = this.scheduledCleanups.get(requestId)
593
+
594
+ if (!cleanups || cleanups.length === 0) {
595
+ return undefined
596
+ }
597
+
598
+ this.scheduledCleanups.delete(requestId)
599
+ return cleanups
600
+ }
601
+
602
+ private async runScheduledCleanups(requestId: string): Promise<void> {
603
+ const cleanups = this.takeScheduledCleanups(requestId)
604
+
605
+ if (cleanups) {
606
+ await this.exhaustCleanups(cleanups)
607
+ }
608
+ }
609
+
610
+ /**
611
+ * Conclude the response resolution for the given request.
612
+ * Runs the scheduled cleanups immediately for responses without a
613
+ * `ReadableStream` body. For streamed responses, returns an observed
614
+ * copy of the response and defers the cleanups until its body settles
615
+ * (is read to completion, errored, or canceled) or the request is
616
+ * aborted, whichever comes first.
617
+ */
618
+ private async complete(args: {
619
+ request: StrictRequest<any>
620
+ requestId: string
621
+ response: Response | undefined | void
622
+ }): Promise<Response | undefined | void> {
623
+ const cleanups = this.takeScheduledCleanups(args.requestId)
624
+
625
+ if (!cleanups) {
626
+ return args.response
627
+ }
628
+
629
+ const observedResponse = args.response
630
+ ? observeResponseBodyStream(args.response)
631
+ : null
632
+
633
+ if (!observedResponse) {
634
+ await this.exhaustCleanups(cleanups)
635
+ return args.response
636
+ }
637
+
638
+ const listenerController = new AbortController()
639
+
640
+ const runCleanupsOnce = (): void => {
641
+ if (listenerController.signal.aborted) {
642
+ return
643
+ }
644
+
645
+ listenerController.abort()
646
+ void this.exhaustCleanups(cleanups)
647
+ }
648
+
649
+ void observedResponse.settled.then(runCleanupsOnce)
650
+
651
+ /**
652
+ * @note Also run the cleanups when the request is aborted.
653
+ * Stream cancellation does not always propagate to the observed
654
+ * response (e.g. if an unconsumed clone of the response exists),
655
+ * while the request abort reliably means the response is unused.
656
+ */
657
+ if (args.request.signal.aborted) {
658
+ runCleanupsOnce()
659
+ } else {
660
+ args.request.signal.addEventListener('abort', runCleanupsOnce, {
661
+ once: true,
662
+ signal: listenerController.signal,
663
+ })
664
+ }
665
+
666
+ return observedResponse.response
667
+ }
668
+ }
669
+
670
+ /**
671
+ * Forwards the cookies from the given response to `document.cookie`.
672
+ */
673
+ export function forwardResponseCookies(response: Response): void {
674
+ // Cookie forwarding is only relevant in the browser.
675
+ if (typeof document === 'undefined') {
676
+ return
677
+ }
678
+
679
+ const responseCookies = getRawSetCookie(response)
680
+
681
+ if (!responseCookies) {
682
+ return
683
+ }
684
+
685
+ // Write the mocked response cookies to the document.
686
+ // Use `headers-polyfill` to get the Set-Cookie header value correctly.
687
+ // This is an alternative until TypeScript 5.2
688
+ // and Node.js v20 become the minimum supported versions
689
+ // and "Headers.prototype.getSetCookie" can be used directly.
690
+ const allResponseCookies = HeadersPolyfill.prototype.getSetCookie.call(
691
+ new Headers([['set-cookie', responseCookies]]),
692
+ )
693
+
694
+ for (const cookieString of allResponseCookies) {
695
+ document.cookie = cookieString
696
+ }
390
697
  }