msw 2.14.3 → 2.14.4

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 (69) hide show
  1. package/lib/core/{HttpResponse-aGiIzO91.d.ts → HttpResponse-BFS34nkx.d.ts} +16 -0
  2. package/lib/core/{HttpResponse-CxHR1nNN.d.mts → HttpResponse-CQwYpuKo.d.mts} +16 -0
  3. package/lib/core/HttpResponse.d.mts +1 -1
  4. package/lib/core/HttpResponse.d.ts +1 -1
  5. package/lib/core/experimental/compat.d.mts +1 -1
  6. package/lib/core/experimental/compat.d.ts +1 -1
  7. package/lib/core/experimental/define-network.d.mts +1 -1
  8. package/lib/core/experimental/define-network.d.ts +1 -1
  9. package/lib/core/experimental/frames/http-frame.d.mts +1 -1
  10. package/lib/core/experimental/frames/http-frame.d.ts +1 -1
  11. package/lib/core/experimental/frames/network-frame.d.mts +1 -1
  12. package/lib/core/experimental/frames/network-frame.d.ts +1 -1
  13. package/lib/core/experimental/frames/websocket-frame.d.mts +1 -1
  14. package/lib/core/experimental/frames/websocket-frame.d.ts +1 -1
  15. package/lib/core/experimental/handlers-controller.d.mts +1 -1
  16. package/lib/core/experimental/handlers-controller.d.ts +1 -1
  17. package/lib/core/experimental/index.d.mts +1 -1
  18. package/lib/core/experimental/index.d.ts +1 -1
  19. package/lib/core/experimental/on-unhandled-frame.d.mts +1 -1
  20. package/lib/core/experimental/on-unhandled-frame.d.ts +1 -1
  21. package/lib/core/experimental/setup-api.d.mts +1 -1
  22. package/lib/core/experimental/setup-api.d.ts +1 -1
  23. package/lib/core/experimental/sources/interceptor-source.d.mts +1 -1
  24. package/lib/core/experimental/sources/interceptor-source.d.ts +1 -1
  25. package/lib/core/experimental/sources/network-source.d.mts +1 -1
  26. package/lib/core/experimental/sources/network-source.d.ts +1 -1
  27. package/lib/core/getResponse.d.mts +1 -1
  28. package/lib/core/getResponse.d.ts +1 -1
  29. package/lib/core/graphql.d.mts +1 -1
  30. package/lib/core/graphql.d.ts +1 -1
  31. package/lib/core/handlers/GraphQLHandler.d.mts +1 -1
  32. package/lib/core/handlers/GraphQLHandler.d.ts +1 -1
  33. package/lib/core/handlers/HttpHandler.d.mts +1 -1
  34. package/lib/core/handlers/HttpHandler.d.ts +1 -1
  35. package/lib/core/handlers/RequestHandler.d.mts +1 -1
  36. package/lib/core/handlers/RequestHandler.d.ts +1 -1
  37. package/lib/core/handlers/RequestHandler.js +82 -1
  38. package/lib/core/handlers/RequestHandler.js.map +1 -1
  39. package/lib/core/handlers/RequestHandler.mjs +82 -1
  40. package/lib/core/handlers/RequestHandler.mjs.map +1 -1
  41. package/lib/core/http.d.mts +1 -1
  42. package/lib/core/http.d.ts +1 -1
  43. package/lib/core/index.d.mts +1 -1
  44. package/lib/core/index.d.ts +1 -1
  45. package/lib/core/passthrough.d.mts +1 -1
  46. package/lib/core/passthrough.d.ts +1 -1
  47. package/lib/core/sse.d.mts +1 -1
  48. package/lib/core/sse.d.ts +1 -1
  49. package/lib/core/utils/HttpResponse/decorators.d.mts +1 -1
  50. package/lib/core/utils/HttpResponse/decorators.d.ts +1 -1
  51. package/lib/core/utils/executeHandlers.d.mts +1 -1
  52. package/lib/core/utils/executeHandlers.d.ts +1 -1
  53. package/lib/core/utils/handleRequest.d.mts +1 -1
  54. package/lib/core/utils/handleRequest.d.ts +1 -1
  55. package/lib/core/utils/internal/attachSiblingHandlers.d.mts +1 -1
  56. package/lib/core/utils/internal/attachSiblingHandlers.d.ts +1 -1
  57. package/lib/core/utils/internal/isHandlerKind.d.mts +1 -1
  58. package/lib/core/utils/internal/isHandlerKind.d.ts +1 -1
  59. package/lib/core/utils/internal/parseGraphQLRequest.d.mts +1 -1
  60. package/lib/core/utils/internal/parseGraphQLRequest.d.ts +1 -1
  61. package/lib/core/utils/internal/parseMultipartData.d.mts +1 -1
  62. package/lib/core/utils/internal/parseMultipartData.d.ts +1 -1
  63. package/lib/core/ws/handleWebSocketEvent.d.mts +1 -1
  64. package/lib/core/ws/handleWebSocketEvent.d.ts +1 -1
  65. package/lib/iife/index.js +81 -1
  66. package/lib/iife/index.js.map +1 -1
  67. package/lib/mockServiceWorker.js +1 -1
  68. package/package.json +2 -2
  69. package/src/core/handlers/RequestHandler.ts +144 -10
@@ -7,7 +7,7 @@
7
7
  * - Please do NOT modify this file.
8
8
  */
9
9
 
10
- const PACKAGE_VERSION = '2.14.3'
10
+ const PACKAGE_VERSION = '2.14.4'
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.14.3",
3
+ "version": "2.14.4",
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.14.3"
289
+ "msw": "^2.14.4"
290
290
  },
291
291
  "peerDependencies": {
292
292
  "typescript": ">= 4.8.x"
@@ -13,6 +13,7 @@ import {
13
13
  type DefaultUnsafeFetchResponse,
14
14
  } from '../HttpResponse'
15
15
  import type { GraphQLRequestBody } from './GraphQLHandler'
16
+ import { devUtils } from '../utils/internal/devUtils'
16
17
  import { getRawSetCookie } from '../utils/HttpResponse/decorators'
17
18
 
18
19
  export type DefaultRequestMultipartBody = Record<
@@ -90,8 +91,22 @@ export type ResponseResolverInfo<
90
91
  > = {
91
92
  request: StrictRequest<RequestBodyType>
92
93
  requestId: string
94
+ /**
95
+ * Schedule a callback to run after this response resolver completes.
96
+ * Handy for cleaning up the side effects introduced in the resolver.
97
+ * @example
98
+ * sse('/', ({ client, finalize }) => {
99
+ * const interval = setInterval(() => client.send({ data: 'ping' }))
100
+ * finalize(() => clearInterval(interval))
101
+ * })
102
+ */
103
+ finalize: ResponseResolverFinalizeFunction
93
104
  } & ResolverExtraInfo
94
105
 
106
+ type ResponseResolverFinalizeFunction = (
107
+ callback: () => MaybePromise<void>,
108
+ ) => void
109
+
95
110
  export type ResponseResolver<
96
111
  ResolverExtraInfo extends Record<string, unknown> = Record<string, unknown>,
97
112
  RequestBodyType extends DefaultBodyType = DefaultBodyType,
@@ -149,7 +164,9 @@ export abstract class RequestHandler<
149
164
  MaybeAsyncResponseResolverReturnType<any>
150
165
  >
151
166
  private resolverIteratorResult?: Response | HttpResponse<any>
167
+ private resolverIteratorCleanups?: Array<() => MaybePromise<void>>
152
168
  private options?: HandlerOptions
169
+ private scheduledCleanups: Map<string, Array<() => MaybePromise<void>>>
153
170
 
154
171
  public info: HandlerInfo & RequestHandlerInternalInfo
155
172
 
@@ -162,6 +179,7 @@ export abstract class RequestHandler<
162
179
  constructor(args: RequestHandlerArgs<HandlerInfo, HandlerOptions>) {
163
180
  this.resolver = args.resolver
164
181
  this.options = args.options
182
+ this.scheduledCleanups = new Map()
165
183
 
166
184
  const callFrame = getCallFrame(new Error())
167
185
 
@@ -180,9 +198,12 @@ export abstract class RequestHandler<
180
198
  * from a clean state.
181
199
  */
182
200
  protected reset(): void {
201
+ this.scheduledCleanups.clear()
202
+
183
203
  const iterator = this.resolverIterator
184
204
  this.resolverIterator = undefined
185
205
  this.resolverIteratorResult = undefined
206
+ this.resolverIteratorCleanups = undefined
186
207
 
187
208
  if (typeof iterator?.return === 'function') {
188
209
  void Promise.resolve(iterator.return())
@@ -332,21 +353,39 @@ export abstract class RequestHandler<
332
353
  parsedResult,
333
354
  })
334
355
 
356
+ const listenerController = new AbortController()
357
+
358
+ args.request.signal.addEventListener(
359
+ 'abort',
360
+ () => this.runScheduledCleanups(args.requestId),
361
+ {
362
+ once: true,
363
+ signal: listenerController.signal,
364
+ },
365
+ )
366
+
335
367
  const mockedResponsePromise = (
336
368
  executeResolver({
337
369
  ...resolverExtras,
370
+ finalize: (callback) => {
371
+ this.scheduleCleanup(args.requestId, callback)
372
+ },
338
373
  requestId: args.requestId,
339
374
  request: args.request,
340
- }) as Promise<Response | undefined>
341
- ).catch((errorOrResponse) => {
342
- // Allow throwing a Response instance in a response resolver.
343
- if (errorOrResponse instanceof Response) {
344
- return errorOrResponse
345
- }
375
+ }) as Promise<Response>
376
+ )
377
+ .catch((errorOrResponse) => {
378
+ // Allow throwing a Response instance in a response resolver.
379
+ if (errorOrResponse instanceof Response) {
380
+ return errorOrResponse
381
+ }
346
382
 
347
- // Otherwise, throw the error as-is.
348
- throw errorOrResponse
349
- })
383
+ // Otherwise, throw the error as-is.
384
+ throw errorOrResponse
385
+ })
386
+ .finally(() => {
387
+ listenerController.abort()
388
+ })
350
389
 
351
390
  const mockedResponse = await mockedResponsePromise
352
391
 
@@ -371,12 +410,40 @@ export abstract class RequestHandler<
371
410
  ): ResponseResolver<ResolverExtras> {
372
411
  return async (info): Promise<ResponseResolverReturnType<any>> => {
373
412
  if (!this.resolverIterator) {
374
- const result = await resolver(info)
413
+ let result
414
+
415
+ try {
416
+ result = await resolver(info)
417
+ } catch (error) {
418
+ // Ensure cleanup if the resolver throws, regardless of the returned type.
419
+ await this.runScheduledCleanups(info.requestId)
420
+
421
+ // Re-throw the error for the "run()" method to handle (e.g. thrown responses).
422
+ throw error
423
+ }
375
424
 
376
425
  if (!isIterable(result)) {
426
+ // Otherwise, run the cleanup immediately if it's a plain response resolver.
427
+ await this.runScheduledCleanups(info.requestId)
377
428
  return result
378
429
  }
379
430
 
431
+ /**
432
+ * @note Carry over any previously registered cleanups onto the iterator cleanups.
433
+ * This is only relevant if "finalize()" is called in a regular resolver that
434
+ * returns an iterator.
435
+ * @example
436
+ * http.get('/', async ({ finalize }) => {
437
+ * finalize(cleanup)
438
+ * return (async function*() {})()
439
+ * })
440
+ */
441
+ const existingCleanups = this.scheduledCleanups.get(info.requestId)
442
+ if (existingCleanups != null && existingCleanups.length > 0) {
443
+ this.resolverIteratorCleanups = existingCleanups
444
+ this.scheduledCleanups.delete(info.requestId)
445
+ }
446
+
380
447
  this.resolverIterator =
381
448
  Symbol.iterator in result
382
449
  ? result[Symbol.iterator]()
@@ -398,6 +465,8 @@ export abstract class RequestHandler<
398
465
  // only after it's been completely exhausted.
399
466
  this.isUsed = true
400
467
 
468
+ await this.runScheduledCleanups(info.requestId)
469
+
401
470
  // Clone the previously stored response so it can be read
402
471
  // when receiving it repeatedly from the "done" generator.
403
472
  return this.resolverIteratorResult?.clone()
@@ -421,6 +490,71 @@ export abstract class RequestHandler<
421
490
  parsedResult: args.parsedResult,
422
491
  }
423
492
  }
493
+
494
+ private scheduleCleanup(
495
+ requestId: string,
496
+ callback: () => MaybePromise<void>,
497
+ ): void {
498
+ if (this.resolverIterator) {
499
+ ;(this.resolverIteratorCleanups ||= []).unshift(callback)
500
+ return
501
+ }
502
+
503
+ const cleanups = this.scheduledCleanups.get(requestId) || []
504
+ cleanups.unshift(callback)
505
+ this.scheduledCleanups.set(requestId, cleanups)
506
+ }
507
+
508
+ private async exhaustCleanups(
509
+ cleanups: Array<() => MaybePromise<void>>,
510
+ ): Promise<void> {
511
+ const errors: Array<Error> = []
512
+
513
+ for (const cleanup of cleanups) {
514
+ try {
515
+ await cleanup()
516
+ } catch (error) {
517
+ if (error instanceof Error) {
518
+ errors.push(error)
519
+ }
520
+ }
521
+ }
522
+
523
+ if (errors.length > 0) {
524
+ devUtils.error(
525
+ 'Failed to execute cleanup for request handler "%s"',
526
+ this.info.header,
527
+ new AggregateError(
528
+ errors,
529
+ `Failed to execute cleanup for request handler "${this.info.header}"`,
530
+ ),
531
+ )
532
+ }
533
+ }
534
+
535
+ private async runScheduledCleanups(requestId: string): Promise<void> {
536
+ if (
537
+ this.resolverIterator &&
538
+ this.resolverIteratorCleanups != null &&
539
+ this.resolverIteratorCleanups.length > 0
540
+ ) {
541
+ try {
542
+ await this.exhaustCleanups(this.resolverIteratorCleanups)
543
+ } finally {
544
+ this.resolverIteratorCleanups = undefined
545
+ }
546
+ return
547
+ }
548
+
549
+ const cleanups = this.scheduledCleanups.get(requestId)
550
+
551
+ if (!cleanups || cleanups.length == 0) {
552
+ return
553
+ }
554
+
555
+ await this.exhaustCleanups(cleanups)
556
+ this.scheduledCleanups.delete(requestId)
557
+ }
424
558
  }
425
559
 
426
560
  /**