msw 2.1.3 → 2.1.5

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 (57) hide show
  1. package/lib/browser/index.js +1775 -32
  2. package/lib/browser/index.js.map +1 -1
  3. package/lib/browser/index.mjs +1769 -28
  4. package/lib/browser/index.mjs.map +1 -1
  5. package/lib/core/handlers/GraphQLHandler.js +2 -2
  6. package/lib/core/handlers/GraphQLHandler.js.map +1 -1
  7. package/lib/core/handlers/GraphQLHandler.mjs +2 -2
  8. package/lib/core/handlers/GraphQLHandler.mjs.map +1 -1
  9. package/lib/core/handlers/HttpHandler.js +2 -2
  10. package/lib/core/handlers/HttpHandler.js.map +1 -1
  11. package/lib/core/handlers/HttpHandler.mjs +2 -2
  12. package/lib/core/handlers/HttpHandler.mjs.map +1 -1
  13. package/lib/core/sharedOptions.d.mts +0 -2
  14. package/lib/core/sharedOptions.d.ts +0 -2
  15. package/lib/core/utils/handleRequest.js +1 -1
  16. package/lib/core/utils/handleRequest.js.map +1 -1
  17. package/lib/core/utils/handleRequest.mjs +1 -1
  18. package/lib/core/utils/handleRequest.mjs.map +1 -1
  19. package/lib/core/utils/internal/parseGraphQLRequest.js +2 -2
  20. package/lib/core/utils/internal/parseGraphQLRequest.js.map +1 -1
  21. package/lib/core/utils/internal/parseGraphQLRequest.mjs +2 -2
  22. package/lib/core/utils/internal/parseGraphQLRequest.mjs.map +1 -1
  23. package/lib/core/utils/request/onUnhandledRequest.d.mts +1 -4
  24. package/lib/core/utils/request/onUnhandledRequest.d.ts +1 -4
  25. package/lib/core/utils/request/onUnhandledRequest.js +14 -115
  26. package/lib/core/utils/request/onUnhandledRequest.js.map +1 -1
  27. package/lib/core/utils/request/onUnhandledRequest.mjs +14 -107
  28. package/lib/core/utils/request/onUnhandledRequest.mjs.map +1 -1
  29. package/lib/core/utils/request/toPublicUrl.d.mts +7 -0
  30. package/lib/core/utils/request/toPublicUrl.d.ts +7 -0
  31. package/lib/core/utils/request/{getPublicUrlFromRequest.js → toPublicUrl.js} +9 -9
  32. package/lib/core/utils/request/toPublicUrl.js.map +1 -0
  33. package/lib/core/utils/request/toPublicUrl.mjs +11 -0
  34. package/lib/core/utils/request/toPublicUrl.mjs.map +1 -0
  35. package/lib/iife/index.js +46 -233
  36. package/lib/iife/index.js.map +1 -1
  37. package/lib/mockServiceWorker.js +1 -1
  38. package/package.json +5 -8
  39. package/src/browser/setupWorker/glossary.ts +1 -0
  40. package/src/browser/setupWorker/setupWorker.ts +1 -0
  41. package/src/browser/setupWorker/start/createRequestListener.ts +9 -0
  42. package/src/browser/setupWorker/start/createResponseListener.ts +9 -7
  43. package/src/core/handlers/GraphQLHandler.ts +2 -2
  44. package/src/core/handlers/HttpHandler.ts +2 -2
  45. package/src/core/utils/handleRequest.ts +1 -1
  46. package/src/core/utils/internal/parseGraphQLRequest.ts +2 -2
  47. package/src/core/utils/request/onUnhandledRequest.test.ts +5 -101
  48. package/src/core/utils/request/onUnhandledRequest.ts +16 -185
  49. package/src/core/utils/request/toPublicUrl.test.ts +18 -0
  50. package/src/core/utils/request/toPublicUrl.ts +15 -0
  51. package/lib/core/utils/request/getPublicUrlFromRequest.d.mts +0 -7
  52. package/lib/core/utils/request/getPublicUrlFromRequest.d.ts +0 -7
  53. package/lib/core/utils/request/getPublicUrlFromRequest.js.map +0 -1
  54. package/lib/core/utils/request/getPublicUrlFromRequest.mjs +0 -11
  55. package/lib/core/utils/request/getPublicUrlFromRequest.mjs.map +0 -1
  56. package/src/core/utils/request/getPublicUrlFromRequest.test.ts +0 -26
  57. package/src/core/utils/request/getPublicUrlFromRequest.ts +0 -15
@@ -2,7 +2,7 @@
2
2
  /* tslint:disable */
3
3
 
4
4
  /**
5
- * Mock Service Worker (2.1.3).
5
+ * Mock Service Worker (2.1.5).
6
6
  * @see https://github.com/mswjs/msw
7
7
  * - Please do NOT modify this file.
8
8
  * - Please do NOT serve this file on production.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "msw",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "Seamless REST/GraphQL API mocking library for browser and Node.js.",
5
5
  "main": "./lib/core/index.js",
6
6
  "module": "./lib/core/index.mjs",
@@ -93,13 +93,11 @@
93
93
  "sideEffects": false,
94
94
  "dependencies": {
95
95
  "@bundled-es-modules/cookie": "^2.0.0",
96
- "@bundled-es-modules/js-levenshtein": "^2.0.1",
97
96
  "@bundled-es-modules/statuses": "^1.0.1",
98
97
  "@mswjs/cookies": "^1.1.0",
99
- "@mswjs/interceptors": "^0.25.14",
98
+ "@mswjs/interceptors": "^0.25.15",
100
99
  "@open-draft/until": "^2.1.0",
101
100
  "@types/cookie": "^0.6.0",
102
- "@types/js-levenshtein": "^1.1.3",
103
101
  "@types/statuses": "^2.0.4",
104
102
  "chalk": "^4.1.2",
105
103
  "chokidar": "^3.4.2",
@@ -107,7 +105,6 @@
107
105
  "headers-polyfill": "^4.0.2",
108
106
  "inquirer": "^8.2.0",
109
107
  "is-node-process": "^1.2.0",
110
- "js-levenshtein": "^1.1.6",
111
108
  "outvariant": "^1.4.2",
112
109
  "path-to-regexp": "^6.2.0",
113
110
  "strict-event-emitter": "^0.5.1",
@@ -192,10 +189,10 @@
192
189
  "check:exports": "node \"./config/scripts/validate-esm.js\"",
193
190
  "test": "pnpm test:unit && pnpm test:node && pnpm test:browser && pnpm test:native",
194
191
  "test:unit": "vitest",
195
- "test:node": "vitest run --config=./test/node/vitest.config.ts",
196
- "test:native": "vitest run --config=./test/native/vitest.config.ts",
192
+ "test:node": "vitest --config=./test/node/vitest.config.ts",
193
+ "test:native": "vitest --config=./test/native/vitest.config.ts",
197
194
  "test:browser": "playwright test -c ./test/browser/playwright.config.ts",
198
- "test:modules:node": "vitest run --config=./test/modules/node/vitest.config.ts",
195
+ "test:modules:node": "vitest --config=./test/modules/node/vitest.config.ts",
199
196
  "test:modules:browser": "playwright test -c ./test/modules/browser/playwright.config.ts",
200
197
  "test:ts": "ts-node test/typings/run.ts",
201
198
  "release": "release publish",
@@ -103,6 +103,7 @@ export interface SetupWorkerInternalContext {
103
103
  worker: ServiceWorker | null
104
104
  registration: ServiceWorkerRegistration | null
105
105
  requestHandlers: Array<RequestHandler>
106
+ requests: Map<string, Request>
106
107
  emitter: Emitter<LifeCycleEventsMap>
107
108
  keepAliveInterval?: number
108
109
  workerChannel: {
@@ -60,6 +60,7 @@ export class SetupWorkerApi
60
60
  worker: null,
61
61
  registration: null,
62
62
  requestHandlers: this.currentHandlers,
63
+ requests: new Map(),
63
64
  emitter: this.emitter,
64
65
  workerChannel: {
65
66
  on: (eventType, callback) => {
@@ -8,6 +8,7 @@ import {
8
8
  WorkerChannel,
9
9
  } from './utils/createMessageChannel'
10
10
  import { parseWorkerRequest } from '../../utils/parseWorkerRequest'
11
+ import { RequestHandler } from '~/core/handlers/RequestHandler'
11
12
  import { handleRequest } from '~/core/utils/handleRequest'
12
13
  import { RequiredDeep } from '~/core/typeUtils'
13
14
  import { devUtils } from '~/core/utils/internal/devUtils'
@@ -30,6 +31,14 @@ export const createRequestListener = (
30
31
  const request = parseWorkerRequest(message.payload)
31
32
  const requestCloneForLogs = request.clone()
32
33
 
34
+ // Make this the first requets clone before the
35
+ // request resolution pipeline even starts.
36
+ // Store the clone in cache so the first matching
37
+ // request handler would skip the cloning phase.
38
+ const requestClone = request.clone()
39
+ RequestHandler.cache.set(request, requestClone)
40
+ context.requests.set(requestId, requestClone)
41
+
33
42
  try {
34
43
  await handleRequest(
35
44
  request,
@@ -1,8 +1,8 @@
1
- import {
1
+ import type {
2
2
  ServiceWorkerIncomingEventsMap,
3
3
  SetupWorkerInternalContext,
4
4
  } from '../glossary'
5
- import { ServiceWorkerMessage } from './utils/createMessageChannel'
5
+ import type { ServiceWorkerMessage } from './utils/createMessageChannel'
6
6
  import { isResponseWithoutBody } from '@mswjs/interceptors'
7
7
 
8
8
  export function createResponseListener(context: SetupWorkerInternalContext) {
@@ -15,6 +15,12 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
15
15
  ) => {
16
16
  const { payload: responseJson } = message
17
17
 
18
+ // Get the Request instance reference stored in the
19
+ // request listener.
20
+ const { requestId } = responseJson
21
+ const request = context.requests.get(requestId)!
22
+ context.requests.delete(requestId)
23
+
18
24
  /**
19
25
  * CORS requests with `mode: "no-cors"` result in "opaque" responses.
20
26
  * That kind of responses cannot be manipulated in JavaScript due
@@ -46,11 +52,7 @@ export function createResponseListener(context: SetupWorkerInternalContext) {
46
52
  responseJson.isMockedResponse ? 'response:mocked' : 'response:bypass',
47
53
  {
48
54
  response,
49
- /**
50
- * @todo @fixme In this context, we don't know anything about
51
- * the request.
52
- */
53
- request: null as any,
55
+ request,
54
56
  requestId: responseJson.requestId,
55
57
  },
56
58
  )
@@ -17,7 +17,7 @@ import {
17
17
  parseGraphQLRequest,
18
18
  parseDocumentNode,
19
19
  } from '../utils/internal/parseGraphQLRequest'
20
- import { getPublicUrlFromRequest } from '../utils/request/getPublicUrlFromRequest'
20
+ import { toPublicUrl } from '../utils/request/toPublicUrl'
21
21
  import { devUtils } from '../utils/internal/devUtils'
22
22
  import { getAllRequestCookies } from '../utils/request/getRequestCookies'
23
23
 
@@ -200,7 +200,7 @@ export class GraphQLHandler extends RequestHandler<
200
200
  }
201
201
 
202
202
  if (!args.parsedResult.operationName && this.info.operationType !== 'all') {
203
- const publicUrl = getPublicUrlFromRequest(args.request)
203
+ const publicUrl = toPublicUrl(args.request.url)
204
204
 
205
205
  devUtils.warn(`\
206
206
  Failed to intercept a GraphQL request at "${args.request.method} ${publicUrl}": anonymous GraphQL operations are not supported.
@@ -11,7 +11,7 @@ import {
11
11
  Path,
12
12
  PathParams,
13
13
  } from '../utils/matching/matchRequestUrl'
14
- import { getPublicUrlFromRequest } from '../utils/request/getPublicUrlFromRequest'
14
+ import { toPublicUrl } from '../utils/request/toPublicUrl'
15
15
  import { getAllRequestCookies } from '../utils/request/getRequestCookies'
16
16
  import { cleanUrl, getSearchParams } from '../utils/url/cleanUrl'
17
17
  import {
@@ -147,7 +147,7 @@ export class HttpHandler extends RequestHandler<
147
147
  }
148
148
 
149
149
  async log(args: { request: Request; response: Response }) {
150
- const publicUrl = getPublicUrlFromRequest(args.request)
150
+ const publicUrl = toPublicUrl(args.request.url)
151
151
  const loggedRequest = await serializeRequest(args.request)
152
152
  const loggedResponse = await serializeResponse(args.response)
153
153
  const statusColor = getStatusCodeColor(loggedResponse.status)
@@ -82,7 +82,7 @@ export async function handleRequest(
82
82
  // If the handler lookup returned nothing, no request handler was found
83
83
  // matching this request. Report the request as unhandled.
84
84
  if (!lookupResult.data) {
85
- await onUnhandledRequest(request, handlers, options.onUnhandledRequest)
85
+ await onUnhandledRequest(request, options.onUnhandledRequest)
86
86
  emitter.emit('request:unhandled', { request, requestId })
87
87
  emitter.emit('request:end', { request, requestId })
88
88
  handleRequestOptions?.onPassthroughResponse?.(request)
@@ -5,7 +5,7 @@ import type {
5
5
  } from 'graphql'
6
6
  import { parse } from 'graphql'
7
7
  import type { GraphQLVariables } from '../../handlers/GraphQLHandler'
8
- import { getPublicUrlFromRequest } from '../request/getPublicUrlFromRequest'
8
+ import { toPublicUrl } from '../request/toPublicUrl'
9
9
  import { devUtils } from './devUtils'
10
10
  import { jsonParse } from './jsonParse'
11
11
  import { parseMultipartData } from './parseMultipartData'
@@ -184,7 +184,7 @@ export async function parseGraphQLRequest(
184
184
  const parsedResult = parseQuery(query)
185
185
 
186
186
  if (parsedResult instanceof Error) {
187
- const requestPublicUrl = getPublicUrlFromRequest(request)
187
+ const requestPublicUrl = toPublicUrl(request.url)
188
188
 
189
189
  throw new Error(
190
190
  devUtils.formatMessage(
@@ -5,10 +5,6 @@ import {
5
5
  onUnhandledRequest,
6
6
  UnhandledRequestCallback,
7
7
  } from './onUnhandledRequest'
8
- import { HttpHandler, HttpMethods } from '../../handlers/HttpHandler'
9
- import { ResponseResolver } from '../../handlers/RequestHandler'
10
-
11
- const resolver: ResponseResolver = () => void 0
12
8
 
13
9
  const fixtures = {
14
10
  warningWithoutSuggestions: `\
@@ -24,18 +20,6 @@ Read more: https://mswjs.io/docs/getting-started/mocks`,
24
20
 
25
21
  • GET /api
26
22
 
27
- If you still wish to intercept this unhandled request, please create a request handler for it.
28
- Read more: https://mswjs.io/docs/getting-started/mocks`,
29
-
30
- warningWithSuggestions: (suggestions: string) => `\
31
- [MSW] Warning: intercepted a request without a matching request handler:
32
-
33
- • GET /api
34
-
35
- Did you mean to request one of the following resources instead?
36
-
37
- ${suggestions}
38
-
39
23
  If you still wish to intercept this unhandled request, please create a request handler for it.
40
24
  Read more: https://mswjs.io/docs/getting-started/mocks`,
41
25
  }
@@ -52,7 +36,6 @@ afterEach(() => {
52
36
  test('supports the "bypass" request strategy', async () => {
53
37
  await onUnhandledRequest(
54
38
  new Request(new URL('http://localhost/api')),
55
- [],
56
39
  'bypass',
57
40
  )
58
41
 
@@ -61,22 +44,14 @@ test('supports the "bypass" request strategy', async () => {
61
44
  })
62
45
 
63
46
  test('supports the "warn" request strategy', async () => {
64
- await onUnhandledRequest(
65
- new Request(new URL('http://localhost/api')),
66
- [],
67
- 'warn',
68
- )
47
+ await onUnhandledRequest(new Request(new URL('http://localhost/api')), 'warn')
69
48
 
70
49
  expect(console.warn).toHaveBeenCalledWith(fixtures.warningWithoutSuggestions)
71
50
  })
72
51
 
73
52
  test('supports the "error" request strategy', async () => {
74
53
  await expect(
75
- onUnhandledRequest(
76
- new Request(new URL('http://localhost/api')),
77
- [],
78
- 'error',
79
- ),
54
+ onUnhandledRequest(new Request(new URL('http://localhost/api')), 'error'),
80
55
  ).rejects.toThrow(
81
56
  '[MSW] Cannot bypass a request when using the "error" strategy for the "onUnhandledRequest" option.',
82
57
  )
@@ -89,7 +64,7 @@ test('supports a custom callback function', async () => {
89
64
  console.warn(`callback: ${request.method} ${request.url}`)
90
65
  })
91
66
  const request = new Request(new URL('/user', 'http://localhost:3000'))
92
- await onUnhandledRequest(request, [], callback)
67
+ await onUnhandledRequest(request, callback)
93
68
 
94
69
  expect(callback).toHaveBeenCalledTimes(1)
95
70
  expect(callback).toHaveBeenCalledWith(request, {
@@ -111,7 +86,7 @@ test('supports calling default strategies from the custom callback function', as
111
86
  },
112
87
  )
113
88
  const request = new Request(new URL('http://localhost/api'))
114
- await expect(onUnhandledRequest(request, [], callback)).rejects.toThrow(
89
+ await expect(onUnhandledRequest(request, callback)).rejects.toThrow(
115
90
  `[MSW] Cannot bypass a request when using the "error" strategy for the "onUnhandledRequest" option.`,
116
91
  )
117
92
 
@@ -126,86 +101,15 @@ test('supports calling default strategies from the custom callback function', as
126
101
  })
127
102
 
128
103
  test('does not print any suggestions given no handlers to suggest', async () => {
129
- await onUnhandledRequest(
130
- new Request(new URL('http://localhost/api')),
131
- [],
132
- 'warn',
133
- )
134
-
135
- expect(console.warn).toHaveBeenCalledWith(fixtures.warningWithoutSuggestions)
136
- })
137
-
138
- test('does not print any suggestions given no handlers are similar', async () => {
139
- await onUnhandledRequest(
140
- new Request(new URL('http://localhost/api')),
141
- [
142
- // None of the defined request handlers match the actual request URL
143
- // to be used as suggestions.
144
- new HttpHandler(HttpMethods.GET, 'https://api.github.com', resolver),
145
- new HttpHandler(HttpMethods.GET, 'https://api.stripe.com', resolver),
146
- ],
147
- 'warn',
148
- )
104
+ await onUnhandledRequest(new Request(new URL('http://localhost/api')), 'warn')
149
105
 
150
106
  expect(console.warn).toHaveBeenCalledWith(fixtures.warningWithoutSuggestions)
151
107
  })
152
108
 
153
- test('respects RegExp as a request handler method', async () => {
154
- await onUnhandledRequest(
155
- new Request(new URL('http://localhost/api')),
156
- [new HttpHandler(/^GE/, 'http://localhost/api', resolver)],
157
- 'warn',
158
- )
159
-
160
- expect(console.warn).toHaveBeenCalledWith(fixtures.warningWithoutSuggestions)
161
- })
162
-
163
- test('sorts the suggestions by relevance', async () => {
164
- await onUnhandledRequest(
165
- new Request(new URL('http://localhost/api')),
166
- [
167
- new HttpHandler(HttpMethods.GET, '/', resolver),
168
- new HttpHandler(HttpMethods.GET, 'https://api.example.com/api', resolver),
169
- new HttpHandler(HttpMethods.POST, '/api', resolver),
170
- ],
171
- 'warn',
172
- )
173
-
174
- expect(console.warn).toHaveBeenCalledWith(
175
- fixtures.warningWithSuggestions(`\
176
- • POST /api
177
- • GET /`),
178
- )
179
- })
180
-
181
- test('does not print more than 4 suggestions', async () => {
182
- await onUnhandledRequest(
183
- new Request(new URL('http://localhost/api')),
184
- [
185
- new HttpHandler(HttpMethods.GET, '/ap', resolver),
186
- new HttpHandler(HttpMethods.GET, '/api', resolver),
187
- new HttpHandler(HttpMethods.GET, '/api-1', resolver),
188
- new HttpHandler(HttpMethods.GET, '/api-2', resolver),
189
- new HttpHandler(HttpMethods.GET, '/api-3', resolver),
190
- new HttpHandler(HttpMethods.GET, '/api-4', resolver),
191
- ],
192
- 'warn',
193
- )
194
-
195
- expect(console.warn).toHaveBeenCalledWith(
196
- fixtures.warningWithSuggestions(`\
197
- • GET /api
198
- • GET /ap
199
- • GET /api-1
200
- • GET /api-2`),
201
- )
202
- })
203
-
204
109
  test('throws an exception given unknown request strategy', async () => {
205
110
  await expect(
206
111
  onUnhandledRequest(
207
112
  new Request(new URL('http://localhost/api')),
208
- [],
209
113
  // @ts-expect-error Intentional unknown strategy.
210
114
  'invalid-strategy',
211
115
  ),
@@ -1,22 +1,6 @@
1
- import jsLevenshtein from '@bundled-es-modules/js-levenshtein'
2
- import { RequestHandler } from '../../handlers/RequestHandler'
3
- import { HttpHandler } from '../../handlers/HttpHandler'
4
- import { GraphQLHandler } from '../../handlers/GraphQLHandler'
5
- import {
6
- ParsedGraphQLQuery,
7
- ParsedGraphQLRequest,
8
- parseGraphQLRequest,
9
- } from '../internal/parseGraphQLRequest'
10
- import { getPublicUrlFromRequest } from './getPublicUrlFromRequest'
11
- import { isStringEqual } from '../internal/isStringEqual'
1
+ import { toPublicUrl } from './toPublicUrl'
12
2
  import { devUtils } from '../internal/devUtils'
13
3
 
14
- const getStringMatchScore = jsLevenshtein
15
-
16
- const MAX_MATCH_SCORE = 3
17
- const MAX_SUGGESTION_COUNT = 4
18
- const TYPE_MATCH_DELTA = 0.5
19
-
20
4
  export interface UnhandledRequestPrint {
21
5
  warning(): void
22
6
  error(): void
@@ -33,183 +17,20 @@ export type UnhandledRequestStrategy =
33
17
  | 'error'
34
18
  | UnhandledRequestCallback
35
19
 
36
- interface RequestHandlerGroups {
37
- http: Array<HttpHandler>
38
- graphql: Array<GraphQLHandler>
39
- }
40
-
41
- function groupHandlersByType(
42
- handlers: Array<RequestHandler>,
43
- ): RequestHandlerGroups {
44
- return handlers.reduce<RequestHandlerGroups>(
45
- (groups, handler) => {
46
- if (handler instanceof HttpHandler) {
47
- groups.http.push(handler)
48
- }
49
-
50
- if (handler instanceof GraphQLHandler) {
51
- groups.graphql.push(handler)
52
- }
53
-
54
- return groups
55
- },
56
- {
57
- http: [],
58
- graphql: [],
59
- },
60
- )
61
- }
62
-
63
- type RequestHandlerSuggestion = [number, RequestHandler]
64
-
65
- type ScoreGetterFn<RequestHandlerType extends RequestHandler> = (
66
- request: Request,
67
- handler: RequestHandlerType,
68
- ) => number
69
-
70
- function getHttpHandlerScore(): ScoreGetterFn<HttpHandler> {
71
- return (request, handler) => {
72
- const { path, method } = handler.info
73
-
74
- if (path instanceof RegExp || method instanceof RegExp) {
75
- return Infinity
76
- }
77
-
78
- const hasSameMethod = isStringEqual(request.method, method)
79
-
80
- // Always treat a handler with the same method as a more similar one.
81
- const methodScoreDelta = hasSameMethod ? TYPE_MATCH_DELTA : 0
82
- const requestPublicUrl = getPublicUrlFromRequest(request)
83
- const score = getStringMatchScore(requestPublicUrl, path)
84
-
85
- return score - methodScoreDelta
86
- }
87
- }
88
-
89
- function getGraphQLHandlerScore(
90
- parsedQuery: ParsedGraphQLQuery,
91
- ): ScoreGetterFn<GraphQLHandler> {
92
- return (_, handler) => {
93
- if (typeof parsedQuery.operationName === 'undefined') {
94
- return Infinity
95
- }
96
-
97
- const { operationType, operationName } = handler.info
98
-
99
- if (typeof operationName !== 'string') {
100
- return Infinity
101
- }
102
-
103
- const hasSameOperationType = parsedQuery.operationType === operationType
104
- // Always treat a handler with the same operation type as a more similar one.
105
- const operationTypeScoreDelta = hasSameOperationType ? TYPE_MATCH_DELTA : 0
106
- const score = getStringMatchScore(parsedQuery.operationName, operationName)
107
-
108
- return score - operationTypeScoreDelta
109
- }
110
- }
111
-
112
- function getSuggestedHandler(
113
- request: Request,
114
- handlers: Array<HttpHandler> | Array<GraphQLHandler>,
115
- getScore: ScoreGetterFn<HttpHandler> | ScoreGetterFn<GraphQLHandler>,
116
- ): Array<RequestHandler> {
117
- const suggestedHandlers = (handlers as Array<RequestHandler>)
118
- .reduce<Array<RequestHandlerSuggestion>>((suggestions, handler) => {
119
- const score = getScore(request, handler as any)
120
- return suggestions.concat([[score, handler]])
121
- }, [])
122
- .sort(([leftScore], [rightScore]) => leftScore - rightScore)
123
- .filter(([score]) => score <= MAX_MATCH_SCORE)
124
- .slice(0, MAX_SUGGESTION_COUNT)
125
- .map(([, handler]) => handler)
126
-
127
- return suggestedHandlers
128
- }
129
-
130
- function getSuggestedHandlersMessage(handlers: RequestHandler[]) {
131
- if (handlers.length > 1) {
132
- return `\
133
- Did you mean to request one of the following resources instead?
134
-
135
- ${handlers.map((handler) => ` • ${handler.info.header}`).join('\n')}`
136
- }
137
-
138
- return `Did you mean to request "${handlers[0].info.header}" instead?`
139
- }
140
-
141
20
  export async function onUnhandledRequest(
142
21
  request: Request,
143
- handlers: Array<RequestHandler>,
144
22
  strategy: UnhandledRequestStrategy = 'warn',
145
23
  ): Promise<void> {
146
- const parsedGraphQLQuery = await parseGraphQLRequest(request).catch(
147
- () => null,
148
- )
149
- const publicUrl = getPublicUrlFromRequest(request)
150
-
151
- function generateHandlerSuggestion(): string {
152
- /**
153
- * @note Ignore exceptions during GraphQL request parsing because at this point
154
- * we cannot assume the unhandled request is a valid GraphQL request.
155
- * If the GraphQL parsing fails, just don't treat it as a GraphQL request.
156
- */
157
- const handlerGroups = groupHandlersByType(handlers)
158
- const relevantHandlers = parsedGraphQLQuery
159
- ? handlerGroups.graphql
160
- : handlerGroups.http
24
+ const url = new URL(request.url)
25
+ const publicUrl = toPublicUrl(url)
161
26
 
162
- const suggestedHandlers = getSuggestedHandler(
163
- request,
164
- relevantHandlers,
165
- parsedGraphQLQuery
166
- ? getGraphQLHandlerScore(parsedGraphQLQuery)
167
- : getHttpHandlerScore(),
168
- )
169
-
170
- return suggestedHandlers.length > 0
171
- ? getSuggestedHandlersMessage(suggestedHandlers)
172
- : ''
173
- }
174
-
175
- function getGraphQLRequestHeader(
176
- parsedGraphQLRequest: ParsedGraphQLRequest<any>,
177
- ): string {
178
- if (!parsedGraphQLRequest?.operationName) {
179
- return `anonymous ${parsedGraphQLRequest?.operationType} (${request.method} ${publicUrl})`
180
- }
181
-
182
- return `${parsedGraphQLRequest.operationType} ${parsedGraphQLRequest.operationName} (${request.method} ${publicUrl})`
183
- }
184
-
185
- function generateUnhandledRequestMessage(): string {
186
- const requestHeader = parsedGraphQLQuery
187
- ? getGraphQLRequestHeader(parsedGraphQLQuery)
188
- : `${request.method} ${publicUrl}`
189
- const handlerSuggestion = generateHandlerSuggestion()
190
-
191
- const messageTemplate = [
192
- `intercepted a request without a matching request handler:`,
193
- ` \u2022 ${requestHeader}`,
194
- handlerSuggestion,
195
- `\
196
- If you still wish to intercept this unhandled request, please create a request handler for it.
197
- Read more: https://mswjs.io/docs/getting-started/mocks\
198
- `,
199
- ].filter(Boolean)
200
- return messageTemplate.join('\n\n')
201
- }
27
+ const unhandledRequestMessage = `intercepted a request without a matching request handler:\n\n \u2022 ${request.method} ${publicUrl}\n\nIf you still wish to intercept this unhandled request, please create a request handler for it.\nRead more: https://mswjs.io/docs/getting-started/mocks`
202
28
 
203
29
  function applyStrategy(strategy: UnhandledRequestStrategy) {
204
- // Generate handler suggestions only when applying the strategy.
205
- // This saves bandwidth for scenarios when developers opt-out
206
- // from the default unhandled request handling strategy.
207
- const message = generateUnhandledRequestMessage()
208
-
209
30
  switch (strategy) {
210
31
  case 'error': {
211
32
  // Print a developer-friendly error.
212
- devUtils.error('Error: %s', message)
33
+ devUtils.error('Error: %s', unhandledRequestMessage)
213
34
 
214
35
  // Throw an exception to halt request processing and not perform the original request.
215
36
  throw new Error(
@@ -220,7 +41,7 @@ Read more: https://mswjs.io/docs/getting-started/mocks\
220
41
  }
221
42
 
222
43
  case 'warn': {
223
- devUtils.warn('Warning: %s', message)
44
+ devUtils.warn('Warning: %s', unhandledRequestMessage)
224
45
  break
225
46
  }
226
47
 
@@ -245,5 +66,15 @@ Read more: https://mswjs.io/docs/getting-started/mocks\
245
66
  return
246
67
  }
247
68
 
69
+ /**
70
+ * @note Ignore "file://" requests.
71
+ * Those often are an implementation detail of modern tooling
72
+ * that fetches modules via HTTP. Developers don't issue those
73
+ * requests and so they mustn't be warned about them.
74
+ */
75
+ if (url.protocol === 'file:') {
76
+ return
77
+ }
78
+
248
79
  applyStrategy(strategy)
249
80
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @vitest-environment jsdom
3
+ */
4
+ import { toPublicUrl } from './toPublicUrl'
5
+
6
+ test('returns an absolute request URL withouth search params', () => {
7
+ expect(toPublicUrl(new URL('https://test.mswjs.io/path'))).toBe(
8
+ 'https://test.mswjs.io/path',
9
+ )
10
+
11
+ expect(toPublicUrl(new URL('http://localhost/path'))).toBe('/path')
12
+
13
+ expect(toPublicUrl(new URL('http://localhost/path?foo=bar'))).toBe('/path')
14
+ })
15
+
16
+ it('returns a relative URL given the request to the same origin', () => {
17
+ expect(toPublicUrl('http://localhost/user')).toBe('/user')
18
+ })
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Returns a relative URL if the given request URL is relative
3
+ * to the current origin. Otherwise returns an absolute URL.
4
+ */
5
+ export function toPublicUrl(url: string | URL): string {
6
+ if (typeof location === 'undefined') {
7
+ return url.toString()
8
+ }
9
+
10
+ const urlInstance = url instanceof URL ? url : new URL(url)
11
+
12
+ return urlInstance.origin === location.origin
13
+ ? urlInstance.pathname
14
+ : urlInstance.origin + urlInstance.pathname
15
+ }
@@ -1,7 +0,0 @@
1
- /**
2
- * Returns a relative URL if the given request URL is relative to the current origin.
3
- * Otherwise returns an absolute URL.
4
- */
5
- declare function getPublicUrlFromRequest(request: Request): string;
6
-
7
- export { getPublicUrlFromRequest };
@@ -1,7 +0,0 @@
1
- /**
2
- * Returns a relative URL if the given request URL is relative to the current origin.
3
- * Otherwise returns an absolute URL.
4
- */
5
- declare function getPublicUrlFromRequest(request: Request): string;
6
-
7
- export { getPublicUrlFromRequest };
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/core/utils/request/getPublicUrlFromRequest.ts"],"sourcesContent":["/**\n * Returns a relative URL if the given request URL is relative to the current origin.\n * Otherwise returns an absolute URL.\n */\nexport function getPublicUrlFromRequest(request: Request): string {\n if (typeof location === 'undefined') {\n return request.url\n }\n\n const url = new URL(request.url)\n\n return url.origin === location.origin\n ? url.pathname\n : url.origin + url.pathname\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,SAAS,wBAAwB,SAA0B;AAChE,MAAI,OAAO,aAAa,aAAa;AACnC,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAE/B,SAAO,IAAI,WAAW,SAAS,SAC3B,IAAI,WACJ,IAAI,SAAS,IAAI;AACvB;","names":[]}
@@ -1,11 +0,0 @@
1
- function getPublicUrlFromRequest(request) {
2
- if (typeof location === "undefined") {
3
- return request.url;
4
- }
5
- const url = new URL(request.url);
6
- return url.origin === location.origin ? url.pathname : url.origin + url.pathname;
7
- }
8
- export {
9
- getPublicUrlFromRequest
10
- };
11
- //# sourceMappingURL=getPublicUrlFromRequest.mjs.map