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.
- package/CHANGELOG.md +4 -0
- package/README.md +1 -1
- package/lib/esm/fetch-deps.js +26 -23
- package/lib/esm/graphql.js +92 -79
- package/lib/esm/index.js +90 -44
- package/lib/esm/{matchRequest-deps.js → matchRequestUrl-deps.js} +29 -17
- package/lib/esm/mockServiceWorker.js +8 -1
- package/lib/esm/rest-deps.js +27 -18
- package/lib/esm/rest.js +1 -1
- package/lib/esm/xml-deps.js +3 -3
- package/lib/types/LiveStorage.d.ts +17 -0
- package/lib/types/context/body.d.ts +2 -2
- package/lib/types/context/errors.d.ts +3 -1
- package/lib/types/context/fetch.d.ts +2 -2
- package/lib/types/context/json.d.ts +5 -3
- package/lib/types/context/text.d.ts +2 -2
- package/lib/types/context/xml.d.ts +2 -2
- package/lib/types/graphql.d.ts +8 -10
- package/lib/types/index.d.ts +3 -3
- package/lib/types/native/index.d.ts +7 -1
- package/lib/types/node/createSetupServer.d.ts +29 -0
- package/lib/types/node/setupServer.d.ts +4 -24
- package/lib/types/response.d.ts +10 -7
- package/lib/types/rest.d.ts +158 -16
- package/lib/types/setupWorker/glossary.d.ts +14 -4
- package/lib/types/setupWorker/start/utils/getWorkerByRegistration.d.ts +2 -1
- package/lib/types/setupWorker/start/utils/getWorkerInstance.d.ts +2 -2
- package/lib/types/sharedOptions.d.ts +1 -1
- package/lib/types/utils/getResponse.d.ts +2 -2
- package/lib/types/utils/handlers/requestHandler.d.ts +74 -0
- package/lib/types/utils/{requestHandlerUtils.d.ts → handlers/requestHandlerUtils.d.ts} +2 -2
- package/lib/types/utils/{isNodeProcess.d.ts → internal/isNodeProcess.d.ts} +1 -0
- package/lib/types/utils/{isStringEqual.d.ts → internal/isStringEqual.d.ts} +0 -0
- package/lib/types/utils/{jsonParse.d.ts → internal/jsonParse.d.ts} +0 -0
- package/lib/types/utils/internal/mergeRight.d.ts +5 -0
- package/lib/types/utils/{logger → logging}/getStatusCodeColor.d.ts +0 -0
- package/lib/types/utils/{logger → logging}/getTimestamp.d.ts +0 -0
- package/lib/types/utils/{logger → logging}/prepareRequest.d.ts +3 -3
- package/lib/types/utils/{logger → logging}/prepareResponse.d.ts +2 -2
- package/lib/types/utils/matching/{matchRequest.d.ts → matchRequestUrl.d.ts} +0 -0
- package/lib/types/utils/request/getPublicUrlFromRequest.d.ts +6 -0
- package/lib/types/utils/request/getRequestCookies.d.ts +1 -1
- package/lib/types/{onUnhandledRequest.d.ts → utils/request/onUnhandledRequest.d.ts} +1 -1
- package/lib/types/utils/request/parseBody.d.ts +5 -0
- package/lib/types/utils/url/getAbsoluteUrl.d.ts +6 -0
- package/lib/types/utils/{getAbsoluteWorkerUrl.d.ts → url/getAbsoluteWorkerUrl.d.ts} +0 -0
- package/lib/types/utils/url/getUrlByMask.d.ts +5 -0
- package/lib/umd/index.js +696 -613
- package/lib/umd/mockServiceWorker.js +8 -1
- package/native/index.js +1508 -133
- package/node/context/delay.d.ts +11 -0
- package/node/context/fetch.d.ts +8 -0
- package/node/context/set.d.ts +2 -0
- package/node/context/status.d.ts +2 -0
- package/node/index.js +1507 -133
- package/node/node/createSetupServer.d.ts +29 -0
- package/node/node/index.d.ts +5 -0
- package/node/node/setupServer.d.ts +7 -0
- package/node/response.d.ts +25 -0
- package/node/utils/NetworkError.d.ts +3 -0
- package/node/utils/getResponse.d.ts +14 -0
- package/{lib/types → node/utils}/handlers/requestHandler.d.ts +13 -12
- package/node/utils/handlers/requestHandlerUtils.d.ts +4 -0
- package/node/utils/internal/compose.d.ts +5 -0
- package/node/utils/internal/isNodeProcess.d.ts +5 -0
- package/node/utils/internal/jsonParse.d.ts +5 -0
- package/node/utils/request/getPublicUrlFromRequest.d.ts +6 -0
- package/node/utils/request/onUnhandledRequest.d.ts +5 -0
- package/node/utils/request/parseBody.d.ts +5 -0
- package/package.json +34 -29
- package/lib/types/utils/getJsonBody.d.ts +0 -5
- package/lib/types/utils/request/parseRequestBody.d.ts +0 -2
- package/lib/types/utils/resolveMask.d.ts +0 -6
- package/lib/types/utils/resolveRelativeUrl.d.ts +0 -6
package/lib/umd/index.js
CHANGED
|
@@ -88,11 +88,11 @@
|
|
|
88
88
|
|
|
89
89
|
function createCommonjsModule(fn, basedir, module) {
|
|
90
90
|
return module = {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
path: basedir,
|
|
92
|
+
exports: {},
|
|
93
|
+
require: function (path, base) {
|
|
94
|
+
return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
|
|
95
|
+
}
|
|
96
96
|
}, fn(module, module.exports), module.exports;
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -574,7 +574,7 @@
|
|
|
574
574
|
/**
|
|
575
575
|
* Sets the body of the response without any `Content-Type` header.
|
|
576
576
|
* @example
|
|
577
|
-
* res(body('
|
|
577
|
+
* res(body('message'))
|
|
578
578
|
*/
|
|
579
579
|
const body = (value) => {
|
|
580
580
|
return (res) => {
|
|
@@ -596,16 +596,16 @@
|
|
|
596
596
|
|
|
597
597
|
/**
|
|
598
598
|
* Returns a boolean indicating if the current process is running in NodeJS environment.
|
|
599
|
+
* @see https://github.com/mswjs/msw/pull/255
|
|
599
600
|
*/
|
|
600
|
-
// Please see https://github.com/mswjs/msw/pull/255
|
|
601
601
|
function isNodeProcess() {
|
|
602
|
+
// Check browser environment.
|
|
602
603
|
if (typeof global !== 'object') {
|
|
603
|
-
// check browser environment
|
|
604
604
|
return false;
|
|
605
605
|
}
|
|
606
|
+
// Check nodejs or React Native environment.
|
|
606
607
|
if (Object.prototype.toString.call(global.process) === '[object process]' ||
|
|
607
608
|
navigator.product === 'ReactNative') {
|
|
608
|
-
// check nodejs or react native environment
|
|
609
609
|
return true;
|
|
610
610
|
}
|
|
611
611
|
}
|
|
@@ -634,9 +634,11 @@
|
|
|
634
634
|
};
|
|
635
635
|
|
|
636
636
|
/**
|
|
637
|
-
* Sets the given
|
|
637
|
+
* Sets the given value as the JSON body of the response.
|
|
638
638
|
* @example
|
|
639
|
-
* res(json({
|
|
639
|
+
* res(json({ key: 'value' }))
|
|
640
|
+
* res(json('Some string'))
|
|
641
|
+
* res(json([1, '2', false, { ok: true }]))
|
|
640
642
|
*/
|
|
641
643
|
const json = (body) => {
|
|
642
644
|
return (res) => {
|
|
@@ -654,20 +656,21 @@
|
|
|
654
656
|
};
|
|
655
657
|
|
|
656
658
|
const useFetch = isNodeProcess() ? require('node-fetch') : window.fetch;
|
|
657
|
-
const gracefully = (promise) => {
|
|
658
|
-
return promise.then((res) => {
|
|
659
|
-
var _a;
|
|
660
|
-
if ((_a = res.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('json')) {
|
|
661
|
-
return res.json();
|
|
662
|
-
}
|
|
663
|
-
return res.text();
|
|
664
|
-
});
|
|
665
|
-
};
|
|
666
659
|
const augmentRequestInit = (requestInit) => {
|
|
667
660
|
const headers = new lib.Headers(requestInit.headers);
|
|
668
661
|
headers.set('x-msw-bypass', 'true');
|
|
669
662
|
return Object.assign(Object.assign({}, requestInit), { headers: headers.getAllHeaders() });
|
|
670
663
|
};
|
|
664
|
+
const createFetchRequestParameters = (input) => {
|
|
665
|
+
const { body, method } = input;
|
|
666
|
+
const requestParameters = Object.assign(Object.assign({}, input), { body: undefined });
|
|
667
|
+
if (['GET', 'HEAD'].includes(method)) {
|
|
668
|
+
return requestParameters;
|
|
669
|
+
}
|
|
670
|
+
requestParameters.body =
|
|
671
|
+
typeof body === 'object' ? JSON.stringify(body) : body;
|
|
672
|
+
return requestParameters;
|
|
673
|
+
};
|
|
671
674
|
/**
|
|
672
675
|
* Wrapper around the native `window.fetch()` function that performs
|
|
673
676
|
* a request bypassing MSW. Requests performed using
|
|
@@ -676,17 +679,17 @@
|
|
|
676
679
|
const fetch = (input, requestInit = {}) => {
|
|
677
680
|
// Keep the default `window.fetch()` call signature
|
|
678
681
|
if (typeof input === 'string') {
|
|
679
|
-
return
|
|
682
|
+
return useFetch(input, augmentRequestInit(requestInit));
|
|
680
683
|
}
|
|
681
|
-
const
|
|
682
|
-
const
|
|
683
|
-
return
|
|
684
|
+
const requestParameters = createFetchRequestParameters(input);
|
|
685
|
+
const compliantRequest = augmentRequestInit(requestParameters);
|
|
686
|
+
return useFetch(input.url.href, compliantRequest);
|
|
684
687
|
};
|
|
685
688
|
|
|
686
689
|
/**
|
|
687
690
|
* Sets a given text as a "Cotent-Type: text/plain" body of the response.
|
|
688
691
|
* @example
|
|
689
|
-
* res(text('
|
|
692
|
+
* res(text('message'))
|
|
690
693
|
*/
|
|
691
694
|
const text = (body) => {
|
|
692
695
|
return (res) => {
|
|
@@ -699,7 +702,7 @@
|
|
|
699
702
|
/**
|
|
700
703
|
* Sets the given XML as the body of the response.
|
|
701
704
|
* @example
|
|
702
|
-
* res(xml('<
|
|
705
|
+
* res(xml('<key>value</key>'))
|
|
703
706
|
*/
|
|
704
707
|
const xml = (body) => {
|
|
705
708
|
return (res) => {
|
|
@@ -725,18 +728,18 @@
|
|
|
725
728
|
});
|
|
726
729
|
|
|
727
730
|
/*! *****************************************************************************
|
|
728
|
-
Copyright (c) Microsoft Corporation.
|
|
729
|
-
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
730
|
-
this file except in compliance with the License. You may obtain a copy of the
|
|
731
|
-
License at http://www.apache.org/licenses/LICENSE-2.0
|
|
731
|
+
Copyright (c) Microsoft Corporation.
|
|
732
732
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
|
736
|
-
MERCHANTABLITY OR NON-INFRINGEMENT.
|
|
733
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
734
|
+
purpose with or without fee is hereby granted.
|
|
737
735
|
|
|
738
|
-
|
|
739
|
-
|
|
736
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
737
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
738
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
739
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
740
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
741
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
742
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
740
743
|
***************************************************************************** */
|
|
741
744
|
|
|
742
745
|
function __awaiter(thisArg, _arguments, P, generator) {
|
|
@@ -779,7 +782,7 @@
|
|
|
779
782
|
* Attempts to resolve a Service Worker instance from a given registration,
|
|
780
783
|
* regardless of its state (active, installing, waiting).
|
|
781
784
|
*/
|
|
782
|
-
const getWorkerByRegistration = (registration, absoluteWorkerUrl) => {
|
|
785
|
+
const getWorkerByRegistration = (registration, absoluteWorkerUrl, findWorker) => {
|
|
783
786
|
const allStates = [
|
|
784
787
|
registration.active,
|
|
785
788
|
registration.installing,
|
|
@@ -787,7 +790,7 @@
|
|
|
787
790
|
];
|
|
788
791
|
const existingStates = allStates.filter(Boolean);
|
|
789
792
|
const mockWorker = existingStates.find((worker) => {
|
|
790
|
-
return worker.scriptURL
|
|
793
|
+
return findWorker(worker.scriptURL, absoluteWorkerUrl);
|
|
791
794
|
});
|
|
792
795
|
return mockWorker || null;
|
|
793
796
|
};
|
|
@@ -804,13 +807,13 @@
|
|
|
804
807
|
* Returns an active Service Worker instance.
|
|
805
808
|
* When not found, registers a new Service Worker.
|
|
806
809
|
*/
|
|
807
|
-
const getWorkerInstance = (url, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
808
|
-
// Resolve the absolute Service Worker URL
|
|
810
|
+
const getWorkerInstance = (url, options = {}, findWorker) => __awaiter(void 0, void 0, void 0, function* () {
|
|
811
|
+
// Resolve the absolute Service Worker URL.
|
|
809
812
|
const absoluteWorkerUrl = getAbsoluteWorkerUrl(url);
|
|
810
813
|
const [, mockRegistrations] = yield lib$1.until(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
811
814
|
const registrations = yield navigator.serviceWorker.getRegistrations();
|
|
812
815
|
return registrations.filter((registration) => {
|
|
813
|
-
return getWorkerByRegistration(registration, absoluteWorkerUrl);
|
|
816
|
+
return getWorkerByRegistration(registration, absoluteWorkerUrl, findWorker);
|
|
814
817
|
});
|
|
815
818
|
}));
|
|
816
819
|
if (!navigator.serviceWorker.controller && mockRegistrations.length > 0) {
|
|
@@ -824,23 +827,25 @@
|
|
|
824
827
|
}
|
|
825
828
|
const [existingRegistration] = mockRegistrations;
|
|
826
829
|
if (existingRegistration) {
|
|
827
|
-
//
|
|
830
|
+
// When the Service Worker is registered, update it and return the reference.
|
|
828
831
|
return existingRegistration.update().then(() => {
|
|
829
832
|
return [
|
|
830
|
-
getWorkerByRegistration(existingRegistration, absoluteWorkerUrl),
|
|
833
|
+
getWorkerByRegistration(existingRegistration, absoluteWorkerUrl, findWorker),
|
|
831
834
|
existingRegistration,
|
|
832
835
|
];
|
|
833
836
|
});
|
|
834
837
|
}
|
|
838
|
+
// When the Service Worker wasn't found, register it anew and return the reference.
|
|
835
839
|
const [error, instance] = yield lib$1.until(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
836
840
|
const registration = yield navigator.serviceWorker.register(url, options);
|
|
837
841
|
return [
|
|
838
842
|
// Compare existing worker registration by its worker URL,
|
|
839
843
|
// to prevent irrelevant workers to resolve here (such as Codesandbox worker).
|
|
840
|
-
getWorkerByRegistration(registration, absoluteWorkerUrl),
|
|
844
|
+
getWorkerByRegistration(registration, absoluteWorkerUrl, findWorker),
|
|
841
845
|
registration,
|
|
842
846
|
];
|
|
843
847
|
}));
|
|
848
|
+
// Handle Service Worker registration errors.
|
|
844
849
|
if (error) {
|
|
845
850
|
const isWorkerMissing = error.message.includes('(404)');
|
|
846
851
|
// Produce a custom error message when given a non-existing Service Worker url.
|
|
@@ -1018,436 +1023,62 @@ Learn more about creating the Service Worker script: https://mswjs.io/docs/cli/i
|
|
|
1018
1023
|
};
|
|
1019
1024
|
});
|
|
1020
1025
|
|
|
1021
|
-
function
|
|
1022
|
-
|
|
1023
|
-
onUnhandledRequest(request);
|
|
1024
|
-
return;
|
|
1025
|
-
}
|
|
1026
|
-
const message = `captured a ${request.method} ${request.url} request without a corresponding request handler.`;
|
|
1027
|
-
switch (onUnhandledRequest) {
|
|
1028
|
-
case 'error': {
|
|
1029
|
-
throw new Error(`[MSW] Error: ${message}`);
|
|
1030
|
-
}
|
|
1031
|
-
case 'warn': {
|
|
1032
|
-
console.warn(`[MSW] Warning: ${message}`);
|
|
1033
|
-
}
|
|
1034
|
-
default:
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1026
|
+
var punycode = createCommonjsModule(function (module, exports) {
|
|
1027
|
+
(function(root) {
|
|
1038
1028
|
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1029
|
+
/** Detect free variables */
|
|
1030
|
+
var freeExports = exports &&
|
|
1031
|
+
!exports.nodeType && exports;
|
|
1032
|
+
var freeModule = module &&
|
|
1033
|
+
!module.nodeType && module;
|
|
1034
|
+
var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal;
|
|
1035
|
+
if (
|
|
1036
|
+
freeGlobal.global === freeGlobal ||
|
|
1037
|
+
freeGlobal.window === freeGlobal ||
|
|
1038
|
+
freeGlobal.self === freeGlobal
|
|
1039
|
+
) {
|
|
1040
|
+
root = freeGlobal;
|
|
1041
|
+
}
|
|
1051
1042
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
}
|
|
1043
|
+
/**
|
|
1044
|
+
* The `punycode` object.
|
|
1045
|
+
* @name punycode
|
|
1046
|
+
* @type Object
|
|
1047
|
+
*/
|
|
1048
|
+
var punycode,
|
|
1059
1049
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
if (body) {
|
|
1063
|
-
// If the intercepted request's body has a JSON Content-Type
|
|
1064
|
-
// parse it into an object, otherwise leave as-is.
|
|
1065
|
-
const hasJsonContent = (_a = headers === null || headers === void 0 ? void 0 : headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('json');
|
|
1066
|
-
if (hasJsonContent && typeof body !== 'object') {
|
|
1067
|
-
return getJsonBody(body);
|
|
1068
|
-
}
|
|
1069
|
-
return body;
|
|
1070
|
-
}
|
|
1071
|
-
// Return whatever falsey body value is given.
|
|
1072
|
-
return body;
|
|
1073
|
-
}
|
|
1050
|
+
/** Highest positive signed 32-bit float value */
|
|
1051
|
+
maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
|
|
1074
1052
|
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
// Return document cookies only when requested a resource
|
|
1085
|
-
// from the same origin as the current document.
|
|
1086
|
-
return location.origin === req.url.origin ? getAllCookies() : {};
|
|
1087
|
-
}
|
|
1088
|
-
case 'include': {
|
|
1089
|
-
// Return all document cookies.
|
|
1090
|
-
return getAllCookies();
|
|
1091
|
-
}
|
|
1092
|
-
default: {
|
|
1093
|
-
return {};
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1053
|
+
/** Bootstring parameters */
|
|
1054
|
+
base = 36,
|
|
1055
|
+
tMin = 1,
|
|
1056
|
+
tMax = 26,
|
|
1057
|
+
skew = 38,
|
|
1058
|
+
damp = 700,
|
|
1059
|
+
initialBias = 72,
|
|
1060
|
+
initialN = 128, // 0x80
|
|
1061
|
+
delimiter = '-', // '\x2D'
|
|
1097
1062
|
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
return actual.toLowerCase() === expected.toLowerCase();
|
|
1103
|
-
}
|
|
1063
|
+
/** Regular expressions */
|
|
1064
|
+
regexPunycode = /^xn--/,
|
|
1065
|
+
regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
|
|
1066
|
+
regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
|
|
1104
1067
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
return new URL(value);
|
|
1112
|
-
}
|
|
1113
|
-
// Serialize headers
|
|
1114
|
-
if (key === 'headers') {
|
|
1115
|
-
return new lib.Headers(value);
|
|
1116
|
-
}
|
|
1117
|
-
// Prevent empty fields from presering an empty value.
|
|
1118
|
-
// It's invalid to perform a GET request with { body: "" }
|
|
1119
|
-
if (
|
|
1120
|
-
// Check if we are parsing deeper in `event.data.payload`,
|
|
1121
|
-
// because this custom JSON parser is invoked for each depth level.
|
|
1122
|
-
this.method &&
|
|
1123
|
-
isStringEqual(this.method, 'GET') &&
|
|
1124
|
-
key === 'body' &&
|
|
1125
|
-
value === '') {
|
|
1126
|
-
return undefined;
|
|
1127
|
-
}
|
|
1128
|
-
return value;
|
|
1129
|
-
});
|
|
1130
|
-
const { type, payload: req } = message;
|
|
1131
|
-
// Ignore irrelevant worker message types
|
|
1132
|
-
if (type !== 'REQUEST') {
|
|
1133
|
-
return null;
|
|
1134
|
-
}
|
|
1135
|
-
// Parse the request's body based on the "Content-Type" header.
|
|
1136
|
-
req.body = parseRequestBody(req.body, req.headers);
|
|
1137
|
-
// Set document cookies on the request.
|
|
1138
|
-
req.cookies = getRequestCookies(req);
|
|
1139
|
-
const { response, handler, publicRequest, parsedRequest, } = yield getResponse(req, context.requestHandlers);
|
|
1140
|
-
// Handle a scenario when there is no request handler
|
|
1141
|
-
// found for a given request.
|
|
1142
|
-
if (!handler) {
|
|
1143
|
-
onUnhandledRequest(req, options.onUnhandledRequest);
|
|
1144
|
-
return channel.send({ type: 'MOCK_NOT_FOUND' });
|
|
1145
|
-
}
|
|
1146
|
-
// Handle a scenario when there is a request handler,
|
|
1147
|
-
// but it doesn't return any mocked response.
|
|
1148
|
-
if (!response) {
|
|
1149
|
-
console.warn('[MSW] Expected a mocking resolver function to return a mocked response Object, but got: %s. Original response is going to be used instead.', response);
|
|
1150
|
-
return channel.send({ type: 'MOCK_NOT_FOUND' });
|
|
1151
|
-
}
|
|
1152
|
-
const responseWithSerializedHeaders = Object.assign(Object.assign({}, response), { headers: lib.headersToList(response.headers) });
|
|
1153
|
-
if (!options.quiet) {
|
|
1154
|
-
setTimeout(() => {
|
|
1155
|
-
handler.log(publicRequest, responseWithSerializedHeaders, handler, parsedRequest);
|
|
1156
|
-
}, response.delay);
|
|
1157
|
-
}
|
|
1158
|
-
channel.send({
|
|
1159
|
-
type: 'MOCK_SUCCESS',
|
|
1160
|
-
payload: responseWithSerializedHeaders,
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
|
-
catch (error) {
|
|
1164
|
-
if (error instanceof NetworkError) {
|
|
1165
|
-
// Treat emulated network error differently,
|
|
1166
|
-
// as it is an intended exception in a request handler.
|
|
1167
|
-
return channel.send({
|
|
1168
|
-
type: 'NETWORK_ERROR',
|
|
1169
|
-
payload: {
|
|
1170
|
-
name: error.name,
|
|
1171
|
-
message: error.message,
|
|
1172
|
-
},
|
|
1173
|
-
});
|
|
1174
|
-
}
|
|
1175
|
-
// Treat all the other exceptions in a request handler
|
|
1176
|
-
// as unintended, alerting that there is a problem needs fixing.
|
|
1177
|
-
channel.send({
|
|
1178
|
-
type: 'INTERNAL_ERROR',
|
|
1179
|
-
payload: {
|
|
1180
|
-
status: 500,
|
|
1181
|
-
body: JSON.stringify({
|
|
1182
|
-
errorType: error.constructor.name,
|
|
1183
|
-
message: error.message,
|
|
1184
|
-
location: error.stack,
|
|
1185
|
-
}),
|
|
1186
|
-
},
|
|
1187
|
-
});
|
|
1188
|
-
}
|
|
1189
|
-
});
|
|
1190
|
-
};
|
|
1068
|
+
/** Error messages */
|
|
1069
|
+
errors = {
|
|
1070
|
+
'overflow': 'Overflow: input needs wider integers to process',
|
|
1071
|
+
'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
|
|
1072
|
+
'invalid-input': 'Invalid input'
|
|
1073
|
+
},
|
|
1191
1074
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
const { payload: actualChecksum } = yield context.events.once('INTEGRITY_CHECK_RESPONSE');
|
|
1197
|
-
// Compare the response from the Service Worker and the
|
|
1198
|
-
// global variable set by webpack upon build.
|
|
1199
|
-
if (actualChecksum !== "ca2c3cd7453d8c614e2c19db63ede1a1") {
|
|
1200
|
-
throw new Error(`Currently active Service Worker (${actualChecksum}) is behind the latest published one (${"ca2c3cd7453d8c614e2c19db63ede1a1"}).`);
|
|
1201
|
-
}
|
|
1202
|
-
return serviceWorker;
|
|
1203
|
-
});
|
|
1204
|
-
}
|
|
1075
|
+
/** Convenience shortcuts */
|
|
1076
|
+
baseMinusTMin = base - tMin,
|
|
1077
|
+
floor = Math.floor,
|
|
1078
|
+
stringFromCharCode = String.fromCharCode,
|
|
1205
1079
|
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
* until the Service Worker instance is ready.
|
|
1209
|
-
* Must only be used in a browser.
|
|
1210
|
-
*/
|
|
1211
|
-
function deferNetworkRequestsUntil(predicatePromise) {
|
|
1212
|
-
// Defer `XMLHttpRequest` until the Service Worker is ready.
|
|
1213
|
-
const originalXhrSend = window.XMLHttpRequest.prototype.send;
|
|
1214
|
-
window.XMLHttpRequest.prototype.send = function (...args) {
|
|
1215
|
-
lib$1.until(() => predicatePromise).then(() => {
|
|
1216
|
-
window.XMLHttpRequest.prototype.send = originalXhrSend;
|
|
1217
|
-
this.send(...args);
|
|
1218
|
-
});
|
|
1219
|
-
};
|
|
1220
|
-
// Defer `fetch` requests until the Service Worker is ready.
|
|
1221
|
-
const originalFetch = window.fetch;
|
|
1222
|
-
window.fetch = (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
1223
|
-
yield lib$1.until(() => predicatePromise);
|
|
1224
|
-
window.fetch = originalFetch;
|
|
1225
|
-
return window.fetch(...args);
|
|
1226
|
-
});
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
const DEFAULT_START_OPTIONS = {
|
|
1230
|
-
serviceWorker: {
|
|
1231
|
-
url: '/mockServiceWorker.js',
|
|
1232
|
-
options: null,
|
|
1233
|
-
},
|
|
1234
|
-
quiet: false,
|
|
1235
|
-
waitUntilReady: true,
|
|
1236
|
-
onUnhandledRequest: 'bypass',
|
|
1237
|
-
};
|
|
1238
|
-
const createStart = (context) => {
|
|
1239
|
-
/**
|
|
1240
|
-
* Registers and activates the mock Service Worker.
|
|
1241
|
-
*/
|
|
1242
|
-
return function start(options) {
|
|
1243
|
-
const resolvedOptions = Object.assign({}, DEFAULT_START_OPTIONS, options);
|
|
1244
|
-
const startWorkerInstance = () => __awaiter(this, void 0, void 0, function* () {
|
|
1245
|
-
if (!('serviceWorker' in navigator)) {
|
|
1246
|
-
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).`);
|
|
1247
|
-
return null;
|
|
1248
|
-
}
|
|
1249
|
-
// Remove all previously existing event listeners.
|
|
1250
|
-
// This way none of the listeners persists between Fast refresh
|
|
1251
|
-
// of the application's code.
|
|
1252
|
-
context.events.removeAllListeners();
|
|
1253
|
-
context.events.addListener(navigator.serviceWorker, 'message', handleRequestWith(context, resolvedOptions));
|
|
1254
|
-
const [, instance] = yield lib$1.until(() => getWorkerInstance(resolvedOptions.serviceWorker.url, resolvedOptions.serviceWorker.options));
|
|
1255
|
-
if (!instance) {
|
|
1256
|
-
return null;
|
|
1257
|
-
}
|
|
1258
|
-
const [worker, registration] = instance;
|
|
1259
|
-
if (!worker) {
|
|
1260
|
-
return null;
|
|
1261
|
-
}
|
|
1262
|
-
context.worker = worker;
|
|
1263
|
-
context.registration = registration;
|
|
1264
|
-
context.events.addListener(window, 'beforeunload', () => {
|
|
1265
|
-
if (worker.state !== 'redundant') {
|
|
1266
|
-
// Notify the Service Worker that this client has closed.
|
|
1267
|
-
// Internally, it's similar to disabling the mocking, only
|
|
1268
|
-
// client close event has a handler that self-terminates
|
|
1269
|
-
// the Service Worker when there are no open clients.
|
|
1270
|
-
worker.postMessage('CLIENT_CLOSED');
|
|
1271
|
-
}
|
|
1272
|
-
});
|
|
1273
|
-
// Check if the active Service Worker is the latest published one
|
|
1274
|
-
const [integrityError] = yield lib$1.until(() => requestIntegrityCheck(context, worker));
|
|
1275
|
-
if (integrityError) {
|
|
1276
|
-
console.error(`\
|
|
1277
|
-
[MSW] Detected outdated Service Worker: ${integrityError.message}
|
|
1278
|
-
|
|
1279
|
-
The mocking is still enabled, but it's highly recommended that you update your Service Worker by running:
|
|
1280
|
-
|
|
1281
|
-
$ npx msw init <PUBLIC_DIR>
|
|
1282
|
-
|
|
1283
|
-
This is necessary to ensure that the Service Worker is in sync with the library to guarantee its stability.
|
|
1284
|
-
If this message still persists after updating, please report an issue: https://github.com/open-draft/msw/issues\
|
|
1285
|
-
`);
|
|
1286
|
-
}
|
|
1287
|
-
// Signal the Service Worker to enable requests interception
|
|
1288
|
-
const [activationError] = yield lib$1.until(() => activateMocking(context, options));
|
|
1289
|
-
if (activationError) {
|
|
1290
|
-
console.error('Failed to enable mocking', activationError);
|
|
1291
|
-
return null;
|
|
1292
|
-
}
|
|
1293
|
-
return registration;
|
|
1294
|
-
});
|
|
1295
|
-
const workerRegistration = startWorkerInstance();
|
|
1296
|
-
// Defer any network requests until the Service Worker instance is ready.
|
|
1297
|
-
// This prevents a race condition between the Service Worker registration
|
|
1298
|
-
// and application's runtime requests (i.e. requests on mount).
|
|
1299
|
-
if (resolvedOptions.waitUntilReady) {
|
|
1300
|
-
deferNetworkRequestsUntil(workerRegistration);
|
|
1301
|
-
}
|
|
1302
|
-
return workerRegistration;
|
|
1303
|
-
};
|
|
1304
|
-
};
|
|
1305
|
-
|
|
1306
|
-
const createStop = (context) => {
|
|
1307
|
-
/**
|
|
1308
|
-
* Signal the Service Worker to disable mocking for this client.
|
|
1309
|
-
* Use this an an explicit way to stop the mocking, while preserving
|
|
1310
|
-
* the worker-client relation. Does not affect the worker's lifecycle.
|
|
1311
|
-
*/
|
|
1312
|
-
return function stop() {
|
|
1313
|
-
var _a;
|
|
1314
|
-
(_a = context.worker) === null || _a === void 0 ? void 0 : _a.postMessage('MOCK_DEACTIVATE');
|
|
1315
|
-
context.events.removeAllListeners();
|
|
1316
|
-
};
|
|
1317
|
-
};
|
|
1318
|
-
|
|
1319
|
-
function use(currentHandlers, ...handlers) {
|
|
1320
|
-
currentHandlers.unshift(...handlers);
|
|
1321
|
-
}
|
|
1322
|
-
function restoreHandlers(handlers) {
|
|
1323
|
-
handlers.forEach((handler) => {
|
|
1324
|
-
if ('shouldSkip' in handler) {
|
|
1325
|
-
handler.shouldSkip = false;
|
|
1326
|
-
}
|
|
1327
|
-
});
|
|
1328
|
-
}
|
|
1329
|
-
function resetHandlers(initialHandlers, ...nextHandlers) {
|
|
1330
|
-
return nextHandlers.length > 0 ? [...nextHandlers] : [...initialHandlers];
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
|
-
// Declare the list of event handlers on the module's scope
|
|
1334
|
-
// so it persists between Fash refreshes of the application's code.
|
|
1335
|
-
let listeners = [];
|
|
1336
|
-
function setupWorker(...requestHandlers) {
|
|
1337
|
-
const context = {
|
|
1338
|
-
worker: null,
|
|
1339
|
-
registration: null,
|
|
1340
|
-
requestHandlers: [...requestHandlers],
|
|
1341
|
-
events: {
|
|
1342
|
-
addListener(target, event, callback) {
|
|
1343
|
-
target.addEventListener(event, callback);
|
|
1344
|
-
listeners.push({ event, target, callback });
|
|
1345
|
-
return () => {
|
|
1346
|
-
target.removeEventListener(event, callback);
|
|
1347
|
-
};
|
|
1348
|
-
},
|
|
1349
|
-
removeAllListeners() {
|
|
1350
|
-
for (const { target, event, callback } of listeners) {
|
|
1351
|
-
target.removeEventListener(event, callback);
|
|
1352
|
-
}
|
|
1353
|
-
listeners = [];
|
|
1354
|
-
},
|
|
1355
|
-
once(type) {
|
|
1356
|
-
const bindings = [];
|
|
1357
|
-
return new Promise((resolve, reject) => {
|
|
1358
|
-
const handleIncomingMessage = (event) => {
|
|
1359
|
-
try {
|
|
1360
|
-
const message = JSON.parse(event.data);
|
|
1361
|
-
if (message.type === type) {
|
|
1362
|
-
resolve(message);
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
catch (error) {
|
|
1366
|
-
reject(error);
|
|
1367
|
-
}
|
|
1368
|
-
};
|
|
1369
|
-
bindings.push(context.events.addListener(navigator.serviceWorker, 'message', handleIncomingMessage), context.events.addListener(navigator.serviceWorker, 'messageerror', reject));
|
|
1370
|
-
}).finally(() => {
|
|
1371
|
-
bindings.forEach((unbind) => unbind());
|
|
1372
|
-
});
|
|
1373
|
-
},
|
|
1374
|
-
},
|
|
1375
|
-
};
|
|
1376
|
-
// Error when attempting to run this function in a NodeJS environment.
|
|
1377
|
-
if (isNodeProcess()) {
|
|
1378
|
-
throw new Error('[MSW] Failed to execute `setupWorker` in a non-browser environment. Consider using `setupServer` for NodeJS environment instead.');
|
|
1379
|
-
}
|
|
1380
|
-
return {
|
|
1381
|
-
start: createStart(context),
|
|
1382
|
-
stop: createStop(context),
|
|
1383
|
-
use(...handlers) {
|
|
1384
|
-
use(context.requestHandlers, ...handlers);
|
|
1385
|
-
},
|
|
1386
|
-
restoreHandlers() {
|
|
1387
|
-
restoreHandlers(context.requestHandlers);
|
|
1388
|
-
},
|
|
1389
|
-
resetHandlers(...nextHandlers) {
|
|
1390
|
-
context.requestHandlers = resetHandlers(requestHandlers, ...nextHandlers);
|
|
1391
|
-
},
|
|
1392
|
-
};
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
var punycode = createCommonjsModule(function (module, exports) {
|
|
1396
|
-
(function(root) {
|
|
1397
|
-
|
|
1398
|
-
/** Detect free variables */
|
|
1399
|
-
var freeExports = exports &&
|
|
1400
|
-
!exports.nodeType && exports;
|
|
1401
|
-
var freeModule = module &&
|
|
1402
|
-
!module.nodeType && module;
|
|
1403
|
-
var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal;
|
|
1404
|
-
if (
|
|
1405
|
-
freeGlobal.global === freeGlobal ||
|
|
1406
|
-
freeGlobal.window === freeGlobal ||
|
|
1407
|
-
freeGlobal.self === freeGlobal
|
|
1408
|
-
) {
|
|
1409
|
-
root = freeGlobal;
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
/**
|
|
1413
|
-
* The `punycode` object.
|
|
1414
|
-
* @name punycode
|
|
1415
|
-
* @type Object
|
|
1416
|
-
*/
|
|
1417
|
-
var punycode,
|
|
1418
|
-
|
|
1419
|
-
/** Highest positive signed 32-bit float value */
|
|
1420
|
-
maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
|
|
1421
|
-
|
|
1422
|
-
/** Bootstring parameters */
|
|
1423
|
-
base = 36,
|
|
1424
|
-
tMin = 1,
|
|
1425
|
-
tMax = 26,
|
|
1426
|
-
skew = 38,
|
|
1427
|
-
damp = 700,
|
|
1428
|
-
initialBias = 72,
|
|
1429
|
-
initialN = 128, // 0x80
|
|
1430
|
-
delimiter = '-', // '\x2D'
|
|
1431
|
-
|
|
1432
|
-
/** Regular expressions */
|
|
1433
|
-
regexPunycode = /^xn--/,
|
|
1434
|
-
regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
|
|
1435
|
-
regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
|
|
1436
|
-
|
|
1437
|
-
/** Error messages */
|
|
1438
|
-
errors = {
|
|
1439
|
-
'overflow': 'Overflow: input needs wider integers to process',
|
|
1440
|
-
'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
|
|
1441
|
-
'invalid-input': 'Invalid input'
|
|
1442
|
-
},
|
|
1443
|
-
|
|
1444
|
-
/** Convenience shortcuts */
|
|
1445
|
-
baseMinusTMin = base - tMin,
|
|
1446
|
-
floor = Math.floor,
|
|
1447
|
-
stringFromCharCode = String.fromCharCode,
|
|
1448
|
-
|
|
1449
|
-
/** Temporary variable */
|
|
1450
|
-
key;
|
|
1080
|
+
/** Temporary variable */
|
|
1081
|
+
key;
|
|
1451
1082
|
|
|
1452
1083
|
/*--------------------------------------------------------------------------*/
|
|
1453
1084
|
|
|
@@ -2674,65 +2305,507 @@ If this message still persists after updating, please report an issue: https://g
|
|
|
2674
2305
|
srcPath.unshift('');
|
|
2675
2306
|
}
|
|
2676
2307
|
|
|
2677
|
-
if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
|
|
2678
|
-
srcPath.push('');
|
|
2679
|
-
}
|
|
2308
|
+
if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
|
|
2309
|
+
srcPath.push('');
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
var isAbsolute = srcPath[0] === '' ||
|
|
2313
|
+
(srcPath[0] && srcPath[0].charAt(0) === '/');
|
|
2314
|
+
|
|
2315
|
+
// put the host back
|
|
2316
|
+
if (psychotic) {
|
|
2317
|
+
result.hostname = result.host = isAbsolute ? '' :
|
|
2318
|
+
srcPath.length ? srcPath.shift() : '';
|
|
2319
|
+
//occationaly the auth can get stuck only in host
|
|
2320
|
+
//this especially happens in cases like
|
|
2321
|
+
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')
|
|
2322
|
+
var authInHost = result.host && result.host.indexOf('@') > 0 ?
|
|
2323
|
+
result.host.split('@') : false;
|
|
2324
|
+
if (authInHost) {
|
|
2325
|
+
result.auth = authInHost.shift();
|
|
2326
|
+
result.host = result.hostname = authInHost.shift();
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
mustEndAbs = mustEndAbs || (result.host && srcPath.length);
|
|
2331
|
+
|
|
2332
|
+
if (mustEndAbs && !isAbsolute) {
|
|
2333
|
+
srcPath.unshift('');
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
if (!srcPath.length) {
|
|
2337
|
+
result.pathname = null;
|
|
2338
|
+
result.path = null;
|
|
2339
|
+
} else {
|
|
2340
|
+
result.pathname = srcPath.join('/');
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
//to support request.http
|
|
2344
|
+
if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
|
|
2345
|
+
result.path = (result.pathname ? result.pathname : '') +
|
|
2346
|
+
(result.search ? result.search : '');
|
|
2347
|
+
}
|
|
2348
|
+
result.auth = relative.auth || result.auth;
|
|
2349
|
+
result.slashes = result.slashes || relative.slashes;
|
|
2350
|
+
result.href = result.format();
|
|
2351
|
+
return result;
|
|
2352
|
+
};
|
|
2353
|
+
|
|
2354
|
+
Url.prototype.parseHost = function() {
|
|
2355
|
+
var host = this.host;
|
|
2356
|
+
var port = portPattern.exec(host);
|
|
2357
|
+
if (port) {
|
|
2358
|
+
port = port[0];
|
|
2359
|
+
if (port !== ':') {
|
|
2360
|
+
this.port = port.substr(1);
|
|
2361
|
+
}
|
|
2362
|
+
host = host.substr(0, host.length - port.length);
|
|
2363
|
+
}
|
|
2364
|
+
if (host) this.hostname = host;
|
|
2365
|
+
};
|
|
2366
|
+
|
|
2367
|
+
/**
|
|
2368
|
+
* Returns a relative URL if the given request URL is relative to the current origin.
|
|
2369
|
+
* Otherwise returns an absolute URL.
|
|
2370
|
+
*/
|
|
2371
|
+
const getPublicUrlFromRequest = (request) => {
|
|
2372
|
+
return request.referrer.startsWith(request.url.origin)
|
|
2373
|
+
? request.url.pathname
|
|
2374
|
+
: format({
|
|
2375
|
+
protocol: request.url.protocol,
|
|
2376
|
+
host: request.url.host,
|
|
2377
|
+
pathname: request.url.pathname,
|
|
2378
|
+
});
|
|
2379
|
+
};
|
|
2380
|
+
|
|
2381
|
+
function onUnhandledRequest(request, onUnhandledRequest = 'bypass') {
|
|
2382
|
+
if (typeof onUnhandledRequest === 'function') {
|
|
2383
|
+
onUnhandledRequest(request);
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
const publicUrl = getPublicUrlFromRequest(request);
|
|
2387
|
+
const message = `captured a ${request.method} ${request.url} request without a corresponding request handler.
|
|
2388
|
+
|
|
2389
|
+
If you wish to intercept this request, consider creating a request handler for it:
|
|
2390
|
+
|
|
2391
|
+
rest.${request.method.toLowerCase()}('${publicUrl}', (req, res, ctx) => {
|
|
2392
|
+
return res(ctx.text('body'))
|
|
2393
|
+
})`;
|
|
2394
|
+
switch (onUnhandledRequest) {
|
|
2395
|
+
case 'error': {
|
|
2396
|
+
throw new Error(`[MSW] Error: ${message}`);
|
|
2397
|
+
}
|
|
2398
|
+
case 'warn': {
|
|
2399
|
+
console.warn(`[MSW] Warning: ${message}`);
|
|
2400
|
+
}
|
|
2401
|
+
default:
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
/**
|
|
2407
|
+
* Parses a given string into a JSON.
|
|
2408
|
+
* Does not throw an exception on an invalid JSON string.
|
|
2409
|
+
*/
|
|
2410
|
+
function jsonParse(str) {
|
|
2411
|
+
try {
|
|
2412
|
+
return JSON.parse(str);
|
|
2413
|
+
}
|
|
2414
|
+
catch (error) {
|
|
2415
|
+
return undefined;
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
/**
|
|
2420
|
+
* Parses a given request/response body based on the `Content-Type` header.
|
|
2421
|
+
*/
|
|
2422
|
+
function parseBody(body, headers) {
|
|
2423
|
+
var _a;
|
|
2424
|
+
if (body) {
|
|
2425
|
+
// If the intercepted request's body has a JSON Content-Type
|
|
2426
|
+
// parse it into an object, otherwise leave as-is.
|
|
2427
|
+
const hasJsonContent = (_a = headers === null || headers === void 0 ? void 0 : headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('json');
|
|
2428
|
+
if (hasJsonContent && typeof body !== 'object') {
|
|
2429
|
+
return jsonParse(body) || body;
|
|
2430
|
+
}
|
|
2431
|
+
return body;
|
|
2432
|
+
}
|
|
2433
|
+
// Return whatever falsey body value is given.
|
|
2434
|
+
return body;
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
function getAllCookies() {
|
|
2438
|
+
return parse_1(document.cookie);
|
|
2439
|
+
}
|
|
2440
|
+
/**
|
|
2441
|
+
* Returns relevant document cookies based on the request `credentials` option.
|
|
2442
|
+
*/
|
|
2443
|
+
function getRequestCookies(req) {
|
|
2444
|
+
switch (req.credentials) {
|
|
2445
|
+
case 'same-origin': {
|
|
2446
|
+
// Return document cookies only when requested a resource
|
|
2447
|
+
// from the same origin as the current document.
|
|
2448
|
+
return location.origin === req.url.origin ? getAllCookies() : {};
|
|
2449
|
+
}
|
|
2450
|
+
case 'include': {
|
|
2451
|
+
// Return all document cookies.
|
|
2452
|
+
return getAllCookies();
|
|
2453
|
+
}
|
|
2454
|
+
default: {
|
|
2455
|
+
return {};
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
/**
|
|
2461
|
+
* Performs a case-insensitive comparison of two given strings.
|
|
2462
|
+
*/
|
|
2463
|
+
function isStringEqual(actual, expected) {
|
|
2464
|
+
return actual.toLowerCase() === expected.toLowerCase();
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
const handleRequestWith = (context, options) => {
|
|
2468
|
+
return (event) => __awaiter(void 0, void 0, void 0, function* () {
|
|
2469
|
+
const channel = createBroadcastChannel(event);
|
|
2470
|
+
try {
|
|
2471
|
+
const message = JSON.parse(event.data, function (key, value) {
|
|
2472
|
+
if (key === 'url') {
|
|
2473
|
+
return new URL(value);
|
|
2474
|
+
}
|
|
2475
|
+
// Serialize headers
|
|
2476
|
+
if (key === 'headers') {
|
|
2477
|
+
return new lib.Headers(value);
|
|
2478
|
+
}
|
|
2479
|
+
// Prevent empty fields from presering an empty value.
|
|
2480
|
+
// It's invalid to perform a GET request with { body: "" }
|
|
2481
|
+
if (
|
|
2482
|
+
// Check if we are parsing deeper in `event.data.payload`,
|
|
2483
|
+
// because this custom JSON parser is invoked for each depth level.
|
|
2484
|
+
this.method &&
|
|
2485
|
+
isStringEqual(this.method, 'GET') &&
|
|
2486
|
+
key === 'body' &&
|
|
2487
|
+
value === '') {
|
|
2488
|
+
return undefined;
|
|
2489
|
+
}
|
|
2490
|
+
return value;
|
|
2491
|
+
});
|
|
2492
|
+
const { type, payload: req } = message;
|
|
2493
|
+
// Ignore irrelevant worker message types
|
|
2494
|
+
if (type !== 'REQUEST') {
|
|
2495
|
+
return null;
|
|
2496
|
+
}
|
|
2497
|
+
// Parse the request's body based on the "Content-Type" header.
|
|
2498
|
+
req.body = parseBody(req.body, req.headers);
|
|
2499
|
+
// Set document cookies on the request.
|
|
2500
|
+
req.cookies = getRequestCookies(req);
|
|
2501
|
+
const { response, handler, publicRequest, parsedRequest, } = yield getResponse(req, context.requestHandlers);
|
|
2502
|
+
// Handle a scenario when there is no request handler
|
|
2503
|
+
// found for a given request.
|
|
2504
|
+
if (!handler) {
|
|
2505
|
+
onUnhandledRequest(req, options.onUnhandledRequest);
|
|
2506
|
+
return channel.send({ type: 'MOCK_NOT_FOUND' });
|
|
2507
|
+
}
|
|
2508
|
+
// Handle a scenario when there is a request handler,
|
|
2509
|
+
// but it doesn't return any mocked response.
|
|
2510
|
+
if (!response) {
|
|
2511
|
+
console.warn('[MSW] Expected a mocking resolver function to return a mocked response Object, but got: %s. Original response is going to be used instead.', response);
|
|
2512
|
+
return channel.send({ type: 'MOCK_NOT_FOUND' });
|
|
2513
|
+
}
|
|
2514
|
+
const responseWithSerializedHeaders = Object.assign(Object.assign({}, response), { headers: lib.headersToList(response.headers) });
|
|
2515
|
+
if (!options.quiet) {
|
|
2516
|
+
setTimeout(() => {
|
|
2517
|
+
handler.log(publicRequest, responseWithSerializedHeaders, handler, parsedRequest);
|
|
2518
|
+
}, response.delay);
|
|
2519
|
+
}
|
|
2520
|
+
channel.send({
|
|
2521
|
+
type: 'MOCK_SUCCESS',
|
|
2522
|
+
payload: responseWithSerializedHeaders,
|
|
2523
|
+
});
|
|
2524
|
+
}
|
|
2525
|
+
catch (error) {
|
|
2526
|
+
if (error instanceof NetworkError) {
|
|
2527
|
+
// Treat emulated network error differently,
|
|
2528
|
+
// as it is an intended exception in a request handler.
|
|
2529
|
+
return channel.send({
|
|
2530
|
+
type: 'NETWORK_ERROR',
|
|
2531
|
+
payload: {
|
|
2532
|
+
name: error.name,
|
|
2533
|
+
message: error.message,
|
|
2534
|
+
},
|
|
2535
|
+
});
|
|
2536
|
+
}
|
|
2537
|
+
// Treat all the other exceptions in a request handler
|
|
2538
|
+
// as unintended, alerting that there is a problem needs fixing.
|
|
2539
|
+
channel.send({
|
|
2540
|
+
type: 'INTERNAL_ERROR',
|
|
2541
|
+
payload: {
|
|
2542
|
+
status: 500,
|
|
2543
|
+
body: JSON.stringify({
|
|
2544
|
+
errorType: error.constructor.name,
|
|
2545
|
+
message: error.message,
|
|
2546
|
+
location: error.stack,
|
|
2547
|
+
}),
|
|
2548
|
+
},
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2551
|
+
});
|
|
2552
|
+
};
|
|
2553
|
+
|
|
2554
|
+
function requestIntegrityCheck(context, serviceWorker) {
|
|
2555
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2556
|
+
// Signal Service Worker to report back its integrity
|
|
2557
|
+
serviceWorker.postMessage('INTEGRITY_CHECK_REQUEST');
|
|
2558
|
+
const { payload: actualChecksum } = yield context.events.once('INTEGRITY_CHECK_RESPONSE');
|
|
2559
|
+
// Compare the response from the Service Worker and the
|
|
2560
|
+
// global variable set by webpack upon build.
|
|
2561
|
+
if (actualChecksum !== "d1e0e502f550d40a34bee90822e4bf98") {
|
|
2562
|
+
throw new Error(`Currently active Service Worker (${actualChecksum}) is behind the latest published one (${"d1e0e502f550d40a34bee90822e4bf98"}).`);
|
|
2563
|
+
}
|
|
2564
|
+
return serviceWorker;
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
/**
|
|
2569
|
+
* Intercepts and defers any requests on the page
|
|
2570
|
+
* until the Service Worker instance is ready.
|
|
2571
|
+
* Must only be used in a browser.
|
|
2572
|
+
*/
|
|
2573
|
+
function deferNetworkRequestsUntil(predicatePromise) {
|
|
2574
|
+
// Defer any `XMLHttpRequest` requests until the Service Worker is ready.
|
|
2575
|
+
const originalXhrSend = window.XMLHttpRequest.prototype.send;
|
|
2576
|
+
window.XMLHttpRequest.prototype.send = function (...args) {
|
|
2577
|
+
// Keep this function synchronous to comply with `XMLHttpRequest.prototype.send`,
|
|
2578
|
+
// because that method is always synchronous.
|
|
2579
|
+
lib$1.until(() => predicatePromise).then(() => {
|
|
2580
|
+
window.XMLHttpRequest.prototype.send = originalXhrSend;
|
|
2581
|
+
this.send(...args);
|
|
2582
|
+
});
|
|
2583
|
+
};
|
|
2584
|
+
// Defer any `fetch` requests until the Service Worker is ready.
|
|
2585
|
+
const originalFetch = window.fetch;
|
|
2586
|
+
window.fetch = (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
2587
|
+
yield lib$1.until(() => predicatePromise);
|
|
2588
|
+
window.fetch = originalFetch;
|
|
2589
|
+
return window.fetch(...args);
|
|
2590
|
+
});
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
function isObject(obj) {
|
|
2594
|
+
return typeof obj === 'object';
|
|
2595
|
+
}
|
|
2596
|
+
/**
|
|
2597
|
+
* Deeply merges two given objects with the right one
|
|
2598
|
+
* having a priority during property assignment.
|
|
2599
|
+
*/
|
|
2600
|
+
function mergeRight(a, b) {
|
|
2601
|
+
const result = Object.assign({}, a);
|
|
2602
|
+
Object.entries(b).forEach(([key, value]) => {
|
|
2603
|
+
const existingValue = result[key];
|
|
2604
|
+
if (Array.isArray(existingValue) && Array.isArray(value)) {
|
|
2605
|
+
result[key] = existingValue.concat(value);
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
if (isObject(existingValue) && isObject(value)) {
|
|
2609
|
+
result[key] = mergeRight(existingValue, value);
|
|
2610
|
+
return;
|
|
2611
|
+
}
|
|
2612
|
+
result[key] = value;
|
|
2613
|
+
});
|
|
2614
|
+
return result;
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
const DEFAULT_START_OPTIONS = {
|
|
2618
|
+
serviceWorker: {
|
|
2619
|
+
url: '/mockServiceWorker.js',
|
|
2620
|
+
options: null,
|
|
2621
|
+
},
|
|
2622
|
+
quiet: false,
|
|
2623
|
+
waitUntilReady: true,
|
|
2624
|
+
onUnhandledRequest: 'bypass',
|
|
2625
|
+
findWorker: (scriptURL, mockServiceWorkerUrl) => scriptURL === mockServiceWorkerUrl,
|
|
2626
|
+
};
|
|
2627
|
+
const createStart = (context) => {
|
|
2628
|
+
/**
|
|
2629
|
+
* Registers and activates the mock Service Worker.
|
|
2630
|
+
*/
|
|
2631
|
+
return function start(options) {
|
|
2632
|
+
const resolvedOptions = mergeRight(DEFAULT_START_OPTIONS, options || {});
|
|
2633
|
+
const startWorkerInstance = () => __awaiter(this, void 0, void 0, function* () {
|
|
2634
|
+
if (!('serviceWorker' in navigator)) {
|
|
2635
|
+
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).`);
|
|
2636
|
+
return null;
|
|
2637
|
+
}
|
|
2638
|
+
// Remove all previously existing event listeners.
|
|
2639
|
+
// This way none of the listeners persists between Fast refresh
|
|
2640
|
+
// of the application's code.
|
|
2641
|
+
context.events.removeAllListeners();
|
|
2642
|
+
context.events.addListener(navigator.serviceWorker, 'message', handleRequestWith(context, resolvedOptions));
|
|
2643
|
+
const [, instance] = yield lib$1.until(() => getWorkerInstance(resolvedOptions.serviceWorker.url, resolvedOptions.serviceWorker.options, resolvedOptions.findWorker));
|
|
2644
|
+
if (!instance) {
|
|
2645
|
+
return null;
|
|
2646
|
+
}
|
|
2647
|
+
const [worker, registration] = instance;
|
|
2648
|
+
if (!worker) {
|
|
2649
|
+
if (options === null || options === void 0 ? void 0 : options.findWorker) {
|
|
2650
|
+
console.error(`\
|
|
2651
|
+
[MSW] Failed to locate the Service Worker registration using a custom "findWorker" predicate.
|
|
2680
2652
|
|
|
2681
|
-
|
|
2682
|
-
|
|
2653
|
+
Please ensure that the custom predicate properly locates the Service Worker registration at "${resolvedOptions.serviceWorker.url}".
|
|
2654
|
+
More details: https://mswjs.io/docs/api/setup-worker/start#findworker
|
|
2655
|
+
`);
|
|
2656
|
+
}
|
|
2657
|
+
else {
|
|
2658
|
+
console.error(`\
|
|
2659
|
+
[MSW] Failed to locate the Service Worker registration.
|
|
2683
2660
|
|
|
2684
|
-
|
|
2685
|
-
if (psychotic) {
|
|
2686
|
-
result.hostname = result.host = isAbsolute ? '' :
|
|
2687
|
-
srcPath.length ? srcPath.shift() : '';
|
|
2688
|
-
//occationaly the auth can get stuck only in host
|
|
2689
|
-
//this especially happens in cases like
|
|
2690
|
-
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')
|
|
2691
|
-
var authInHost = result.host && result.host.indexOf('@') > 0 ?
|
|
2692
|
-
result.host.split('@') : false;
|
|
2693
|
-
if (authInHost) {
|
|
2694
|
-
result.auth = authInHost.shift();
|
|
2695
|
-
result.host = result.hostname = authInHost.shift();
|
|
2696
|
-
}
|
|
2697
|
-
}
|
|
2661
|
+
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.
|
|
2698
2662
|
|
|
2699
|
-
|
|
2663
|
+
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`);
|
|
2664
|
+
}
|
|
2665
|
+
return null;
|
|
2666
|
+
}
|
|
2667
|
+
context.worker = worker;
|
|
2668
|
+
context.registration = registration;
|
|
2669
|
+
context.events.addListener(window, 'beforeunload', () => {
|
|
2670
|
+
if (worker.state !== 'redundant') {
|
|
2671
|
+
// Notify the Service Worker that this client has closed.
|
|
2672
|
+
// Internally, it's similar to disabling the mocking, only
|
|
2673
|
+
// client close event has a handler that self-terminates
|
|
2674
|
+
// the Service Worker when there are no open clients.
|
|
2675
|
+
worker.postMessage('CLIENT_CLOSED');
|
|
2676
|
+
}
|
|
2677
|
+
// Make sure we're always clearing the interval - there are reports that not doing this can
|
|
2678
|
+
// cause memory leaks in headless browser environments.
|
|
2679
|
+
window.clearInterval(context.keepAliveInterval);
|
|
2680
|
+
});
|
|
2681
|
+
// Check if the active Service Worker is the latest published one
|
|
2682
|
+
const [integrityError] = yield lib$1.until(() => requestIntegrityCheck(context, worker));
|
|
2683
|
+
if (integrityError) {
|
|
2684
|
+
console.error(`\
|
|
2685
|
+
[MSW] Detected outdated Service Worker: ${integrityError.message}
|
|
2700
2686
|
|
|
2701
|
-
|
|
2702
|
-
srcPath.unshift('');
|
|
2703
|
-
}
|
|
2687
|
+
The mocking is still enabled, but it's highly recommended that you update your Service Worker by running:
|
|
2704
2688
|
|
|
2705
|
-
|
|
2706
|
-
result.pathname = null;
|
|
2707
|
-
result.path = null;
|
|
2708
|
-
} else {
|
|
2709
|
-
result.pathname = srcPath.join('/');
|
|
2710
|
-
}
|
|
2689
|
+
$ npx msw init <PUBLIC_DIR>
|
|
2711
2690
|
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2691
|
+
This is necessary to ensure that the Service Worker is in sync with the library to guarantee its stability.
|
|
2692
|
+
If this message still persists after updating, please report an issue: https://github.com/open-draft/msw/issues\
|
|
2693
|
+
`);
|
|
2694
|
+
}
|
|
2695
|
+
// Signal the Service Worker to enable requests interception
|
|
2696
|
+
const [activationError] = yield lib$1.until(() => activateMocking(context, options));
|
|
2697
|
+
if (activationError) {
|
|
2698
|
+
console.error('Failed to enable mocking', activationError);
|
|
2699
|
+
return null;
|
|
2700
|
+
}
|
|
2701
|
+
context.keepAliveInterval = window.setInterval(() => worker.postMessage('KEEPALIVE_REQUEST'), 5000);
|
|
2702
|
+
return registration;
|
|
2703
|
+
});
|
|
2704
|
+
const workerRegistration = startWorkerInstance();
|
|
2705
|
+
// Defer any network requests until the Service Worker instance is ready.
|
|
2706
|
+
// This prevents a race condition between the Service Worker registration
|
|
2707
|
+
// and application's runtime requests (i.e. requests on mount).
|
|
2708
|
+
if (resolvedOptions.waitUntilReady) {
|
|
2709
|
+
deferNetworkRequestsUntil(workerRegistration);
|
|
2710
|
+
}
|
|
2711
|
+
return workerRegistration;
|
|
2712
|
+
};
|
|
2721
2713
|
};
|
|
2722
2714
|
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2715
|
+
const createStop = (context) => {
|
|
2716
|
+
/**
|
|
2717
|
+
* Signal the Service Worker to disable mocking for this client.
|
|
2718
|
+
* Use this an an explicit way to stop the mocking, while preserving
|
|
2719
|
+
* the worker-client relation. Does not affect the worker's lifecycle.
|
|
2720
|
+
*/
|
|
2721
|
+
return function stop() {
|
|
2722
|
+
var _a;
|
|
2723
|
+
(_a = context.worker) === null || _a === void 0 ? void 0 : _a.postMessage('MOCK_DEACTIVATE');
|
|
2724
|
+
context.events.removeAllListeners();
|
|
2725
|
+
window.clearInterval(context.keepAliveInterval);
|
|
2726
|
+
};
|
|
2734
2727
|
};
|
|
2735
2728
|
|
|
2729
|
+
function use(currentHandlers, ...handlers) {
|
|
2730
|
+
currentHandlers.unshift(...handlers);
|
|
2731
|
+
}
|
|
2732
|
+
function restoreHandlers(handlers) {
|
|
2733
|
+
handlers.forEach((handler) => {
|
|
2734
|
+
if ('shouldSkip' in handler) {
|
|
2735
|
+
handler.shouldSkip = false;
|
|
2736
|
+
}
|
|
2737
|
+
});
|
|
2738
|
+
}
|
|
2739
|
+
function resetHandlers(initialHandlers, ...nextHandlers) {
|
|
2740
|
+
return nextHandlers.length > 0 ? [...nextHandlers] : [...initialHandlers];
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
// Declare the list of event handlers on the module's scope
|
|
2744
|
+
// so it persists between Fash refreshes of the application's code.
|
|
2745
|
+
let listeners = [];
|
|
2746
|
+
function setupWorker(...requestHandlers) {
|
|
2747
|
+
requestHandlers.forEach((handler) => {
|
|
2748
|
+
if (Array.isArray(handler))
|
|
2749
|
+
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).`);
|
|
2750
|
+
});
|
|
2751
|
+
const context = {
|
|
2752
|
+
worker: null,
|
|
2753
|
+
registration: null,
|
|
2754
|
+
requestHandlers: [...requestHandlers],
|
|
2755
|
+
events: {
|
|
2756
|
+
addListener(target, event, callback) {
|
|
2757
|
+
target.addEventListener(event, callback);
|
|
2758
|
+
listeners.push({ event, target, callback });
|
|
2759
|
+
return () => {
|
|
2760
|
+
target.removeEventListener(event, callback);
|
|
2761
|
+
};
|
|
2762
|
+
},
|
|
2763
|
+
removeAllListeners() {
|
|
2764
|
+
for (const { target, event, callback } of listeners) {
|
|
2765
|
+
target.removeEventListener(event, callback);
|
|
2766
|
+
}
|
|
2767
|
+
listeners = [];
|
|
2768
|
+
},
|
|
2769
|
+
once(type) {
|
|
2770
|
+
const bindings = [];
|
|
2771
|
+
return new Promise((resolve, reject) => {
|
|
2772
|
+
const handleIncomingMessage = (event) => {
|
|
2773
|
+
try {
|
|
2774
|
+
const message = JSON.parse(event.data);
|
|
2775
|
+
if (message.type === type) {
|
|
2776
|
+
resolve(message);
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
catch (error) {
|
|
2780
|
+
reject(error);
|
|
2781
|
+
}
|
|
2782
|
+
};
|
|
2783
|
+
bindings.push(context.events.addListener(navigator.serviceWorker, 'message', handleIncomingMessage), context.events.addListener(navigator.serviceWorker, 'messageerror', reject));
|
|
2784
|
+
}).finally(() => {
|
|
2785
|
+
bindings.forEach((unbind) => unbind());
|
|
2786
|
+
});
|
|
2787
|
+
},
|
|
2788
|
+
},
|
|
2789
|
+
};
|
|
2790
|
+
// Error when attempting to run this function in a NodeJS environment.
|
|
2791
|
+
if (isNodeProcess()) {
|
|
2792
|
+
throw new Error('[MSW] Failed to execute `setupWorker` in a non-browser environment. Consider using `setupServer` for NodeJS environment instead.');
|
|
2793
|
+
}
|
|
2794
|
+
return {
|
|
2795
|
+
start: createStart(context),
|
|
2796
|
+
stop: createStop(context),
|
|
2797
|
+
use(...handlers) {
|
|
2798
|
+
use(context.requestHandlers, ...handlers);
|
|
2799
|
+
},
|
|
2800
|
+
restoreHandlers() {
|
|
2801
|
+
restoreHandlers(context.requestHandlers);
|
|
2802
|
+
},
|
|
2803
|
+
resetHandlers(...nextHandlers) {
|
|
2804
|
+
context.requestHandlers = resetHandlers(requestHandlers, ...nextHandlers);
|
|
2805
|
+
},
|
|
2806
|
+
};
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2736
2809
|
/**
|
|
2737
2810
|
* Formats a mocked request for introspection in browser's console.
|
|
2738
2811
|
*/
|
|
@@ -2744,12 +2817,10 @@ If this message still persists after updating, please report an issue: https://g
|
|
|
2744
2817
|
* Formats a mocked response for introspection in browser's console.
|
|
2745
2818
|
*/
|
|
2746
2819
|
function prepareResponse(res) {
|
|
2747
|
-
|
|
2748
|
-
const resHeaders = lib.listToHeaders(res.headers);
|
|
2820
|
+
const responseHeaders = lib.listToHeaders(res.headers);
|
|
2749
2821
|
return Object.assign(Object.assign({}, res), {
|
|
2750
2822
|
// Parse a response JSON body for preview in the logs
|
|
2751
|
-
body: (
|
|
2752
|
-
: res.body });
|
|
2823
|
+
body: parseBody(res.body, responseHeaders) });
|
|
2753
2824
|
}
|
|
2754
2825
|
|
|
2755
2826
|
function getTimestamp() {
|
|
@@ -2826,10 +2897,10 @@ If this message still persists after updating, please report an issue: https://g
|
|
|
2826
2897
|
});
|
|
2827
2898
|
|
|
2828
2899
|
/**
|
|
2829
|
-
*
|
|
2900
|
+
* Returns an absolute URL based on the given relative URL, if possible.
|
|
2830
2901
|
* Ignores regular expressions.
|
|
2831
2902
|
*/
|
|
2832
|
-
const
|
|
2903
|
+
const getAbsoluteUrl = (mask) => {
|
|
2833
2904
|
// Global `location` object doesn't exist in Node.
|
|
2834
2905
|
// Relative request predicate URL cannot become absolute.
|
|
2835
2906
|
const hasLocation = typeof location !== 'undefined';
|
|
@@ -2840,17 +2911,21 @@ If this message still persists after updating, please report an issue: https://g
|
|
|
2840
2911
|
|
|
2841
2912
|
/**
|
|
2842
2913
|
* Converts a given request handler mask into a URL, if given a valid URL string.
|
|
2843
|
-
* Otherwise, returns the mask as-is.
|
|
2844
2914
|
*/
|
|
2845
|
-
function
|
|
2846
|
-
|
|
2915
|
+
function getUrlByMask(mask) {
|
|
2916
|
+
/**
|
|
2917
|
+
* If a string mask contains an asterisk (wildcard), return it as-is.
|
|
2918
|
+
* Converting a URL-like path string into an actual URL is misleading.
|
|
2919
|
+
* @see https://github.com/mswjs/msw/issues/357
|
|
2920
|
+
*/
|
|
2921
|
+
if (mask instanceof RegExp || mask.includes('*')) {
|
|
2847
2922
|
return mask;
|
|
2848
2923
|
}
|
|
2849
2924
|
try {
|
|
2850
2925
|
// Attempt to create a URL instance out of the mask string.
|
|
2851
2926
|
// Resolve mask to an absolute URL, because even a valid relative URL
|
|
2852
2927
|
// cannot be converted into the URL instance (required absolute URL only).
|
|
2853
|
-
return new URL(
|
|
2928
|
+
return new URL(getAbsoluteUrl(mask));
|
|
2854
2929
|
}
|
|
2855
2930
|
catch (error) {
|
|
2856
2931
|
// Otherwise, the mask is a path string.
|
|
@@ -2863,7 +2938,7 @@ If this message still persists after updating, please report an issue: https://g
|
|
|
2863
2938
|
? getCleanUrl_1.getCleanUrl(resolvedMask)
|
|
2864
2939
|
: resolvedMask instanceof RegExp
|
|
2865
2940
|
? resolvedMask
|
|
2866
|
-
:
|
|
2941
|
+
: getAbsoluteUrl(resolvedMask);
|
|
2867
2942
|
}
|
|
2868
2943
|
|
|
2869
2944
|
/**
|
|
@@ -2871,13 +2946,14 @@ If this message still persists after updating, please report an issue: https://g
|
|
|
2871
2946
|
* against a mask.
|
|
2872
2947
|
*/
|
|
2873
2948
|
function matchRequestUrl(url, mask) {
|
|
2874
|
-
const resolvedMask =
|
|
2949
|
+
const resolvedMask = getUrlByMask(mask);
|
|
2875
2950
|
const cleanMask = getCleanMask(resolvedMask);
|
|
2876
2951
|
const cleanRequestUrl = getCleanUrl_1.getCleanUrl(url);
|
|
2877
2952
|
return match(cleanMask, cleanRequestUrl);
|
|
2878
2953
|
}
|
|
2879
2954
|
|
|
2880
2955
|
(function (RESTMethods) {
|
|
2956
|
+
RESTMethods["HEAD"] = "HEAD";
|
|
2881
2957
|
RESTMethods["GET"] = "GET";
|
|
2882
2958
|
RESTMethods["POST"] = "POST";
|
|
2883
2959
|
RESTMethods["PUT"] = "PUT";
|
|
@@ -2898,7 +2974,7 @@ If this message still persists after updating, please report an issue: https://g
|
|
|
2898
2974
|
};
|
|
2899
2975
|
const createRestHandler = (method) => {
|
|
2900
2976
|
return (mask, resolver) => {
|
|
2901
|
-
const resolvedMask =
|
|
2977
|
+
const resolvedMask = getUrlByMask(mask);
|
|
2902
2978
|
return {
|
|
2903
2979
|
parse(req) {
|
|
2904
2980
|
// Match the request during parsing to prevent matching it twice
|
|
@@ -2937,14 +3013,7 @@ ${queryParams
|
|
|
2937
3013
|
})\
|
|
2938
3014
|
`);
|
|
2939
3015
|
}
|
|
2940
|
-
const
|
|
2941
|
-
const publicUrl = isRelativeRequest
|
|
2942
|
-
? req.url.pathname
|
|
2943
|
-
: format({
|
|
2944
|
-
protocol: req.url.protocol,
|
|
2945
|
-
host: req.url.host,
|
|
2946
|
-
pathname: req.url.pathname,
|
|
2947
|
-
});
|
|
3016
|
+
const publicUrl = getPublicUrlFromRequest(req);
|
|
2948
3017
|
const loggedRequest = prepareRequest(req);
|
|
2949
3018
|
const loggedResponse = prepareResponse(res);
|
|
2950
3019
|
console.groupCollapsed('[MSW] %s %s %s (%c%s%c)', getTimestamp(), req.method, publicUrl, `color:${getStatusCodeColor(res.status)}`, res.status, 'color:inherit');
|
|
@@ -2960,6 +3029,7 @@ ${queryParams
|
|
|
2960
3029
|
};
|
|
2961
3030
|
};
|
|
2962
3031
|
const rest = {
|
|
3032
|
+
head: createRestHandler(exports.RESTMethods.HEAD),
|
|
2963
3033
|
get: createRestHandler(exports.RESTMethods.GET),
|
|
2964
3034
|
post: createRestHandler(exports.RESTMethods.POST),
|
|
2965
3035
|
put: createRestHandler(exports.RESTMethods.PUT),
|
|
@@ -5975,96 +6045,109 @@ ${queryParams
|
|
|
5975
6045
|
function parseQuery(query, definitionOperation = 'query') {
|
|
5976
6046
|
var _a;
|
|
5977
6047
|
const ast = parse$1(query);
|
|
5978
|
-
const operationDef = ast.definitions.find((def) =>
|
|
5979
|
-
def.
|
|
6048
|
+
const operationDef = ast.definitions.find((def) => {
|
|
6049
|
+
return (def.kind === 'OperationDefinition' &&
|
|
6050
|
+
(definitionOperation === 'all' || def.operation === definitionOperation));
|
|
6051
|
+
});
|
|
5980
6052
|
return {
|
|
6053
|
+
operationType: operationDef === null || operationDef === void 0 ? void 0 : operationDef.operation,
|
|
5981
6054
|
operationName: (_a = operationDef === null || operationDef === void 0 ? void 0 : operationDef.name) === null || _a === void 0 ? void 0 : _a.value,
|
|
5982
6055
|
};
|
|
5983
6056
|
}
|
|
5984
|
-
|
|
5985
|
-
return
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
if (!query) {
|
|
5997
|
-
return null;
|
|
5998
|
-
}
|
|
5999
|
-
const variables = variablesString
|
|
6000
|
-
? jsonParse(variablesString)
|
|
6001
|
-
: {};
|
|
6002
|
-
const { operationName } = parseQuery(query, operationType);
|
|
6003
|
-
return {
|
|
6004
|
-
operationType,
|
|
6005
|
-
operationName,
|
|
6006
|
-
variables,
|
|
6007
|
-
};
|
|
6008
|
-
}
|
|
6009
|
-
case 'POST': {
|
|
6010
|
-
if (!((_a = req.body) === null || _a === void 0 ? void 0 : _a.query)) {
|
|
6011
|
-
return null;
|
|
6012
|
-
}
|
|
6013
|
-
const { query, variables } = req.body;
|
|
6014
|
-
const { operationName } = parseQuery(query, operationType);
|
|
6015
|
-
return {
|
|
6016
|
-
operationType,
|
|
6017
|
-
operationName,
|
|
6018
|
-
variables,
|
|
6019
|
-
};
|
|
6020
|
-
}
|
|
6021
|
-
default:
|
|
6057
|
+
function graphQLRequestHandler(expectedOperationType, expectedOperationName, mask, resolver) {
|
|
6058
|
+
return {
|
|
6059
|
+
resolver,
|
|
6060
|
+
parse(req) {
|
|
6061
|
+
var _a;
|
|
6062
|
+
// According to the GraphQL specification, a GraphQL request can be issued
|
|
6063
|
+
// using both "GET" and "POST" methods.
|
|
6064
|
+
switch (req.method) {
|
|
6065
|
+
case 'GET': {
|
|
6066
|
+
const query = req.url.searchParams.get('query');
|
|
6067
|
+
const variablesString = req.url.searchParams.get('variables') || '';
|
|
6068
|
+
if (!query) {
|
|
6022
6069
|
return null;
|
|
6070
|
+
}
|
|
6071
|
+
const variables = variablesString
|
|
6072
|
+
? jsonParse(variablesString)
|
|
6073
|
+
: {};
|
|
6074
|
+
const { operationType, operationName } = parseQuery(query, expectedOperationType);
|
|
6075
|
+
return {
|
|
6076
|
+
operationType,
|
|
6077
|
+
operationName,
|
|
6078
|
+
variables,
|
|
6079
|
+
};
|
|
6023
6080
|
}
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
return
|
|
6081
|
+
case 'POST': {
|
|
6082
|
+
if (!((_a = req.body) === null || _a === void 0 ? void 0 : _a.query)) {
|
|
6083
|
+
return null;
|
|
6084
|
+
}
|
|
6085
|
+
const { query, variables } = req.body;
|
|
6086
|
+
const { operationType, operationName } = parseQuery(query, expectedOperationType);
|
|
6087
|
+
return {
|
|
6088
|
+
operationType,
|
|
6089
|
+
operationName,
|
|
6090
|
+
variables,
|
|
6091
|
+
};
|
|
6031
6092
|
}
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
return
|
|
6042
|
-
}
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6093
|
+
default:
|
|
6094
|
+
return null;
|
|
6095
|
+
}
|
|
6096
|
+
},
|
|
6097
|
+
getPublicRequest(req, parsed) {
|
|
6098
|
+
return Object.assign(Object.assign({}, req), { variables: parsed.variables || {} });
|
|
6099
|
+
},
|
|
6100
|
+
predicate(req, parsed) {
|
|
6101
|
+
if (!parsed || !parsed.operationName) {
|
|
6102
|
+
return false;
|
|
6103
|
+
}
|
|
6104
|
+
// Match the request URL against a given mask,
|
|
6105
|
+
// in case of an endpoint-specific request handler.
|
|
6106
|
+
const hasMatchingMask = matchRequestUrl(req.url, mask);
|
|
6107
|
+
const isMatchingOperation = expectedOperationName instanceof RegExp
|
|
6108
|
+
? expectedOperationName.test(parsed.operationName)
|
|
6109
|
+
: expectedOperationName === parsed.operationName;
|
|
6110
|
+
return hasMatchingMask.matches && isMatchingOperation;
|
|
6111
|
+
},
|
|
6112
|
+
defineContext() {
|
|
6113
|
+
return graphqlContext;
|
|
6114
|
+
},
|
|
6115
|
+
log(req, res, handler, parsed) {
|
|
6116
|
+
const { operationType, operationName } = parsed;
|
|
6117
|
+
const loggedRequest = prepareRequest(req);
|
|
6118
|
+
const loggedResponse = prepareResponse(res);
|
|
6119
|
+
console.groupCollapsed('[MSW] %s %s (%c%s%c)', getTimestamp(), operationName, `color:${getStatusCodeColor(res.status)}`, res.status, 'color:inherit');
|
|
6120
|
+
console.log('Request:', loggedRequest);
|
|
6121
|
+
console.log('Handler:', {
|
|
6122
|
+
operationType,
|
|
6123
|
+
operationName: expectedOperationName,
|
|
6124
|
+
predicate: handler.predicate,
|
|
6125
|
+
});
|
|
6126
|
+
console.log('Response:', loggedResponse);
|
|
6127
|
+
console.groupEnd();
|
|
6128
|
+
},
|
|
6129
|
+
};
|
|
6130
|
+
}
|
|
6131
|
+
const createGraphQLScopedHandler = (expectedOperationType, mask) => {
|
|
6132
|
+
return (expectedOperationName, resolver) => {
|
|
6133
|
+
return graphQLRequestHandler(expectedOperationType, expectedOperationName, mask, resolver);
|
|
6134
|
+
};
|
|
6135
|
+
};
|
|
6136
|
+
const createGraphQLOperationHandler = (mask) => {
|
|
6137
|
+
return (resolver) => {
|
|
6138
|
+
return graphQLRequestHandler('all', new RegExp('.*'), mask, resolver);
|
|
6058
6139
|
};
|
|
6059
6140
|
};
|
|
6060
6141
|
const graphqlStandardHandlers = {
|
|
6061
|
-
|
|
6062
|
-
|
|
6142
|
+
operation: createGraphQLOperationHandler('*'),
|
|
6143
|
+
query: createGraphQLScopedHandler('query', '*'),
|
|
6144
|
+
mutation: createGraphQLScopedHandler('mutation', '*'),
|
|
6063
6145
|
};
|
|
6064
6146
|
function createGraphQLLink(uri) {
|
|
6065
6147
|
return {
|
|
6066
|
-
|
|
6067
|
-
|
|
6148
|
+
operation: createGraphQLOperationHandler(uri),
|
|
6149
|
+
query: createGraphQLScopedHandler('query', uri),
|
|
6150
|
+
mutation: createGraphQLScopedHandler('mutation', uri),
|
|
6068
6151
|
};
|
|
6069
6152
|
}
|
|
6070
6153
|
const graphql = Object.assign(Object.assign({}, graphqlStandardHandlers), { link: createGraphQLLink });
|