msw 2.11.1 → 2.11.3

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 (72) hide show
  1. package/lib/browser/index.d.mts +0 -3
  2. package/lib/browser/index.d.ts +0 -3
  3. package/lib/browser/index.js +536 -318
  4. package/lib/browser/index.js.map +1 -1
  5. package/lib/browser/index.mjs +536 -318
  6. package/lib/browser/index.mjs.map +1 -1
  7. package/lib/core/handlers/GraphQLHandler.js.map +1 -1
  8. package/lib/core/handlers/GraphQLHandler.mjs.map +1 -1
  9. package/lib/core/handlers/HttpHandler.js.map +1 -1
  10. package/lib/core/handlers/HttpHandler.mjs.map +1 -1
  11. package/lib/core/utils/handleRequest.js +8 -8
  12. package/lib/core/utils/handleRequest.js.map +1 -1
  13. package/lib/core/utils/handleRequest.mjs +8 -8
  14. package/lib/core/utils/handleRequest.mjs.map +1 -1
  15. package/lib/core/utils/internal/devUtils.js.map +1 -1
  16. package/lib/core/utils/internal/devUtils.mjs.map +1 -1
  17. package/lib/core/utils/internal/getCallFrame.js +2 -2
  18. package/lib/core/utils/internal/getCallFrame.js.map +1 -1
  19. package/lib/core/utils/internal/getCallFrame.mjs +2 -2
  20. package/lib/core/utils/internal/getCallFrame.mjs.map +1 -1
  21. package/lib/core/utils/internal/parseGraphQLRequest.js +1 -0
  22. package/lib/core/utils/internal/parseGraphQLRequest.js.map +1 -1
  23. package/lib/core/utils/internal/parseGraphQLRequest.mjs +1 -0
  24. package/lib/core/utils/internal/parseGraphQLRequest.mjs.map +1 -1
  25. package/lib/core/utils/matching/matchRequestUrl.js +1 -1
  26. package/lib/core/utils/matching/matchRequestUrl.js.map +1 -1
  27. package/lib/core/utils/matching/matchRequestUrl.mjs +1 -1
  28. package/lib/core/utils/matching/matchRequestUrl.mjs.map +1 -1
  29. package/lib/core/utils/url/cleanUrl.js +1 -1
  30. package/lib/core/utils/url/cleanUrl.js.map +1 -1
  31. package/lib/core/utils/url/cleanUrl.mjs +1 -1
  32. package/lib/core/utils/url/cleanUrl.mjs.map +1 -1
  33. package/lib/core/utils/url/isAbsoluteUrl.js +1 -1
  34. package/lib/core/utils/url/isAbsoluteUrl.js.map +1 -1
  35. package/lib/core/utils/url/isAbsoluteUrl.mjs +1 -1
  36. package/lib/core/utils/url/isAbsoluteUrl.mjs.map +1 -1
  37. package/lib/core/ws/WebSocketIndexedDBClientStore.js.map +1 -1
  38. package/lib/core/ws/WebSocketIndexedDBClientStore.mjs.map +1 -1
  39. package/lib/core/ws/utils/attachWebSocketLogger.js.map +1 -1
  40. package/lib/core/ws/utils/attachWebSocketLogger.mjs.map +1 -1
  41. package/lib/iife/index.js +2536 -2314
  42. package/lib/iife/index.js.map +1 -1
  43. package/lib/mockServiceWorker.js +16 -12
  44. package/package.json +22 -15
  45. package/src/browser/setupWorker/glossary.ts +9 -114
  46. package/src/browser/setupWorker/setupWorker.ts +82 -119
  47. package/src/browser/setupWorker/start/createRequestListener.ts +20 -24
  48. package/src/browser/setupWorker/start/createResponseListener.ts +15 -22
  49. package/src/browser/setupWorker/start/createStartHandler.ts +24 -18
  50. package/src/browser/setupWorker/start/utils/enableMocking.ts +18 -21
  51. package/src/browser/setupWorker/start/utils/getWorkerInstance.ts +17 -16
  52. package/src/browser/setupWorker/start/utils/printStartMessage.ts +0 -2
  53. package/src/browser/utils/checkWorkerIntegrity.ts +22 -14
  54. package/src/browser/utils/deferNetworkRequestsUntil.ts +1 -1
  55. package/src/browser/utils/workerChannel.ts +146 -0
  56. package/src/core/handlers/GraphQLHandler.ts +0 -3
  57. package/src/core/handlers/HttpHandler.ts +0 -2
  58. package/src/core/utils/handleRequest.ts +8 -8
  59. package/src/core/utils/internal/devUtils.ts +0 -2
  60. package/src/core/utils/internal/getCallFrame.ts +2 -2
  61. package/src/core/utils/internal/parseGraphQLRequest.ts +1 -0
  62. package/src/core/utils/matching/matchRequestUrl.ts +2 -2
  63. package/src/core/utils/request/onUnhandledRequest.test.ts +1 -1
  64. package/src/core/utils/url/cleanUrl.ts +1 -1
  65. package/src/core/utils/url/isAbsoluteUrl.ts +1 -1
  66. package/src/core/ws/WebSocketIndexedDBClientStore.ts +0 -4
  67. package/src/core/ws/utils/attachWebSocketLogger.ts +0 -14
  68. package/src/mockServiceWorker.js +14 -10
  69. package/src/browser/setupWorker/start/createFallbackStart.ts +0 -21
  70. package/src/browser/setupWorker/start/utils/createMessageChannel.ts +0 -32
  71. package/src/browser/setupWorker/stop/createFallbackStop.ts +0 -11
  72. package/src/browser/setupWorker/stop/createStop.ts +0 -35
@@ -1,12 +1,5 @@
1
- import {
2
- StartOptions,
3
- SetupWorkerInternalContext,
4
- ServiceWorkerIncomingEventsMap,
5
- } from '../glossary'
6
- import {
7
- ServiceWorkerMessage,
8
- WorkerChannel,
9
- } from './utils/createMessageChannel'
1
+ import { Emitter } from 'rettime'
2
+ import { StartOptions, SetupWorkerInternalContext } from '../glossary'
10
3
  import { deserializeRequest } from '../../utils/deserializeRequest'
11
4
  import { RequestHandler } from '~/core/handlers/RequestHandler'
12
5
  import { handleRequest } from '~/core/utils/handleRequest'
@@ -18,18 +11,21 @@ import { isHandlerKind } from '~/core/utils/internal/isHandlerKind'
18
11
  export const createRequestListener = (
19
12
  context: SetupWorkerInternalContext,
20
13
  options: RequiredDeep<StartOptions>,
21
- ) => {
22
- return async (
23
- event: MessageEvent,
24
- message: ServiceWorkerMessage<
25
- 'REQUEST',
26
- ServiceWorkerIncomingEventsMap['REQUEST']
27
- >,
28
- ) => {
29
- const messageChannel = new WorkerChannel(event.ports[0])
14
+ ): Emitter.ListenerType<typeof context.workerChannel, 'REQUEST'> => {
15
+ return async (event) => {
16
+ // Treat any incoming requests from the worker as passthrough
17
+ // if `worker.stop()` has been called for this client.
18
+ if (
19
+ !context.isMockingEnabled &&
20
+ context.workerStoppedAt &&
21
+ event.data.interceptedAt > context.workerStoppedAt
22
+ ) {
23
+ event.postMessage('PASSTHROUGH')
24
+ return
25
+ }
30
26
 
31
- const requestId = message.payload.id
32
- const request = deserializeRequest(message.payload)
27
+ const requestId = event.data.id
28
+ const request = deserializeRequest(event.data)
33
29
  const requestCloneForLogs = request.clone()
34
30
 
35
31
  // Make this the first request clone before the
@@ -48,7 +44,7 @@ export const createRequestListener = (
48
44
  context.emitter,
49
45
  {
50
46
  onPassthroughResponse() {
51
- messageChannel.postMessage('PASSTHROUGH')
47
+ event.postMessage('PASSTHROUGH')
52
48
  },
53
49
  async onMockedResponse(response, { handler, parsedResult }) {
54
50
  // Clone the mocked response so its body could be read
@@ -65,7 +61,7 @@ export const createRequestListener = (
65
61
  if (context.supports.readableStreamTransfer) {
66
62
  const responseStreamOrNull = response.body
67
63
 
68
- messageChannel.postMessage(
64
+ event.postMessage(
69
65
  'MOCK_RESPONSE',
70
66
  {
71
67
  ...responseInit,
@@ -85,7 +81,7 @@ export const createRequestListener = (
85
81
  ? null
86
82
  : await responseClone.arrayBuffer()
87
83
 
88
- messageChannel.postMessage('MOCK_RESPONSE', {
84
+ event.postMessage('MOCK_RESPONSE', {
89
85
  ...responseInit,
90
86
  body: responseBufferOrNull,
91
87
  })
@@ -118,7 +114,7 @@ This exception has been gracefully handled as a 500 response, however, it's stro
118
114
 
119
115
  // Treat all other exceptions in a request handler as unintended,
120
116
  // alerting that there is a problem that needs fixing.
121
- messageChannel.postMessage('MOCK_RESPONSE', {
117
+ event.postMessage('MOCK_RESPONSE', {
122
118
  status: 500,
123
119
  statusText: 'Request Handler Error',
124
120
  headers: {
@@ -1,21 +1,14 @@
1
1
  import { FetchResponse } from '@mswjs/interceptors'
2
- import type {
3
- ServiceWorkerIncomingEventsMap,
4
- SetupWorkerInternalContext,
5
- } from '../glossary'
6
- import type { ServiceWorkerMessage } from './utils/createMessageChannel'
2
+ import type { Emitter } from 'rettime'
3
+ import type { SetupWorkerInternalContext } from '../glossary'
7
4
  import { deserializeRequest } from '../../utils/deserializeRequest'
8
5
 
9
- export function createResponseListener(context: SetupWorkerInternalContext) {
10
- return (
11
- _: MessageEvent,
12
- message: ServiceWorkerMessage<
13
- 'RESPONSE',
14
- ServiceWorkerIncomingEventsMap['RESPONSE']
15
- >,
16
- ) => {
17
- const { payload: responseJson } = message
18
- const request = deserializeRequest(responseJson.request)
6
+ export function createResponseListener(
7
+ context: SetupWorkerInternalContext,
8
+ ): Emitter.ListenerType<typeof context.workerChannel, 'RESPONSE'> {
9
+ return (event) => {
10
+ const responseMessage = event.data
11
+ const request = deserializeRequest(responseMessage.request)
19
12
 
20
13
  /**
21
14
  * CORS requests with `mode: "no-cors"` result in "opaque" responses.
@@ -24,12 +17,12 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
24
17
  * @see https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
25
18
  * @see https://github.com/mswjs/msw/issues/529
26
19
  */
27
- if (responseJson.response.type?.includes('opaque')) {
20
+ if (responseMessage.response.type?.includes('opaque')) {
28
21
  return
29
22
  }
30
23
 
31
24
  const response =
32
- responseJson.response.status === 0
25
+ responseMessage.response.status === 0
33
26
  ? Response.error()
34
27
  : new FetchResponse(
35
28
  /**
@@ -38,11 +31,11 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
38
31
  * throw when passed a non-null body, so ensure it's null here
39
32
  * for those codes
40
33
  */
41
- FetchResponse.isResponseWithBody(responseJson.response.status)
42
- ? responseJson.response.body
34
+ FetchResponse.isResponseWithBody(responseMessage.response.status)
35
+ ? responseMessage.response.body
43
36
  : null,
44
37
  {
45
- ...responseJson,
38
+ ...responseMessage,
46
39
  /**
47
40
  * Set response URL if it's not set already.
48
41
  * @see https://github.com/mswjs/msw/issues/2030
@@ -53,9 +46,9 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
53
46
  )
54
47
 
55
48
  context.emitter.emit(
56
- responseJson.isMockedResponse ? 'response:mocked' : 'response:bypass',
49
+ responseMessage.isMockedResponse ? 'response:mocked' : 'response:bypass',
57
50
  {
58
- requestId: responseJson.request.id,
51
+ requestId: responseMessage.request.id,
59
52
  request,
60
53
  response,
61
54
  },
@@ -1,11 +1,12 @@
1
1
  import { devUtils } from '~/core/utils/internal/devUtils'
2
2
  import { getWorkerInstance } from './utils/getWorkerInstance'
3
3
  import { enableMocking } from './utils/enableMocking'
4
- import { SetupWorkerInternalContext, StartHandler } from '../glossary'
4
+ import type { SetupWorkerInternalContext, StartHandler } from '../glossary'
5
5
  import { createRequestListener } from './createRequestListener'
6
6
  import { checkWorkerIntegrity } from '../../utils/checkWorkerIntegrity'
7
7
  import { createResponseListener } from './createResponseListener'
8
8
  import { validateWorkerScope } from './utils/validateWorkerScope'
9
+ import { DeferredPromise } from '@open-draft/deferred-promise'
9
10
 
10
11
  export const createStartHandler = (
11
12
  context: SetupWorkerInternalContext,
@@ -15,7 +16,7 @@ export const createStartHandler = (
15
16
  // Remove all previously existing event listeners.
16
17
  // This way none of the listeners persists between Fast refresh
17
18
  // of the application's code.
18
- context.events.removeAllListeners()
19
+ context.workerChannel.removeAllListeners()
19
20
 
20
21
  // Handle requests signaled by the worker.
21
22
  context.workerChannel.on(
@@ -57,17 +58,18 @@ Please consider using a custom "serviceWorker.url" option to point to the actual
57
58
  throw new Error(missingWorkerMessage)
58
59
  }
59
60
 
60
- context.worker = worker
61
+ context.workerPromise.resolve(worker)
61
62
  context.registration = registration
62
63
 
63
- context.events.addListener(window, 'beforeunload', () => {
64
+ window.addEventListener('beforeunload', () => {
64
65
  if (worker.state !== 'redundant') {
65
66
  // Notify the Service Worker that this client has closed.
66
67
  // Internally, it's similar to disabling the mocking, only
67
68
  // client close event has a handler that self-terminates
68
69
  // the Service Worker when there are no open clients.
69
- context.workerChannel.send('CLIENT_CLOSED')
70
+ context.workerChannel.postMessage('CLIENT_CLOSED')
70
71
  }
72
+
71
73
  // Make sure we're always clearing the interval - there are reports that not doing this can
72
74
  // cause memory leaks in headless browser environments.
73
75
  window.clearInterval(context.keepAliveInterval)
@@ -82,14 +84,13 @@ Please consider using a custom "serviceWorker.url" option to point to the actual
82
84
  // by the currently installed version of MSW.
83
85
  await checkWorkerIntegrity(context).catch((error) => {
84
86
  devUtils.error(
85
- 'Error while checking the worker script integrity. Please report this on GitHub (https://github.com/mswjs/msw/issues), including the original error below.',
87
+ 'Error while checking the worker script integrity. Please report this on GitHub (https://github.com/mswjs/msw/issues) and include the original error below.',
86
88
  )
87
- // eslint-disable-next-line no-console
88
89
  console.error(error)
89
90
  })
90
91
 
91
92
  context.keepAliveInterval = window.setInterval(
92
- () => context.workerChannel.send('KEEPALIVE_REQUEST'),
93
+ () => context.workerChannel.postMessage('KEEPALIVE_REQUEST'),
93
94
  5000,
94
95
  )
95
96
 
@@ -104,22 +105,27 @@ Please consider using a custom "serviceWorker.url" option to point to the actual
104
105
  async (registration) => {
105
106
  const pendingInstance = registration.installing || registration.waiting
106
107
 
107
- // Wait until the worker is activated.
108
- // Assume the worker is already activated if there's no pending registration
109
- // (i.e. when reloading the page after a successful activation).
110
108
  if (pendingInstance) {
111
- await new Promise<void>((resolve) => {
112
- pendingInstance.addEventListener('statechange', () => {
113
- if (pendingInstance.state === 'activated') {
114
- return resolve()
115
- }
116
- })
109
+ const activationPromise = new DeferredPromise<void>()
110
+
111
+ pendingInstance.addEventListener('statechange', () => {
112
+ if (pendingInstance.state === 'activated') {
113
+ activationPromise.resolve()
114
+ }
117
115
  })
116
+
117
+ // Wait until the worker is activated.
118
+ // Assume the worker is already activated if there's no pending registration
119
+ // (i.e. when reloading the page after a successful activation).
120
+ await activationPromise
118
121
  }
119
122
 
120
123
  // Print the activation message only after the worker has been activated.
121
124
  await enableMocking(context, options).catch((error) => {
122
- throw new Error(`Failed to enable mocking: ${error?.message}`)
125
+ devUtils.error(
126
+ 'Failed to enable mocking. Please report this on GitHub (https://github.com/mswjs/msw/issues) and include the original error below.',
127
+ )
128
+ throw error
123
129
  })
124
130
 
125
131
  return registration
@@ -1,33 +1,30 @@
1
- import { devUtils } from '~/core/utils/internal/devUtils'
2
- import { StartOptions, SetupWorkerInternalContext } from '../../glossary'
1
+ import { DeferredPromise } from '@open-draft/deferred-promise'
2
+ import type { StartOptions, SetupWorkerInternalContext } from '../../glossary'
3
3
  import { printStartMessage } from './printStartMessage'
4
4
 
5
5
  /**
6
6
  * Signals the worker to enable the interception of requests.
7
7
  */
8
- export async function enableMocking(
8
+ export function enableMocking(
9
9
  context: SetupWorkerInternalContext,
10
10
  options: StartOptions,
11
- ) {
12
- context.workerChannel.send('MOCK_ACTIVATE')
13
- const { payload } = await context.events.once('MOCKING_ENABLED')
11
+ ): Promise<boolean> {
12
+ const mockingEnabledPromise = new DeferredPromise<boolean>()
14
13
 
15
- // Warn the developer on multiple "worker.start()" calls.
16
- // While this will not affect the worker in any way,
17
- // it likely indicates an issue with the developer's code.
18
- if (context.isMockingEnabled) {
19
- devUtils.warn(
20
- `Found a redundant "worker.start()" call. Note that starting the worker while mocking is already enabled will have no effect. Consider removing this "worker.start()" call.`,
21
- )
22
- return
23
- }
14
+ context.workerChannel.postMessage('MOCK_ACTIVATE')
15
+ context.workerChannel.once('MOCKING_ENABLED', async (event) => {
16
+ context.isMockingEnabled = true
17
+ const worker = await context.workerPromise
24
18
 
25
- context.isMockingEnabled = true
19
+ printStartMessage({
20
+ quiet: options.quiet,
21
+ workerScope: context.registration?.scope,
22
+ workerUrl: worker.scriptURL,
23
+ client: event.data.client,
24
+ })
26
25
 
27
- printStartMessage({
28
- quiet: options.quiet,
29
- workerScope: context.registration?.scope,
30
- workerUrl: context.worker?.scriptURL,
31
- client: payload.client,
26
+ mockingEnabledPromise.resolve(true)
32
27
  })
28
+
29
+ return mockingEnabledPromise
33
30
  }
@@ -1,4 +1,4 @@
1
- import { until } from '@open-draft/until'
1
+ import { until } from 'until-async'
2
2
  import { devUtils } from '~/core/utils/internal/devUtils'
3
3
  import { getAbsoluteWorkerUrl } from '../../../utils/getAbsoluteWorkerUrl'
4
4
  import { getWorkerByRegistration } from './getWorkerByRegistration'
@@ -52,21 +52,22 @@ export const getWorkerInstance = async (
52
52
  }
53
53
 
54
54
  // When the Service Worker wasn't found, register it anew and return the reference.
55
- const registrationResult = await until<Error, ServiceWorkerInstanceTuple>(
56
- async () => {
57
- const registration = await navigator.serviceWorker.register(url, options)
58
- return [
59
- // Compare existing worker registration by its worker URL,
60
- // to prevent irrelevant workers to resolve here (such as Codesandbox worker).
61
- getWorkerByRegistration(registration, absoluteWorkerUrl, findWorker),
62
- registration,
63
- ]
64
- },
65
- )
55
+ const [registrationError, registrationResult] = await until<
56
+ Error,
57
+ ServiceWorkerInstanceTuple
58
+ >(async () => {
59
+ const registration = await navigator.serviceWorker.register(url, options)
60
+ return [
61
+ // Compare existing worker registration by its worker URL,
62
+ // to prevent irrelevant workers to resolve here (such as Codesandbox worker).
63
+ getWorkerByRegistration(registration, absoluteWorkerUrl, findWorker),
64
+ registration,
65
+ ]
66
+ })
66
67
 
67
68
  // Handle Service Worker registration errors.
68
- if (registrationResult.error) {
69
- const isWorkerMissing = registrationResult.error.message.includes('(404)')
69
+ if (registrationError) {
70
+ const isWorkerMissing = registrationError.message.includes('(404)')
70
71
 
71
72
  // Produce a custom error message when given a non-existing Service Worker url.
72
73
  // Suggest developers to check their setup.
@@ -87,10 +88,10 @@ Learn more about creating the Service Worker script: https://mswjs.io/docs/cli/i
87
88
  throw new Error(
88
89
  devUtils.formatMessage(
89
90
  'Failed to register the Service Worker:\n\n%s',
90
- registrationResult.error.message,
91
+ registrationError.message,
91
92
  ),
92
93
  )
93
94
  }
94
95
 
95
- return registrationResult.data
96
+ return registrationResult
96
97
  }
@@ -19,7 +19,6 @@ export function printStartMessage(args: PrintStartMessageArgs = {}) {
19
19
 
20
20
  const message = args.message || 'Mocking enabled.'
21
21
 
22
- // eslint-disable-next-line no-console
23
22
  console.groupCollapsed(
24
23
  `%c${devUtils.formatMessage(message)}`,
25
24
  'color:orangered;font-weight:bold;',
@@ -48,6 +47,5 @@ export function printStartMessage(args: PrintStartMessageArgs = {}) {
48
47
  console.log('Client ID: %s (%s)', args.client.id, args.client.frameType)
49
48
  }
50
49
 
51
- // eslint-disable-next-line no-console
52
50
  console.groupEnd()
53
51
  }
@@ -1,34 +1,42 @@
1
1
  import { devUtils } from '~/core/utils/internal/devUtils'
2
2
  import type { SetupWorkerInternalContext } from '../setupWorker/glossary'
3
+ import { DeferredPromise } from '@open-draft/deferred-promise'
3
4
 
4
5
  /**
5
6
  * Check whether the registered Service Worker has been
6
7
  * generated by the installed version of the library.
7
8
  * Prints a warning message if the worker scripts mismatch.
8
9
  */
9
- export async function checkWorkerIntegrity(
10
+ export function checkWorkerIntegrity(
10
11
  context: SetupWorkerInternalContext,
11
12
  ): Promise<void> {
12
- // Request the integrity checksum from the registered worker.
13
- context.workerChannel.send('INTEGRITY_CHECK_REQUEST')
13
+ const integrityCheckPromise = new DeferredPromise<void>()
14
14
 
15
- const { payload } = await context.events.once('INTEGRITY_CHECK_RESPONSE')
15
+ // Request the integrity checksum from the registered worker.
16
+ context.workerChannel.postMessage('INTEGRITY_CHECK_REQUEST')
17
+ context.workerChannel.once('INTEGRITY_CHECK_RESPONSE', (event) => {
18
+ const { checksum, packageVersion } = event.data
16
19
 
17
- // Compare the response from the Service Worker and the
18
- // global variable set during the build.
20
+ // Compare the response from the Service Worker and the
21
+ // global variable set during the build.
19
22
 
20
- // The integrity is validated based on the worker script's checksum
21
- // that's derived from its minified content during the build.
22
- // The "SERVICE_WORKER_CHECKSUM" global variable is injected by the build.
23
- if (payload.checksum !== SERVICE_WORKER_CHECKSUM) {
24
- devUtils.warn(
25
- `The currently registered Service Worker has been generated by a different version of MSW (${payload.packageVersion}) and may not be fully compatible with the installed version.
23
+ // The integrity is validated based on the worker script's checksum
24
+ // that's derived from its minified content during the build.
25
+ // The "SERVICE_WORKER_CHECKSUM" global variable is injected by the build.
26
+ if (checksum !== SERVICE_WORKER_CHECKSUM) {
27
+ devUtils.warn(
28
+ `The currently registered Service Worker has been generated by a different version of MSW (${packageVersion}) and may not be fully compatible with the installed version.
26
29
 
27
30
  It's recommended you update your worker script by running this command:
28
31
 
29
32
  \u2022 npx msw init <PUBLIC_DIR>
30
33
 
31
34
  You can also automate this process and make the worker script update automatically upon the library installations. Read more: https://mswjs.io/docs/cli/init.`,
32
- )
33
- }
35
+ )
36
+ }
37
+
38
+ integrityCheckPromise.resolve()
39
+ })
40
+
41
+ return integrityCheckPromise
34
42
  }
@@ -1,4 +1,4 @@
1
- import { until } from '@open-draft/until'
1
+ import { until } from 'until-async'
2
2
 
3
3
  /**
4
4
  * Intercepts and defers any requests on the page
@@ -0,0 +1,146 @@
1
+ import { Emitter, TypedEvent } from 'rettime'
2
+ import { isObject } from '~/core/utils/internal/isObject'
3
+ import type { StringifiedResponse } from '../setupWorker/glossary'
4
+
5
+ export interface WorkerChannelOptions {
6
+ worker: Promise<ServiceWorker>
7
+ }
8
+
9
+ export type WorkerChannelEventMap = {
10
+ REQUEST: WorkerEvent<IncomingWorkerRequest>
11
+ RESPONSE: WorkerEvent<IncomingWorkerResponse>
12
+ MOCKING_ENABLED: WorkerEvent<{
13
+ client: {
14
+ id: string
15
+ frameType: string
16
+ }
17
+ }>
18
+ INTEGRITY_CHECK_RESPONSE: WorkerEvent<{
19
+ packageVersion: string
20
+ checksum: string
21
+ }>
22
+ KEEPALIVE_RESPONSE: TypedEvent<never>
23
+ }
24
+
25
+ /**
26
+ * Request representation received from the worker message event.
27
+ */
28
+ export interface IncomingWorkerRequest
29
+ extends Omit<
30
+ Request,
31
+ | 'text'
32
+ | 'body'
33
+ | 'json'
34
+ | 'blob'
35
+ | 'arrayBuffer'
36
+ | 'formData'
37
+ | 'clone'
38
+ | 'signal'
39
+ | 'isHistoryNavigation'
40
+ | 'isReloadNavigation'
41
+ > {
42
+ /**
43
+ * Unique ID of the request generated once the request is
44
+ * intercepted by the "fetch" event in the Service Worker.
45
+ */
46
+ id: string
47
+ interceptedAt: number
48
+ body?: ArrayBuffer | null
49
+ }
50
+
51
+ type IncomingWorkerResponse = {
52
+ isMockedResponse: boolean
53
+ request: IncomingWorkerRequest
54
+ response: Pick<
55
+ Response,
56
+ 'type' | 'ok' | 'status' | 'statusText' | 'body' | 'headers' | 'redirected'
57
+ >
58
+ }
59
+
60
+ export type WorkerEventResponse = {
61
+ MOCK_RESPONSE: [
62
+ data: StringifiedResponse,
63
+ transfer?: [ReadableStream<Uint8Array>],
64
+ ]
65
+ PASSTHROUGH: []
66
+ }
67
+
68
+ export class WorkerEvent<
69
+ DataType,
70
+ ReturnType = any,
71
+ EventType extends string = string,
72
+ > extends TypedEvent<DataType, ReturnType, EventType> {
73
+ #workerEvent: MessageEvent
74
+
75
+ constructor(workerEvent: MessageEvent) {
76
+ const type = workerEvent.data.type as EventType
77
+ const data = workerEvent.data.payload as DataType
78
+
79
+ /**
80
+ * @note This is the only place we're mapping { type, payload }
81
+ * message structure of the worker. The client references the
82
+ * payload via `event.data`.
83
+ */
84
+ super(
85
+ // @ts-expect-error Troublesome `TypedEvent` extension.
86
+ type,
87
+ { data },
88
+ )
89
+ this.#workerEvent = workerEvent
90
+ }
91
+
92
+ get ports() {
93
+ return this.#workerEvent.ports
94
+ }
95
+
96
+ /**
97
+ * Reply directly to this event using its `MessagePort`.
98
+ */
99
+ public postMessage<Type extends keyof WorkerEventResponse>(
100
+ type: Type,
101
+ ...rest: WorkerEventResponse[Type]
102
+ ): void {
103
+ this.#workerEvent.ports[0].postMessage(
104
+ { type, data: rest[0] },
105
+ { transfer: rest[1] },
106
+ )
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Map of the events that can be sent to the Service Worker
112
+ * from any execution context.
113
+ */
114
+ type OutgoingWorkerEvents =
115
+ | 'MOCK_ACTIVATE'
116
+ | 'INTEGRITY_CHECK_REQUEST'
117
+ | 'KEEPALIVE_REQUEST'
118
+ | 'CLIENT_CLOSED'
119
+
120
+ export class WorkerChannel extends Emitter<WorkerChannelEventMap> {
121
+ constructor(protected readonly options: WorkerChannelOptions) {
122
+ super()
123
+
124
+ navigator.serviceWorker.addEventListener('message', async (event) => {
125
+ const worker = await this.options.worker
126
+
127
+ if (event.source != null && event.source !== worker) {
128
+ return
129
+ }
130
+
131
+ if (event.data && isObject(event.data) && 'type' in event.data) {
132
+ this.emit(new WorkerEvent<any, any, any>(event))
133
+ }
134
+ })
135
+ }
136
+
137
+ /**
138
+ * Send data to the Service Worker controlling this client.
139
+ * This triggers the `message` event listener on ServiceWorkerGlobalScope.
140
+ */
141
+ public postMessage(type: OutgoingWorkerEvents): void {
142
+ this.options.worker.then((worker) => {
143
+ worker.postMessage(type)
144
+ })
145
+ }
146
+ }
@@ -176,7 +176,6 @@ export class GraphQLHandler extends RequestHandler<
176
176
  GraphQLHandler.parsedRequestCache.set(
177
177
  request,
178
178
  await parseGraphQLRequest(request).catch((error) => {
179
- // eslint-disable-next-line no-console
180
179
  console.error(error)
181
180
  return undefined
182
181
  }),
@@ -318,7 +317,6 @@ Consider naming this operation or using "graphql.operation()" request handler to
318
317
  ? `${args.parsedResult.operationType} ${args.parsedResult.operationName}`
319
318
  : `anonymous ${args.parsedResult.operationType}`
320
319
 
321
- // eslint-disable-next-line no-console
322
320
  console.groupCollapsed(
323
321
  devUtils.formatMessage(
324
322
  `${getTimestamp()} ${requestInfo} (%c${loggedResponse.status} ${
@@ -334,7 +332,6 @@ Consider naming this operation or using "graphql.operation()" request handler to
334
332
  console.log('Handler:', this)
335
333
  // eslint-disable-next-line no-console
336
334
  console.log('Response:', loggedResponse)
337
- // eslint-disable-next-line no-console
338
335
  console.groupEnd()
339
336
  }
340
337
  }
@@ -201,7 +201,6 @@ export class HttpHandler extends RequestHandler<
201
201
  const loggedResponse = await serializeResponse(args.response)
202
202
  const statusColor = getStatusCodeColor(loggedResponse.status)
203
203
 
204
- // eslint-disable-next-line no-console
205
204
  console.groupCollapsed(
206
205
  devUtils.formatMessage(
207
206
  `${getTimestamp()} ${args.request.method} ${publicUrl} (%c${
@@ -217,7 +216,6 @@ export class HttpHandler extends RequestHandler<
217
216
  console.log('Handler:', this)
218
217
  // eslint-disable-next-line no-console
219
218
  console.log('Response', loggedResponse)
220
- // eslint-disable-next-line no-console
221
219
  console.groupEnd()
222
220
  }
223
221
  }