msw 2.4.0 → 2.4.2
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/cli/index.js +0 -1
- package/cli/init.js +59 -31
- package/cli/invariant.js +1 -1
- package/lib/core/utils/internal/parseGraphQLRequest.js +4 -4
- package/lib/core/utils/internal/parseGraphQLRequest.js.map +1 -1
- package/lib/core/utils/internal/parseGraphQLRequest.mjs +6 -3
- package/lib/core/utils/internal/parseGraphQLRequest.mjs.map +1 -1
- package/lib/iife/index.js +26604 -16017
- package/lib/iife/index.js.map +1 -1
- package/lib/mockServiceWorker.js +1 -1
- package/package.json +3 -2
- package/src/core/utils/internal/parseGraphQLRequest.ts +11 -3
package/cli/index.js
CHANGED
package/cli/init.js
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const chalk = require('chalk')
|
|
4
|
-
const { until } = require('@open-draft/until')
|
|
5
4
|
const confirm = require('@inquirer/confirm').default
|
|
6
5
|
const invariant = require('./invariant')
|
|
7
6
|
const { SERVICE_WORKER_BUILD_PATH } = require('../config/constants')
|
|
8
7
|
|
|
9
8
|
module.exports = async function init(args) {
|
|
10
|
-
const [, publicDir] = args._
|
|
11
9
|
const CWD = args.cwd || process.cwd()
|
|
10
|
+
const publicDir = args._[1] ? normalizePath(args._[1]) : undefined
|
|
12
11
|
|
|
13
12
|
const packageJsonPath = path.resolve(CWD, 'package.json')
|
|
14
13
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
15
|
-
const savedWorkerDirectories = Array.prototype
|
|
16
|
-
(packageJson.msw && packageJson.msw.workerDirectory) || []
|
|
17
|
-
|
|
14
|
+
const savedWorkerDirectories = Array.prototype
|
|
15
|
+
.concat((packageJson.msw && packageJson.msw.workerDirectory) || [])
|
|
16
|
+
.map(normalizePath)
|
|
18
17
|
|
|
19
18
|
if (publicDir) {
|
|
20
19
|
// If the public directory was provided, copy the worker script
|
|
21
20
|
// to that directory only. Even if there are paths stored in "msw.workerDirectory",
|
|
22
21
|
// those will not be touched.
|
|
23
22
|
await copyWorkerScript(publicDir, CWD)
|
|
24
|
-
const relativePublicDir =
|
|
23
|
+
const relativePublicDir = path.relative(CWD, publicDir)
|
|
25
24
|
printSuccessMessage([publicDir])
|
|
26
25
|
|
|
27
26
|
if (args.save) {
|
|
@@ -52,8 +51,7 @@ module.exports = async function init(args) {
|
|
|
52
51
|
return
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
// Calling "init" without a public directory but with the "--save" flag
|
|
56
|
-
// is no-op.
|
|
54
|
+
// Calling "init" without a public directory but with the "--save" flag is a no-op.
|
|
57
55
|
invariant(
|
|
58
56
|
args.save == null,
|
|
59
57
|
'Failed to copy the worker script: cannot call the "init" command without a public directory but with the "--save" flag. Either drop the "--save" flag to copy the worker script to all paths listed in "msw.workerDirectory", or add an explicit public directory to the command, like "npx msw init ./public".',
|
|
@@ -69,7 +67,7 @@ module.exports = async function init(args) {
|
|
|
69
67
|
return copyWorkerScript(destination, CWD).catch((error) => {
|
|
70
68
|
// Inject the absolute destination path onto the copy function rejections
|
|
71
69
|
// so it's available in the failed paths array below.
|
|
72
|
-
throw [
|
|
70
|
+
throw [toAbsolutePath(destination, CWD), error]
|
|
73
71
|
})
|
|
74
72
|
}),
|
|
75
73
|
)
|
|
@@ -92,51 +90,61 @@ module.exports = async function init(args) {
|
|
|
92
90
|
}
|
|
93
91
|
}
|
|
94
92
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
/**
|
|
94
|
+
* @param {string} maybeAbsolutePath
|
|
95
|
+
* @param {string} cwd
|
|
96
|
+
* @returns {string}
|
|
97
|
+
*/
|
|
98
|
+
function toAbsolutePath(maybeAbsolutePath, cwd) {
|
|
100
99
|
return path.isAbsolute(maybeAbsolutePath)
|
|
101
100
|
? maybeAbsolutePath
|
|
102
101
|
: path.resolve(cwd, maybeAbsolutePath)
|
|
103
102
|
}
|
|
104
103
|
|
|
104
|
+
/**
|
|
105
|
+
* @param {string} destination
|
|
106
|
+
* @param {string} cwd
|
|
107
|
+
* @returns {Promise<string>}
|
|
108
|
+
*/
|
|
105
109
|
async function copyWorkerScript(destination, cwd) {
|
|
106
110
|
// When running as a part of "postinstall" script, "cwd" equals the library's directory.
|
|
107
111
|
// The "postinstall" script resolves the right absolute public directory path.
|
|
108
|
-
const absolutePublicDir =
|
|
112
|
+
const absolutePublicDir = toAbsolutePath(destination, cwd)
|
|
109
113
|
|
|
110
114
|
if (!fs.existsSync(absolutePublicDir)) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
await fs.promises
|
|
116
|
+
.mkdir(absolutePublicDir, { recursive: true })
|
|
117
|
+
.catch((error) => {
|
|
118
|
+
throw new Error(
|
|
119
|
+
invariant(
|
|
120
|
+
false,
|
|
121
|
+
'Failed to copy the worker script at "%s": directory does not exist and could not be created.\nMake sure to include a relative path to the public directory of your application.\n\nSee the original error below:\n\n%s',
|
|
122
|
+
absolutePublicDir,
|
|
123
|
+
error,
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
})
|
|
122
127
|
}
|
|
123
128
|
|
|
124
129
|
console.log('Copying the worker script at "%s"...', absolutePublicDir)
|
|
125
130
|
|
|
126
|
-
const
|
|
127
|
-
const
|
|
131
|
+
const workerFilename = path.basename(SERVICE_WORKER_BUILD_PATH)
|
|
132
|
+
const workerDestinationPath = path.resolve(absolutePublicDir, workerFilename)
|
|
128
133
|
|
|
129
|
-
fs.copyFileSync(SERVICE_WORKER_BUILD_PATH,
|
|
134
|
+
fs.copyFileSync(SERVICE_WORKER_BUILD_PATH, workerDestinationPath)
|
|
130
135
|
|
|
131
|
-
return
|
|
136
|
+
return workerDestinationPath
|
|
132
137
|
}
|
|
133
138
|
|
|
139
|
+
/**
|
|
140
|
+
* @param {Array<string>} paths
|
|
141
|
+
*/
|
|
134
142
|
function printSuccessMessage(paths) {
|
|
135
143
|
console.log(`
|
|
136
144
|
${chalk.green('Worker script successfully copied!')}
|
|
137
145
|
${paths.map((path) => chalk.gray(` - ${path}\n`))}
|
|
138
146
|
Continue by describing the network in your application:
|
|
139
|
-
|
|
147
|
+
|
|
140
148
|
|
|
141
149
|
${chalk.cyan.bold('https://mswjs.io/docs/getting-started')}
|
|
142
150
|
`)
|
|
@@ -151,6 +159,10 @@ ${pathsWithErrors
|
|
|
151
159
|
`)
|
|
152
160
|
}
|
|
153
161
|
|
|
162
|
+
/**
|
|
163
|
+
* @param {string} packageJsonPath
|
|
164
|
+
* @param {string} publicDir
|
|
165
|
+
*/
|
|
154
166
|
function saveWorkerDirectory(packageJsonPath, publicDir) {
|
|
155
167
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
156
168
|
|
|
@@ -179,6 +191,12 @@ function saveWorkerDirectory(packageJsonPath, publicDir) {
|
|
|
179
191
|
)
|
|
180
192
|
}
|
|
181
193
|
|
|
194
|
+
/**
|
|
195
|
+
* @param {string} message
|
|
196
|
+
* @param {string} packageJsonPath
|
|
197
|
+
* @param {string} publicDir
|
|
198
|
+
* @returns {void}
|
|
199
|
+
*/
|
|
182
200
|
function promptWorkerDirectoryUpdate(message, packageJsonPath, publicDir) {
|
|
183
201
|
return confirm({
|
|
184
202
|
theme: {
|
|
@@ -191,3 +209,13 @@ function promptWorkerDirectoryUpdate(message, packageJsonPath, publicDir) {
|
|
|
191
209
|
}
|
|
192
210
|
})
|
|
193
211
|
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Normalizes the given path, replacing ambiguous path separators
|
|
215
|
+
* with the platform-specific path separator.
|
|
216
|
+
* @param {string} input Path to normalize.
|
|
217
|
+
* @returns {string}
|
|
218
|
+
*/
|
|
219
|
+
function normalizePath(input) {
|
|
220
|
+
return input.replace(/[\\|\/]+/g, path.sep)
|
|
221
|
+
}
|
package/cli/invariant.js
CHANGED
|
@@ -22,7 +22,6 @@ __export(parseGraphQLRequest_exports, {
|
|
|
22
22
|
parseGraphQLRequest: () => parseGraphQLRequest
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(parseGraphQLRequest_exports);
|
|
25
|
-
var import_graphql = require("graphql");
|
|
26
25
|
var import_toPublicUrl = require("../request/toPublicUrl.js");
|
|
27
26
|
var import_devUtils = require("./devUtils.js");
|
|
28
27
|
var import_jsonParse = require("./jsonParse.js");
|
|
@@ -36,9 +35,10 @@ function parseDocumentNode(node) {
|
|
|
36
35
|
operationName: operationDef?.name?.value
|
|
37
36
|
};
|
|
38
37
|
}
|
|
39
|
-
function parseQuery(query) {
|
|
38
|
+
async function parseQuery(query) {
|
|
39
|
+
const { parse } = require("graphql");
|
|
40
40
|
try {
|
|
41
|
-
const ast =
|
|
41
|
+
const ast = parse(query);
|
|
42
42
|
return parseDocumentNode(ast);
|
|
43
43
|
} catch (error) {
|
|
44
44
|
return error;
|
|
@@ -123,7 +123,7 @@ async function parseGraphQLRequest(request) {
|
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
const { query, variables } = input;
|
|
126
|
-
const parsedResult = parseQuery(query);
|
|
126
|
+
const parsedResult = await parseQuery(query);
|
|
127
127
|
if (parsedResult instanceof Error) {
|
|
128
128
|
const requestPublicUrl = (0, import_toPublicUrl.toPublicUrl)(request.url);
|
|
129
129
|
throw new Error(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/utils/internal/parseGraphQLRequest.ts"],"sourcesContent":["import type {\n DocumentNode,\n OperationDefinitionNode,\n OperationTypeNode,\n} from 'graphql'\nimport
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/utils/internal/parseGraphQLRequest.ts"],"sourcesContent":["import type {\n DocumentNode,\n OperationDefinitionNode,\n OperationTypeNode,\n} from 'graphql'\nimport type { GraphQLVariables } from '../../handlers/GraphQLHandler'\nimport { toPublicUrl } from '../request/toPublicUrl'\nimport { devUtils } from './devUtils'\nimport { jsonParse } from './jsonParse'\nimport { parseMultipartData } from './parseMultipartData'\n\ninterface GraphQLInput {\n query: string | null\n variables?: GraphQLVariables\n}\n\nexport interface ParsedGraphQLQuery {\n operationType: OperationTypeNode\n operationName?: string\n}\n\nexport type ParsedGraphQLRequest<\n VariablesType extends GraphQLVariables = GraphQLVariables,\n> =\n | (ParsedGraphQLQuery & {\n query: string\n variables?: VariablesType\n })\n | undefined\n\nexport function parseDocumentNode(node: DocumentNode): ParsedGraphQLQuery {\n const operationDef = node.definitions.find((definition) => {\n return definition.kind === 'OperationDefinition'\n }) as OperationDefinitionNode\n\n return {\n operationType: operationDef?.operation,\n operationName: operationDef?.name?.value,\n }\n}\n\nasync function parseQuery(query: string): Promise<ParsedGraphQLQuery | Error> {\n /**\n * @note Use `require` to get the \"graphql\" module here.\n * It has to be scoped to this function because this module leaks to the\n * root export. It has to be `require` because tools like Jest have trouble\n * handling dynamic imports. It gets replaced with a dynamic import on build time.\n */\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { parse } = require('graphql')\n\n try {\n const ast = parse(query)\n return parseDocumentNode(ast)\n } catch (error) {\n return error as Error\n }\n}\n\nexport type GraphQLParsedOperationsMap = Record<string, string[]>\nexport type GraphQLMultipartRequestBody = {\n operations: string\n map?: string\n} & {\n [fileName: string]: File\n}\n\nfunction extractMultipartVariables<VariablesType extends GraphQLVariables>(\n variables: VariablesType,\n map: GraphQLParsedOperationsMap,\n files: Record<string, File>,\n) {\n const operations = { variables }\n\n for (const [key, pathArray] of Object.entries(map)) {\n if (!(key in files)) {\n throw new Error(`Given files do not have a key '${key}' .`)\n }\n\n for (const dotPath of pathArray) {\n const [lastPath, ...reversedPaths] = dotPath.split('.').reverse()\n const paths = reversedPaths.reverse()\n let target: Record<string, any> = operations\n\n for (const path of paths) {\n if (!(path in target)) {\n throw new Error(`Property '${paths}' is not in operations.`)\n }\n\n target = target[path]\n }\n\n target[lastPath] = files[key]\n }\n }\n\n return operations.variables\n}\n\nasync function getGraphQLInput(request: Request): Promise<GraphQLInput | null> {\n switch (request.method) {\n case 'GET': {\n const url = new URL(request.url)\n const query = url.searchParams.get('query')\n const variables = url.searchParams.get('variables') || ''\n\n return {\n query,\n variables: jsonParse(variables),\n }\n }\n\n case 'POST': {\n // Clone the request so we could read its body without locking\n // the body stream to the downward consumers.\n const requestClone = request.clone()\n\n // Handle multipart body GraphQL operations.\n if (\n request.headers.get('content-type')?.includes('multipart/form-data')\n ) {\n const responseJson = parseMultipartData<GraphQLMultipartRequestBody>(\n await requestClone.text(),\n request.headers,\n )\n\n if (!responseJson) {\n return null\n }\n\n const { operations, map, ...files } = responseJson\n const parsedOperations =\n jsonParse<{ query?: string; variables?: GraphQLVariables }>(\n operations,\n ) || {}\n\n if (!parsedOperations.query) {\n return null\n }\n\n const parsedMap = jsonParse<GraphQLParsedOperationsMap>(map || '') || {}\n const variables = parsedOperations.variables\n ? extractMultipartVariables(\n parsedOperations.variables,\n parsedMap,\n files,\n )\n : {}\n\n return {\n query: parsedOperations.query,\n variables,\n }\n }\n\n // Handle plain POST GraphQL operations.\n const requestJson: {\n query: string\n variables?: GraphQLVariables\n operations?: any /** @todo Annotate this */\n } = await requestClone.json().catch(() => null)\n\n if (requestJson?.query) {\n const { query, variables } = requestJson\n\n return {\n query,\n variables,\n }\n }\n }\n\n default:\n return null\n }\n}\n\n/**\n * Determines if a given request can be considered a GraphQL request.\n * Does not parse the query and does not guarantee its validity.\n */\nexport async function parseGraphQLRequest(\n request: Request,\n): Promise<ParsedGraphQLRequest> {\n const input = await getGraphQLInput(request)\n\n if (!input || !input.query) {\n return\n }\n\n const { query, variables } = input\n const parsedResult = await parseQuery(query)\n\n if (parsedResult instanceof Error) {\n const requestPublicUrl = toPublicUrl(request.url)\n\n throw new Error(\n devUtils.formatMessage(\n 'Failed to intercept a GraphQL request to \"%s %s\": cannot parse query. See the error message from the parser below.\\n\\n%s',\n request.method,\n requestPublicUrl,\n parsedResult.message,\n ),\n )\n }\n\n return {\n query: input.query,\n operationType: parsedResult.operationType,\n operationName: parsedResult.operationName,\n variables,\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { parse } from "graphql";
|
|
2
1
|
import { toPublicUrl } from '../request/toPublicUrl.mjs';
|
|
3
2
|
import { devUtils } from './devUtils.mjs';
|
|
4
3
|
import { jsonParse } from './jsonParse.mjs';
|
|
@@ -12,7 +11,11 @@ function parseDocumentNode(node) {
|
|
|
12
11
|
operationName: operationDef?.name?.value
|
|
13
12
|
};
|
|
14
13
|
}
|
|
15
|
-
function parseQuery(query) {
|
|
14
|
+
async function parseQuery(query) {
|
|
15
|
+
const { parse } = await import("graphql").catch((error) => {
|
|
16
|
+
console.error('[MSW] Failed to parse a GraphQL query: cannot import the "graphql" module. Please make sure you install it if you wish to intercept GraphQL requests. See the original import error below.');
|
|
17
|
+
throw error;
|
|
18
|
+
});
|
|
16
19
|
try {
|
|
17
20
|
const ast = parse(query);
|
|
18
21
|
return parseDocumentNode(ast);
|
|
@@ -99,7 +102,7 @@ async function parseGraphQLRequest(request) {
|
|
|
99
102
|
return;
|
|
100
103
|
}
|
|
101
104
|
const { query, variables } = input;
|
|
102
|
-
const parsedResult = parseQuery(query);
|
|
105
|
+
const parsedResult = await parseQuery(query);
|
|
103
106
|
if (parsedResult instanceof Error) {
|
|
104
107
|
const requestPublicUrl = toPublicUrl(request.url);
|
|
105
108
|
throw new Error(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/utils/internal/parseGraphQLRequest.ts"],"sourcesContent":["import type {\n DocumentNode,\n OperationDefinitionNode,\n OperationTypeNode,\n} from 'graphql'\nimport
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/utils/internal/parseGraphQLRequest.ts"],"sourcesContent":["import type {\n DocumentNode,\n OperationDefinitionNode,\n OperationTypeNode,\n} from 'graphql'\nimport type { GraphQLVariables } from '../../handlers/GraphQLHandler'\nimport { toPublicUrl } from '../request/toPublicUrl'\nimport { devUtils } from './devUtils'\nimport { jsonParse } from './jsonParse'\nimport { parseMultipartData } from './parseMultipartData'\n\ninterface GraphQLInput {\n query: string | null\n variables?: GraphQLVariables\n}\n\nexport interface ParsedGraphQLQuery {\n operationType: OperationTypeNode\n operationName?: string\n}\n\nexport type ParsedGraphQLRequest<\n VariablesType extends GraphQLVariables = GraphQLVariables,\n> =\n | (ParsedGraphQLQuery & {\n query: string\n variables?: VariablesType\n })\n | undefined\n\nexport function parseDocumentNode(node: DocumentNode): ParsedGraphQLQuery {\n const operationDef = node.definitions.find((definition) => {\n return definition.kind === 'OperationDefinition'\n }) as OperationDefinitionNode\n\n return {\n operationType: operationDef?.operation,\n operationName: operationDef?.name?.value,\n }\n}\n\nasync function parseQuery(query: string): Promise<ParsedGraphQLQuery | Error> {\n /**\n * @note Use `require` to get the \"graphql\" module here.\n * It has to be scoped to this function because this module leaks to the\n * root export. It has to be `require` because tools like Jest have trouble\n * handling dynamic imports. It gets replaced with a dynamic import on build time.\n */\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { parse } =await import('graphql').catch((error) => {console.error('[MSW] Failed to parse a GraphQL query: cannot import the \"graphql\" module. Please make sure you install it if you wish to intercept GraphQL requests. See the original import error below.'); throw error})\n\n try {\n const ast = parse(query)\n return parseDocumentNode(ast)\n } catch (error) {\n return error as Error\n }\n}\n\nexport type GraphQLParsedOperationsMap = Record<string, string[]>\nexport type GraphQLMultipartRequestBody = {\n operations: string\n map?: string\n} & {\n [fileName: string]: File\n}\n\nfunction extractMultipartVariables<VariablesType extends GraphQLVariables>(\n variables: VariablesType,\n map: GraphQLParsedOperationsMap,\n files: Record<string, File>,\n) {\n const operations = { variables }\n\n for (const [key, pathArray] of Object.entries(map)) {\n if (!(key in files)) {\n throw new Error(`Given files do not have a key '${key}' .`)\n }\n\n for (const dotPath of pathArray) {\n const [lastPath, ...reversedPaths] = dotPath.split('.').reverse()\n const paths = reversedPaths.reverse()\n let target: Record<string, any> = operations\n\n for (const path of paths) {\n if (!(path in target)) {\n throw new Error(`Property '${paths}' is not in operations.`)\n }\n\n target = target[path]\n }\n\n target[lastPath] = files[key]\n }\n }\n\n return operations.variables\n}\n\nasync function getGraphQLInput(request: Request): Promise<GraphQLInput | null> {\n switch (request.method) {\n case 'GET': {\n const url = new URL(request.url)\n const query = url.searchParams.get('query')\n const variables = url.searchParams.get('variables') || ''\n\n return {\n query,\n variables: jsonParse(variables),\n }\n }\n\n case 'POST': {\n // Clone the request so we could read its body without locking\n // the body stream to the downward consumers.\n const requestClone = request.clone()\n\n // Handle multipart body GraphQL operations.\n if (\n request.headers.get('content-type')?.includes('multipart/form-data')\n ) {\n const responseJson = parseMultipartData<GraphQLMultipartRequestBody>(\n await requestClone.text(),\n request.headers,\n )\n\n if (!responseJson) {\n return null\n }\n\n const { operations, map, ...files } = responseJson\n const parsedOperations =\n jsonParse<{ query?: string; variables?: GraphQLVariables }>(\n operations,\n ) || {}\n\n if (!parsedOperations.query) {\n return null\n }\n\n const parsedMap = jsonParse<GraphQLParsedOperationsMap>(map || '') || {}\n const variables = parsedOperations.variables\n ? extractMultipartVariables(\n parsedOperations.variables,\n parsedMap,\n files,\n )\n : {}\n\n return {\n query: parsedOperations.query,\n variables,\n }\n }\n\n // Handle plain POST GraphQL operations.\n const requestJson: {\n query: string\n variables?: GraphQLVariables\n operations?: any /** @todo Annotate this */\n } = await requestClone.json().catch(() => null)\n\n if (requestJson?.query) {\n const { query, variables } = requestJson\n\n return {\n query,\n variables,\n }\n }\n }\n\n default:\n return null\n }\n}\n\n/**\n * Determines if a given request can be considered a GraphQL request.\n * Does not parse the query and does not guarantee its validity.\n */\nexport async function parseGraphQLRequest(\n request: Request,\n): Promise<ParsedGraphQLRequest> {\n const input = await getGraphQLInput(request)\n\n if (!input || !input.query) {\n return\n }\n\n const { query, variables } = input\n const parsedResult = await parseQuery(query)\n\n if (parsedResult instanceof Error) {\n const requestPublicUrl = toPublicUrl(request.url)\n\n throw new Error(\n devUtils.formatMessage(\n 'Failed to intercept a GraphQL request to \"%s %s\": cannot parse query. See the error message from the parser below.\\n\\n%s',\n request.method,\n requestPublicUrl,\n parsedResult.message,\n ),\n )\n }\n\n return {\n query: input.query,\n operationType: parsedResult.operationType,\n operationName: parsedResult.operationName,\n variables,\n }\n}\n"],"mappings":"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","names":[]}
|