msw 0.20.5 → 0.21.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 (74) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -1
  3. package/lib/esm/fetch-deps.js +26 -23
  4. package/lib/esm/graphql.js +92 -79
  5. package/lib/esm/index.js +90 -44
  6. package/lib/esm/{matchRequest-deps.js → matchRequestUrl-deps.js} +29 -17
  7. package/lib/esm/mockServiceWorker.js +8 -1
  8. package/lib/esm/rest-deps.js +27 -18
  9. package/lib/esm/rest.js +1 -1
  10. package/lib/esm/xml-deps.js +3 -3
  11. package/lib/types/LiveStorage.d.ts +17 -0
  12. package/lib/types/context/body.d.ts +2 -2
  13. package/lib/types/context/errors.d.ts +3 -1
  14. package/lib/types/context/fetch.d.ts +2 -2
  15. package/lib/types/context/json.d.ts +5 -3
  16. package/lib/types/context/text.d.ts +2 -2
  17. package/lib/types/context/xml.d.ts +2 -2
  18. package/lib/types/graphql.d.ts +8 -10
  19. package/lib/types/index.d.ts +3 -3
  20. package/lib/types/native/index.d.ts +7 -1
  21. package/lib/types/node/createSetupServer.d.ts +29 -0
  22. package/lib/types/node/setupServer.d.ts +4 -24
  23. package/lib/types/response.d.ts +10 -7
  24. package/lib/types/rest.d.ts +158 -16
  25. package/lib/types/setupWorker/glossary.d.ts +14 -4
  26. package/lib/types/setupWorker/start/utils/getWorkerByRegistration.d.ts +2 -1
  27. package/lib/types/setupWorker/start/utils/getWorkerInstance.d.ts +2 -2
  28. package/lib/types/sharedOptions.d.ts +1 -1
  29. package/lib/types/utils/getResponse.d.ts +2 -2
  30. package/lib/types/utils/handlers/requestHandler.d.ts +74 -0
  31. package/lib/types/utils/{requestHandlerUtils.d.ts → handlers/requestHandlerUtils.d.ts} +2 -2
  32. package/lib/types/utils/{isNodeProcess.d.ts → internal/isNodeProcess.d.ts} +1 -0
  33. package/lib/types/utils/{isStringEqual.d.ts → internal/isStringEqual.d.ts} +0 -0
  34. package/lib/types/utils/{jsonParse.d.ts → internal/jsonParse.d.ts} +0 -0
  35. package/lib/types/utils/internal/mergeRight.d.ts +5 -0
  36. package/lib/types/utils/{logger → logging}/getStatusCodeColor.d.ts +0 -0
  37. package/lib/types/utils/{logger → logging}/getTimestamp.d.ts +0 -0
  38. package/lib/types/utils/{logger → logging}/prepareRequest.d.ts +3 -3
  39. package/lib/types/utils/{logger → logging}/prepareResponse.d.ts +2 -2
  40. package/lib/types/utils/matching/{matchRequest.d.ts → matchRequestUrl.d.ts} +0 -0
  41. package/lib/types/utils/request/getPublicUrlFromRequest.d.ts +6 -0
  42. package/lib/types/utils/request/getRequestCookies.d.ts +1 -1
  43. package/lib/types/{onUnhandledRequest.d.ts → utils/request/onUnhandledRequest.d.ts} +1 -1
  44. package/lib/types/utils/request/parseBody.d.ts +5 -0
  45. package/lib/types/utils/url/getAbsoluteUrl.d.ts +6 -0
  46. package/lib/types/utils/{getAbsoluteWorkerUrl.d.ts → url/getAbsoluteWorkerUrl.d.ts} +0 -0
  47. package/lib/types/utils/url/getUrlByMask.d.ts +5 -0
  48. package/lib/umd/index.js +696 -613
  49. package/lib/umd/mockServiceWorker.js +8 -1
  50. package/native/index.js +1508 -133
  51. package/node/context/delay.d.ts +11 -0
  52. package/node/context/fetch.d.ts +8 -0
  53. package/node/context/set.d.ts +2 -0
  54. package/node/context/status.d.ts +2 -0
  55. package/node/index.js +1507 -133
  56. package/node/node/createSetupServer.d.ts +29 -0
  57. package/node/node/index.d.ts +5 -0
  58. package/node/node/setupServer.d.ts +7 -0
  59. package/node/response.d.ts +25 -0
  60. package/node/utils/NetworkError.d.ts +3 -0
  61. package/node/utils/getResponse.d.ts +14 -0
  62. package/{lib/types → node/utils}/handlers/requestHandler.d.ts +13 -12
  63. package/node/utils/handlers/requestHandlerUtils.d.ts +4 -0
  64. package/node/utils/internal/compose.d.ts +5 -0
  65. package/node/utils/internal/isNodeProcess.d.ts +5 -0
  66. package/node/utils/internal/jsonParse.d.ts +5 -0
  67. package/node/utils/request/getPublicUrlFromRequest.d.ts +6 -0
  68. package/node/utils/request/onUnhandledRequest.d.ts +5 -0
  69. package/node/utils/request/parseBody.d.ts +5 -0
  70. package/package.json +34 -29
  71. package/lib/types/utils/getJsonBody.d.ts +0 -5
  72. package/lib/types/utils/request/parseRequestBody.d.ts +0 -2
  73. package/lib/types/utils/resolveMask.d.ts +0 -6
  74. package/lib/types/utils/resolveRelativeUrl.d.ts +0 -6
package/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # Change Log
2
+
3
+ This project adheres to [Semantic Versioning](https://semver.org/).
4
+ Every release, along with the migration instructions, is documented on the Github [Releases](https://github.com/mswjs/msw/releases) page.
package/README.md CHANGED
@@ -61,7 +61,7 @@ Browser usage is what sets Mock Service Worker apart from other tools. Utilizing
61
61
  ### How is it different?
62
62
 
63
63
  - Intercepts requests on the network level, not the application level.
64
- - If your think of your application as a box, Mock Service Worker lives in its own box next to yours, instead of opening and altering it for the purpose of mocking.
64
+ - If you think of your application as a box, Mock Service Worker lives in its own box next to yours, instead of opening and altering it for the purpose of mocking.
65
65
  - Agnostic of request-issuing libraries, so you can use it with `fetch`, `axios`, `react-query`, you-name-it.
66
66
  - The same mock definition can be reused for unit, integration, E2E testing, and debugging.
67
67
 
@@ -82,11 +82,11 @@ var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof win
82
82
 
83
83
  function createCommonjsModule(fn, basedir, module) {
84
84
  return module = {
85
- path: basedir,
86
- exports: {},
87
- require: function (path, base) {
88
- return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
89
- }
85
+ path: basedir,
86
+ exports: {},
87
+ require: function (path, base) {
88
+ return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
89
+ }
90
90
  }, fn(module, module.exports), module.exports;
91
91
  }
92
92
 
@@ -350,16 +350,16 @@ function set(...args) {
350
350
 
351
351
  /**
352
352
  * Returns a boolean indicating if the current process is running in NodeJS environment.
353
+ * @see https://github.com/mswjs/msw/pull/255
353
354
  */
354
- // Please see https://github.com/mswjs/msw/pull/255
355
355
  function isNodeProcess() {
356
+ // Check browser environment.
356
357
  if (typeof global !== 'object') {
357
- // check browser environment
358
358
  return false;
359
359
  }
360
+ // Check nodejs or React Native environment.
360
361
  if (Object.prototype.toString.call(global.process) === '[object process]' ||
361
362
  navigator.product === 'ReactNative') {
362
- // check nodejs or react native environment
363
363
  return true;
364
364
  }
365
365
  }
@@ -388,9 +388,11 @@ const delay = (durationMs) => {
388
388
  };
389
389
 
390
390
  /**
391
- * Sets the given Object as the JSON body of the response.
391
+ * Sets the given value as the JSON body of the response.
392
392
  * @example
393
- * res(json({ foo: 'bar' }))
393
+ * res(json({ key: 'value' }))
394
+ * res(json('Some string'))
395
+ * res(json([1, '2', false, { ok: true }]))
394
396
  */
395
397
  const json = (body) => {
396
398
  return (res) => {
@@ -401,20 +403,21 @@ const json = (body) => {
401
403
  };
402
404
 
403
405
  const useFetch = isNodeProcess() ? require('node-fetch') : window.fetch;
404
- const gracefully = (promise) => {
405
- return promise.then((res) => {
406
- var _a;
407
- if ((_a = res.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('json')) {
408
- return res.json();
409
- }
410
- return res.text();
411
- });
412
- };
413
406
  const augmentRequestInit = (requestInit) => {
414
407
  const headers = new lib.Headers(requestInit.headers);
415
408
  headers.set('x-msw-bypass', 'true');
416
409
  return Object.assign(Object.assign({}, requestInit), { headers: headers.getAllHeaders() });
417
410
  };
411
+ const createFetchRequestParameters = (input) => {
412
+ const { body, method } = input;
413
+ const requestParameters = Object.assign(Object.assign({}, input), { body: undefined });
414
+ if (['GET', 'HEAD'].includes(method)) {
415
+ return requestParameters;
416
+ }
417
+ requestParameters.body =
418
+ typeof body === 'object' ? JSON.stringify(body) : body;
419
+ return requestParameters;
420
+ };
418
421
  /**
419
422
  * Wrapper around the native `window.fetch()` function that performs
420
423
  * a request bypassing MSW. Requests performed using
@@ -423,11 +426,11 @@ const augmentRequestInit = (requestInit) => {
423
426
  const fetch = (input, requestInit = {}) => {
424
427
  // Keep the default `window.fetch()` call signature
425
428
  if (typeof input === 'string') {
426
- return gracefully(useFetch(input, augmentRequestInit(requestInit)));
429
+ return useFetch(input, augmentRequestInit(requestInit));
427
430
  }
428
- const { body } = input;
429
- const compliantReq = augmentRequestInit(Object.assign(Object.assign({}, input), { body: typeof body === 'object' ? JSON.stringify(body) : body }));
430
- return gracefully(useFetch(input.url.href, compliantReq));
431
+ const requestParameters = createFetchRequestParameters(input);
432
+ const compliantRequest = augmentRequestInit(requestParameters);
433
+ return useFetch(input.url.href, compliantRequest);
431
434
  };
432
435
 
433
436
  export { set as a, commonjsGlobal as b, createCommonjsModule as c, delay as d, fetch as f, isNodeProcess as i, json as j, lib as l, status as s };
@@ -1,6 +1,6 @@
1
1
  import { a as set, s as status, d as delay, f as fetch } from './fetch-deps.js';
2
2
  import { d as data, e as errors } from './errors-deps.js';
3
- import { j as jsonParse, m as matchRequestUrl, p as prepareRequest, a as prepareResponse, b as getTimestamp, c as getStatusCodeColor } from './matchRequest-deps.js';
3
+ import { j as jsonParse, m as matchRequestUrl, a as prepareRequest, b as prepareResponse, c as getTimestamp, d as getStatusCodeColor } from './matchRequestUrl-deps.js';
4
4
 
5
5
  // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2317')
6
6
  var nodejsCustomInspectSymbol = typeof Symbol === 'function' && typeof Symbol.for === 'function' ? Symbol.for('nodejs.util.inspect.custom') : undefined;
@@ -3009,98 +3009,111 @@ const graphqlContext = {
3009
3009
  function parseQuery(query, definitionOperation = 'query') {
3010
3010
  var _a;
3011
3011
  const ast = parse(query);
3012
- const operationDef = ast.definitions.find((def) => def.kind === 'OperationDefinition' &&
3013
- def.operation === definitionOperation);
3012
+ const operationDef = ast.definitions.find((def) => {
3013
+ return (def.kind === 'OperationDefinition' &&
3014
+ (definitionOperation === 'all' || def.operation === definitionOperation));
3015
+ });
3014
3016
  return {
3017
+ operationType: operationDef === null || operationDef === void 0 ? void 0 : operationDef.operation,
3015
3018
  operationName: (_a = operationDef === null || operationDef === void 0 ? void 0 : operationDef.name) === null || _a === void 0 ? void 0 : _a.value,
3016
3019
  };
3017
3020
  }
3018
- const createGraphQLHandler = (operationType, mask) => {
3019
- return (expectedOperation, resolver) => {
3020
- return {
3021
- resolver,
3022
- parse(req) {
3023
- var _a;
3024
- // According to the GraphQL specification, a GraphQL request can be issued
3025
- // using both "GET" and "POST" methods.
3026
- switch (req.method) {
3027
- case 'GET': {
3028
- const query = req.url.searchParams.get('query');
3029
- const variablesString = req.url.searchParams.get('variables') || '';
3030
- if (!query) {
3031
- return null;
3032
- }
3033
- const variables = variablesString
3034
- ? jsonParse(variablesString)
3035
- : {};
3036
- const { operationName } = parseQuery(query, operationType);
3037
- return {
3038
- operationType,
3039
- operationName,
3040
- variables,
3041
- };
3042
- }
3043
- case 'POST': {
3044
- if (!((_a = req.body) === null || _a === void 0 ? void 0 : _a.query)) {
3045
- return null;
3046
- }
3047
- const { query, variables } = req.body;
3048
- const { operationName } = parseQuery(query, operationType);
3049
- return {
3050
- operationType,
3051
- operationName,
3052
- variables,
3053
- };
3054
- }
3055
- default:
3021
+ function graphQLRequestHandler(expectedOperationType, expectedOperationName, mask, resolver) {
3022
+ return {
3023
+ resolver,
3024
+ parse(req) {
3025
+ var _a;
3026
+ // According to the GraphQL specification, a GraphQL request can be issued
3027
+ // using both "GET" and "POST" methods.
3028
+ switch (req.method) {
3029
+ case 'GET': {
3030
+ const query = req.url.searchParams.get('query');
3031
+ const variablesString = req.url.searchParams.get('variables') || '';
3032
+ if (!query) {
3056
3033
  return null;
3034
+ }
3035
+ const variables = variablesString
3036
+ ? jsonParse(variablesString)
3037
+ : {};
3038
+ const { operationType, operationName } = parseQuery(query, expectedOperationType);
3039
+ return {
3040
+ operationType,
3041
+ operationName,
3042
+ variables,
3043
+ };
3057
3044
  }
3058
- },
3059
- getPublicRequest(req, parsed) {
3060
- return Object.assign(Object.assign({}, req), { variables: parsed.variables || {} });
3061
- },
3062
- predicate(req, parsed) {
3063
- if (!parsed || !parsed.operationName) {
3064
- return false;
3045
+ case 'POST': {
3046
+ if (!((_a = req.body) === null || _a === void 0 ? void 0 : _a.query)) {
3047
+ return null;
3048
+ }
3049
+ const { query, variables } = req.body;
3050
+ const { operationType, operationName } = parseQuery(query, expectedOperationType);
3051
+ return {
3052
+ operationType,
3053
+ operationName,
3054
+ variables,
3055
+ };
3065
3056
  }
3066
- // Match the request URL against a given mask,
3067
- // in case of an endpoint-specific request handler.
3068
- const hasMatchingMask = matchRequestUrl(req.url, mask);
3069
- const isMatchingOperation = expectedOperation instanceof RegExp
3070
- ? expectedOperation.test(parsed.operationName)
3071
- : expectedOperation === parsed.operationName;
3072
- return isMatchingOperation && hasMatchingMask.matches;
3073
- },
3074
- defineContext() {
3075
- return graphqlContext;
3076
- },
3077
- log(req, res, handler, parsed) {
3078
- const { operationName } = parsed;
3079
- const loggedRequest = prepareRequest(req);
3080
- const loggedResponse = prepareResponse(res);
3081
- console.groupCollapsed('[MSW] %s %s (%c%s%c)', getTimestamp(), operationName, `color:${getStatusCodeColor(res.status)}`, res.status, 'color:inherit');
3082
- console.log('Request:', loggedRequest);
3083
- console.log('Handler:', {
3084
- operationType,
3085
- operationName: expectedOperation,
3086
- predicate: handler.predicate,
3087
- });
3088
- console.log('Response:', loggedResponse);
3089
- console.groupEnd();
3090
- },
3091
- };
3057
+ default:
3058
+ return null;
3059
+ }
3060
+ },
3061
+ getPublicRequest(req, parsed) {
3062
+ return Object.assign(Object.assign({}, req), { variables: parsed.variables || {} });
3063
+ },
3064
+ predicate(req, parsed) {
3065
+ if (!parsed || !parsed.operationName) {
3066
+ return false;
3067
+ }
3068
+ // Match the request URL against a given mask,
3069
+ // in case of an endpoint-specific request handler.
3070
+ const hasMatchingMask = matchRequestUrl(req.url, mask);
3071
+ const isMatchingOperation = expectedOperationName instanceof RegExp
3072
+ ? expectedOperationName.test(parsed.operationName)
3073
+ : expectedOperationName === parsed.operationName;
3074
+ return hasMatchingMask.matches && isMatchingOperation;
3075
+ },
3076
+ defineContext() {
3077
+ return graphqlContext;
3078
+ },
3079
+ log(req, res, handler, parsed) {
3080
+ const { operationType, operationName } = parsed;
3081
+ const loggedRequest = prepareRequest(req);
3082
+ const loggedResponse = prepareResponse(res);
3083
+ console.groupCollapsed('[MSW] %s %s (%c%s%c)', getTimestamp(), operationName, `color:${getStatusCodeColor(res.status)}`, res.status, 'color:inherit');
3084
+ console.log('Request:', loggedRequest);
3085
+ console.log('Handler:', {
3086
+ operationType,
3087
+ operationName: expectedOperationName,
3088
+ predicate: handler.predicate,
3089
+ });
3090
+ console.log('Response:', loggedResponse);
3091
+ console.groupEnd();
3092
+ },
3093
+ };
3094
+ }
3095
+ const createGraphQLScopedHandler = (expectedOperationType, mask) => {
3096
+ return (expectedOperationName, resolver) => {
3097
+ return graphQLRequestHandler(expectedOperationType, expectedOperationName, mask, resolver);
3098
+ };
3099
+ };
3100
+ const createGraphQLOperationHandler = (mask) => {
3101
+ return (resolver) => {
3102
+ return graphQLRequestHandler('all', new RegExp('.*'), mask, resolver);
3092
3103
  };
3093
3104
  };
3094
3105
  const graphqlStandardHandlers = {
3095
- query: createGraphQLHandler('query', '*'),
3096
- mutation: createGraphQLHandler('mutation', '*'),
3106
+ operation: createGraphQLOperationHandler('*'),
3107
+ query: createGraphQLScopedHandler('query', '*'),
3108
+ mutation: createGraphQLScopedHandler('mutation', '*'),
3097
3109
  };
3098
3110
  function createGraphQLLink(uri) {
3099
3111
  return {
3100
- query: createGraphQLHandler('query', uri),
3101
- mutation: createGraphQLHandler('mutation', uri),
3112
+ operation: createGraphQLOperationHandler(uri),
3113
+ query: createGraphQLScopedHandler('query', uri),
3114
+ mutation: createGraphQLScopedHandler('mutation', uri),
3102
3115
  };
3103
3116
  }
3104
3117
  const graphql = Object.assign(Object.assign({}, graphqlStandardHandlers), { link: createGraphQLLink });
3105
3118
 
3106
- export { graphql, graphqlContext, parseQuery };
3119
+ export { graphql, graphqlContext };
package/lib/esm/index.js CHANGED
@@ -2,25 +2,25 @@ import { c as createCommonjsModule, s as status, a as set, d as delay, f as fetc
2
2
  import { p as parse_1 } from './xml-deps.js';
3
3
  import './errors-deps.js';
4
4
  export { i as context } from './index-deps.js';
5
- import { g as getJsonBody } from './matchRequest-deps.js';
6
- export { m as matchRequestUrl } from './matchRequest-deps.js';
7
- import { i as isStringEqual } from './rest-deps.js';
5
+ import { g as getPublicUrlFromRequest, i as isStringEqual } from './rest-deps.js';
8
6
  export { R as RESTMethods, r as rest, a as restContext } from './rest-deps.js';
7
+ import { p as parseBody } from './matchRequestUrl-deps.js';
8
+ export { m as matchRequestUrl } from './matchRequestUrl-deps.js';
9
9
  export { graphql, graphqlContext } from './graphql.js';
10
10
 
11
11
  /*! *****************************************************************************
12
- Copyright (c) Microsoft Corporation. All rights reserved.
13
- Licensed under the Apache License, Version 2.0 (the "License"); you may not use
14
- this file except in compliance with the License. You may obtain a copy of the
15
- License at http://www.apache.org/licenses/LICENSE-2.0
12
+ Copyright (c) Microsoft Corporation.
16
13
 
17
- THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18
- KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
19
- WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
20
- MERCHANTABLITY OR NON-INFRINGEMENT.
14
+ Permission to use, copy, modify, and/or distribute this software for any
15
+ purpose with or without fee is hereby granted.
21
16
 
22
- See the Apache Version 2.0 License for specific language governing permissions
23
- and limitations under the License.
17
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
18
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
19
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
20
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
21
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
22
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23
+ PERFORMANCE OF THIS SOFTWARE.
24
24
  ***************************************************************************** */
25
25
 
26
26
  function __awaiter(thisArg, _arguments, P, generator) {
@@ -63,7 +63,7 @@ exports.until = until.until;
63
63
  * Attempts to resolve a Service Worker instance from a given registration,
64
64
  * regardless of its state (active, installing, waiting).
65
65
  */
66
- const getWorkerByRegistration = (registration, absoluteWorkerUrl) => {
66
+ const getWorkerByRegistration = (registration, absoluteWorkerUrl, findWorker) => {
67
67
  const allStates = [
68
68
  registration.active,
69
69
  registration.installing,
@@ -71,7 +71,7 @@ const getWorkerByRegistration = (registration, absoluteWorkerUrl) => {
71
71
  ];
72
72
  const existingStates = allStates.filter(Boolean);
73
73
  const mockWorker = existingStates.find((worker) => {
74
- return worker.scriptURL === absoluteWorkerUrl;
74
+ return findWorker(worker.scriptURL, absoluteWorkerUrl);
75
75
  });
76
76
  return mockWorker || null;
77
77
  };
@@ -88,13 +88,13 @@ function getAbsoluteWorkerUrl(relativeUrl) {
88
88
  * Returns an active Service Worker instance.
89
89
  * When not found, registers a new Service Worker.
90
90
  */
91
- const getWorkerInstance = (url, options) => __awaiter(void 0, void 0, void 0, function* () {
92
- // Resolve the absolute Service Worker URL
91
+ const getWorkerInstance = (url, options = {}, findWorker) => __awaiter(void 0, void 0, void 0, function* () {
92
+ // Resolve the absolute Service Worker URL.
93
93
  const absoluteWorkerUrl = getAbsoluteWorkerUrl(url);
94
94
  const [, mockRegistrations] = yield lib.until(() => __awaiter(void 0, void 0, void 0, function* () {
95
95
  const registrations = yield navigator.serviceWorker.getRegistrations();
96
96
  return registrations.filter((registration) => {
97
- return getWorkerByRegistration(registration, absoluteWorkerUrl);
97
+ return getWorkerByRegistration(registration, absoluteWorkerUrl, findWorker);
98
98
  });
99
99
  }));
100
100
  if (!navigator.serviceWorker.controller && mockRegistrations.length > 0) {
@@ -108,23 +108,25 @@ const getWorkerInstance = (url, options) => __awaiter(void 0, void 0, void 0, fu
108
108
  }
109
109
  const [existingRegistration] = mockRegistrations;
110
110
  if (existingRegistration) {
111
- // Update existing service worker to ensure it's up-to-date
111
+ // When the Service Worker is registered, update it and return the reference.
112
112
  return existingRegistration.update().then(() => {
113
113
  return [
114
- getWorkerByRegistration(existingRegistration, absoluteWorkerUrl),
114
+ getWorkerByRegistration(existingRegistration, absoluteWorkerUrl, findWorker),
115
115
  existingRegistration,
116
116
  ];
117
117
  });
118
118
  }
119
+ // When the Service Worker wasn't found, register it anew and return the reference.
119
120
  const [error, instance] = yield lib.until(() => __awaiter(void 0, void 0, void 0, function* () {
120
121
  const registration = yield navigator.serviceWorker.register(url, options);
121
122
  return [
122
123
  // Compare existing worker registration by its worker URL,
123
124
  // to prevent irrelevant workers to resolve here (such as Codesandbox worker).
124
- getWorkerByRegistration(registration, absoluteWorkerUrl),
125
+ getWorkerByRegistration(registration, absoluteWorkerUrl, findWorker),
125
126
  registration,
126
127
  ];
127
128
  }));
129
+ // Handle Service Worker registration errors.
128
130
  if (error) {
129
131
  const isWorkerMissing = error.message.includes('(404)');
130
132
  // Produce a custom error message when given a non-existing Service Worker url.
@@ -307,7 +309,14 @@ function onUnhandledRequest(request, onUnhandledRequest = 'bypass') {
307
309
  onUnhandledRequest(request);
308
310
  return;
309
311
  }
310
- const message = `captured a ${request.method} ${request.url} request without a corresponding request handler.`;
312
+ const publicUrl = getPublicUrlFromRequest(request);
313
+ const message = `captured a ${request.method} ${request.url} request without a corresponding request handler.
314
+
315
+ If you wish to intercept this request, consider creating a request handler for it:
316
+
317
+ rest.${request.method.toLowerCase()}('${publicUrl}', (req, res, ctx) => {
318
+ return res(ctx.text('body'))
319
+ })`;
311
320
  switch (onUnhandledRequest) {
312
321
  case 'error': {
313
322
  throw new Error(`[MSW] Error: ${message}`);
@@ -320,21 +329,6 @@ function onUnhandledRequest(request, onUnhandledRequest = 'bypass') {
320
329
  }
321
330
  }
322
331
 
323
- function parseRequestBody(body, headers) {
324
- var _a;
325
- if (body) {
326
- // If the intercepted request's body has a JSON Content-Type
327
- // parse it into an object, otherwise leave as-is.
328
- const hasJsonContent = (_a = headers === null || headers === void 0 ? void 0 : headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('json');
329
- if (hasJsonContent && typeof body !== 'object') {
330
- return getJsonBody(body);
331
- }
332
- return body;
333
- }
334
- // Return whatever falsey body value is given.
335
- return body;
336
- }
337
-
338
332
  function getAllCookies() {
339
333
  return parse_1(document.cookie);
340
334
  }
@@ -389,7 +383,7 @@ const handleRequestWith = (context, options) => {
389
383
  return null;
390
384
  }
391
385
  // Parse the request's body based on the "Content-Type" header.
392
- req.body = parseRequestBody(req.body, req.headers);
386
+ req.body = parseBody(req.body, req.headers);
393
387
  // Set document cookies on the request.
394
388
  req.cookies = getRequestCookies(req);
395
389
  const { response, handler, publicRequest, parsedRequest, } = yield getResponse(req, context.requestHandlers);
@@ -452,8 +446,8 @@ function requestIntegrityCheck(context, serviceWorker) {
452
446
  const { payload: actualChecksum } = yield context.events.once('INTEGRITY_CHECK_RESPONSE');
453
447
  // Compare the response from the Service Worker and the
454
448
  // global variable set by webpack upon build.
455
- if (actualChecksum !== "ca2c3cd7453d8c614e2c19db63ede1a1") {
456
- throw new Error(`Currently active Service Worker (${actualChecksum}) is behind the latest published one (${"ca2c3cd7453d8c614e2c19db63ede1a1"}).`);
449
+ if (actualChecksum !== "d1e0e502f550d40a34bee90822e4bf98") {
450
+ throw new Error(`Currently active Service Worker (${actualChecksum}) is behind the latest published one (${"d1e0e502f550d40a34bee90822e4bf98"}).`);
457
451
  }
458
452
  return serviceWorker;
459
453
  });
@@ -465,15 +459,17 @@ function requestIntegrityCheck(context, serviceWorker) {
465
459
  * Must only be used in a browser.
466
460
  */
467
461
  function deferNetworkRequestsUntil(predicatePromise) {
468
- // Defer `XMLHttpRequest` until the Service Worker is ready.
462
+ // Defer any `XMLHttpRequest` requests until the Service Worker is ready.
469
463
  const originalXhrSend = window.XMLHttpRequest.prototype.send;
470
464
  window.XMLHttpRequest.prototype.send = function (...args) {
465
+ // Keep this function synchronous to comply with `XMLHttpRequest.prototype.send`,
466
+ // because that method is always synchronous.
471
467
  lib.until(() => predicatePromise).then(() => {
472
468
  window.XMLHttpRequest.prototype.send = originalXhrSend;
473
469
  this.send(...args);
474
470
  });
475
471
  };
476
- // Defer `fetch` requests until the Service Worker is ready.
472
+ // Defer any `fetch` requests until the Service Worker is ready.
477
473
  const originalFetch = window.fetch;
478
474
  window.fetch = (...args) => __awaiter(this, void 0, void 0, function* () {
479
475
  yield lib.until(() => predicatePromise);
@@ -482,6 +478,30 @@ function deferNetworkRequestsUntil(predicatePromise) {
482
478
  });
483
479
  }
484
480
 
481
+ function isObject(obj) {
482
+ return typeof obj === 'object';
483
+ }
484
+ /**
485
+ * Deeply merges two given objects with the right one
486
+ * having a priority during property assignment.
487
+ */
488
+ function mergeRight(a, b) {
489
+ const result = Object.assign({}, a);
490
+ Object.entries(b).forEach(([key, value]) => {
491
+ const existingValue = result[key];
492
+ if (Array.isArray(existingValue) && Array.isArray(value)) {
493
+ result[key] = existingValue.concat(value);
494
+ return;
495
+ }
496
+ if (isObject(existingValue) && isObject(value)) {
497
+ result[key] = mergeRight(existingValue, value);
498
+ return;
499
+ }
500
+ result[key] = value;
501
+ });
502
+ return result;
503
+ }
504
+
485
505
  const DEFAULT_START_OPTIONS = {
486
506
  serviceWorker: {
487
507
  url: '/mockServiceWorker.js',
@@ -490,13 +510,14 @@ const DEFAULT_START_OPTIONS = {
490
510
  quiet: false,
491
511
  waitUntilReady: true,
492
512
  onUnhandledRequest: 'bypass',
513
+ findWorker: (scriptURL, mockServiceWorkerUrl) => scriptURL === mockServiceWorkerUrl,
493
514
  };
494
515
  const createStart = (context) => {
495
516
  /**
496
517
  * Registers and activates the mock Service Worker.
497
518
  */
498
519
  return function start(options) {
499
- const resolvedOptions = Object.assign({}, DEFAULT_START_OPTIONS, options);
520
+ const resolvedOptions = mergeRight(DEFAULT_START_OPTIONS, options || {});
500
521
  const startWorkerInstance = () => __awaiter(this, void 0, void 0, function* () {
501
522
  if (!('serviceWorker' in navigator)) {
502
523
  console.error(`[MSW] Failed to register a Service Worker: this browser does not support Service Workers (see https://caniuse.com/serviceworkers), or your application is running on an insecure host (consider using HTTPS for custom hostnames).`);
@@ -507,12 +528,28 @@ const createStart = (context) => {
507
528
  // of the application's code.
508
529
  context.events.removeAllListeners();
509
530
  context.events.addListener(navigator.serviceWorker, 'message', handleRequestWith(context, resolvedOptions));
510
- const [, instance] = yield lib.until(() => getWorkerInstance(resolvedOptions.serviceWorker.url, resolvedOptions.serviceWorker.options));
531
+ const [, instance] = yield lib.until(() => getWorkerInstance(resolvedOptions.serviceWorker.url, resolvedOptions.serviceWorker.options, resolvedOptions.findWorker));
511
532
  if (!instance) {
512
533
  return null;
513
534
  }
514
535
  const [worker, registration] = instance;
515
536
  if (!worker) {
537
+ if (options === null || options === void 0 ? void 0 : options.findWorker) {
538
+ console.error(`\
539
+ [MSW] Failed to locate the Service Worker registration using a custom "findWorker" predicate.
540
+
541
+ Please ensure that the custom predicate properly locates the Service Worker registration at "${resolvedOptions.serviceWorker.url}".
542
+ More details: https://mswjs.io/docs/api/setup-worker/start#findworker
543
+ `);
544
+ }
545
+ else {
546
+ console.error(`\
547
+ [MSW] Failed to locate the Service Worker registration.
548
+
549
+ This most likely means that the worker script URL "${resolvedOptions.serviceWorker.url}" cannot resolve against the actual public hostname (${location.host}). This may happen if your application runs behind a proxy, or has a dynamic hostname.
550
+
551
+ Please consider using a custom "serviceWorker.url" option to point to the actual worker script location, or a custom "findWorker" option to resolve the Service Worker registration manually. More details: https://mswjs.io/docs/api/setup-worker/start`);
552
+ }
516
553
  return null;
517
554
  }
518
555
  context.worker = worker;
@@ -525,6 +562,9 @@ const createStart = (context) => {
525
562
  // the Service Worker when there are no open clients.
526
563
  worker.postMessage('CLIENT_CLOSED');
527
564
  }
565
+ // Make sure we're always clearing the interval - there are reports that not doing this can
566
+ // cause memory leaks in headless browser environments.
567
+ window.clearInterval(context.keepAliveInterval);
528
568
  });
529
569
  // Check if the active Service Worker is the latest published one
530
570
  const [integrityError] = yield lib.until(() => requestIntegrityCheck(context, worker));
@@ -546,6 +586,7 @@ If this message still persists after updating, please report an issue: https://g
546
586
  console.error('Failed to enable mocking', activationError);
547
587
  return null;
548
588
  }
589
+ context.keepAliveInterval = window.setInterval(() => worker.postMessage('KEEPALIVE_REQUEST'), 5000);
549
590
  return registration;
550
591
  });
551
592
  const workerRegistration = startWorkerInstance();
@@ -569,6 +610,7 @@ const createStop = (context) => {
569
610
  var _a;
570
611
  (_a = context.worker) === null || _a === void 0 ? void 0 : _a.postMessage('MOCK_DEACTIVATE');
571
612
  context.events.removeAllListeners();
613
+ window.clearInterval(context.keepAliveInterval);
572
614
  };
573
615
  };
574
616
 
@@ -590,6 +632,10 @@ function resetHandlers(initialHandlers, ...nextHandlers) {
590
632
  // so it persists between Fash refreshes of the application's code.
591
633
  let listeners = [];
592
634
  function setupWorker(...requestHandlers) {
635
+ requestHandlers.forEach((handler) => {
636
+ if (Array.isArray(handler))
637
+ throw new Error(`[MSW] Failed to call "setupWorker" given an Array of request handlers (setupWorker([a, b])), expected to receive each handler individually: setupWorker(a, b).`);
638
+ });
593
639
  const context = {
594
640
  worker: null,
595
641
  registration: null,