nock 15.0.0-beta.11 → 15.0.0-beta.13

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.
@@ -3,10 +3,6 @@
3
3
  import * as common from './common.js'
4
4
  import { intercept as debug } from './debug.js'
5
5
  import * as builtinMock from './interceptors/builtin.js'
6
- import { createRequire } from 'node:module'
7
-
8
- const _require = createRequire(import.meta.url)
9
- let undiciMock
10
6
 
11
7
  let allInterceptors = {}
12
8
  let allowNetConnect
@@ -190,6 +186,10 @@ function removeInterceptor(
190
186
  ) {
191
187
  allInterceptors[baseUrl].interceptors.splice(i, 1)
192
188
  interceptor.scope.remove(key, interceptor)
189
+ const scopeIndex = interceptor.scope.interceptors.indexOf(interceptor)
190
+ if (scopeIndex !== -1) {
191
+ interceptor.scope.interceptors.splice(scopeIndex, 1)
192
+ }
193
193
  break
194
194
  }
195
195
  }
@@ -231,25 +231,6 @@ function activeMocks() {
231
231
  function activate() {
232
232
  if (!_isActive) {
233
233
  builtinMock.activate()
234
-
235
- if (!undiciMock) {
236
- try {
237
- undiciMock = _require('./interceptors/undici.js')
238
- } catch (err ) {
239
- if (
240
- err.code !== 'MODULE_NOT_FOUND' &&
241
- err.code !== 'ERR_MODULE_NOT_FOUND' &&
242
- err.code !== 'ERR_REQUIRE_ESM'
243
- ) {
244
- throw err
245
- }
246
- debug(
247
- 'Undici mocking is disabled because the undici module is not installed',
248
- )
249
- }
250
- }
251
-
252
- undiciMock?.activate()
253
234
  _isActive = true
254
235
  } else {
255
236
  throw new Error('Nock already active')
@@ -259,7 +240,6 @@ function activate() {
259
240
  function deactivate() {
260
241
  if (_isActive) {
261
242
  builtinMock.deactivate()
262
- undiciMock?.deactivate()
263
243
  _isActive = false
264
244
  }
265
245
  }
@@ -1,15 +1,13 @@
1
- import http from 'node:http'
2
- import { getRawRequest, BatchInterceptor } from '@mswjs/interceptors'
3
- import nodeInterceptors from '@mswjs/interceptors/presets/node'
4
1
  import * as common from '../common.js'
5
2
  import handleRequest from '../handle-request.js'
6
- import { arrayBuffer } from 'node:stream/consumers'
7
- import { getClientRequestBodyStream } from '@mswjs/interceptors/utils/node'
8
3
  import { setGetRequestBody } from '../utils/node/index.js'
4
+ import { BatchInterceptor } from '@mswjs/interceptors'
5
+ import { FetchInterceptor } from '@mswjs/interceptors/fetch'
6
+ import { HttpRequestInterceptor } from '@mswjs/interceptors/http'
9
7
 
10
8
  const interceptor = new BatchInterceptor({
11
- name: 'nock-interceptor',
12
- interceptors: nodeInterceptors,
9
+ name: 'intercept',
10
+ interceptors: [new HttpRequestInterceptor(), new FetchInterceptor()],
13
11
  })
14
12
 
15
13
  function activate() {
@@ -19,38 +17,36 @@ function activate() {
19
17
  interceptor.on('unhandledException', ({ controller, error } ) => {
20
18
  controller.errorWith(error)
21
19
  })
22
- interceptor.on('request', async function ({ request, controller } ) {
23
- if (request.headers.get('expect') === '100-continue') {
24
- // We currently do not support mocking 100-continue responses, so they are passed through for now.
25
- return
26
- }
27
- const rawRequest = getRawRequest(request)
28
-
29
- // If this is GET request with body, we need to read the body from the socket because Fetch API doesn't support this.
30
- const requestBodyBuffer = common.decompressRequestBody(
31
- rawRequest instanceof http.ClientRequest &&
32
- request.method === 'GET' &&
33
- Number(request.headers.get('content-length')) > 0
34
- ? await arrayBuffer(getClientRequestBodyStream(request))
35
- : await request.clone().arrayBuffer(),
36
- request.headers.get('content-encoding') || '',
37
- )
38
-
39
- const decompressedRequest = new Request(request, {
40
- body:
41
- requestBodyBuffer.byteLength > 0 && request.method !== 'GET'
42
- ? requestBodyBuffer
43
- : undefined,
44
- })
45
- if (requestBodyBuffer.byteLength > 0 && request.method === 'GET') {
46
- setGetRequestBody(decompressedRequest, requestBodyBuffer)
47
- }
48
-
49
- const response = await handleRequest(decompressedRequest)
50
- if (response) {
51
- controller.respondWith(response)
52
- }
53
- })
20
+ interceptor.on(
21
+ 'request',
22
+ async function ({ request, controller, initiator }) {
23
+ if (request.headers.get('expect') === '100-continue') {
24
+ // We currently do not support mocking 100-continue responses, so they are passed through for now.
25
+ return
26
+ }
27
+
28
+ // If this is GET request with body, we need to read the body from the socket because Fetch API doesn't support this.
29
+ const requestBodyBuffer = common.decompressRequestBody(
30
+ await request.clone().arrayBuffer(),
31
+ request.headers.get('content-encoding') || '',
32
+ )
33
+
34
+ const decompressedRequest = new Request(request, {
35
+ body:
36
+ requestBodyBuffer.byteLength > 0 && request.method !== 'GET'
37
+ ? requestBodyBuffer
38
+ : undefined,
39
+ })
40
+ if (requestBodyBuffer.byteLength > 0 && request.method === 'GET') {
41
+ setGetRequestBody(decompressedRequest, requestBodyBuffer)
42
+ }
43
+
44
+ const response = await handleRequest(decompressedRequest)
45
+ if (response) {
46
+ controller.respondWith(response)
47
+ }
48
+ },
49
+ )
54
50
  }
55
51
 
56
52
  function deactivate() {
@@ -14,15 +14,18 @@ import { inspect } from 'node:util'
14
14
 
15
15
  import * as common from './common.js'
16
16
  import { restoreOverriddenClientRequest } from './intercept.js'
17
- import { gzipSync, brotliCompressSync, deflateSync } from 'node:zlib'
18
- import nodeInterceptors from '@mswjs/interceptors/presets/node'
17
+ import { BatchInterceptor } from '@mswjs/interceptors'
18
+ import { ClientRequestInterceptor } from '@mswjs/interceptors/ClientRequest'
19
+ import { FetchInterceptor } from '@mswjs/interceptors/fetch'
20
+
19
21
  const SEPARATOR = '\n<<<<<<-- cut here -->>>>>>\n'
20
22
  let recordingInProgress = false
21
23
  let _outputs = []
22
24
 
23
- // TODO: don't reuse the nodeInterceptors, create new ones.
24
- const clientRequestInterceptor = nodeInterceptors[0]
25
- const fetchRequestInterceptor = nodeInterceptors[2]
25
+ const interceptor = new BatchInterceptor({
26
+ name: 'intercept',
27
+ interceptors: [new ClientRequestInterceptor(), new FetchInterceptor()],
28
+ })
26
29
 
27
30
  function getScope(url ) {
28
31
  return common.normalizeOrigin(url)
@@ -219,39 +222,10 @@ function record(recOptions ) {
219
222
  restoreOverriddenClientRequest()
220
223
 
221
224
  // We override the requests so that we can save information on them before executing.
222
- clientRequestInterceptor.apply()
223
- fetchRequestInterceptor.apply()
224
- clientRequestInterceptor.on(
225
- 'response',
226
- async function ({ request, response } ) {
227
- await recordResponse(request, response)
228
- },
229
- )
230
- fetchRequestInterceptor.on(
231
- 'response',
232
- async function ({ request, response } ) {
233
- // fetch decompresses the body automatically, so we need to recompress it
234
- const codings =
235
- response.headers
236
- .get('content-encoding')
237
- ?.toLowerCase()
238
- .split(',')
239
- .map((c ) => c.trim()) || []
240
-
241
- let body = await response.arrayBuffer()
242
- for (const coding of codings) {
243
- if (coding === 'gzip') {
244
- body = gzipSync(body)
245
- } else if (coding === 'deflate') {
246
- body = deflateSync(body)
247
- } else if (coding === 'br') {
248
- body = brotliCompressSync(body)
249
- }
250
- }
251
-
252
- await recordResponse(request, new Response(body, response))
253
- },
254
- )
225
+ interceptor.apply()
226
+ interceptor.on('response', async function ({ request, response }) {
227
+ await recordResponse(request, response)
228
+ })
255
229
 
256
230
  async function recordResponse(mswRequest , mswResponse ) {
257
231
  const request = mswRequest.clone()
@@ -319,8 +293,7 @@ function restore() {
319
293
  'restoring all the overridden http/https properties',
320
294
  )
321
295
 
322
- clientRequestInterceptor.dispose()
323
- fetchRequestInterceptor.dispose()
296
+ interceptor.dispose()
324
297
  restoreOverriddenClientRequest()
325
298
  recordingInProgress = false
326
299
  }
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "testing",
8
8
  "isolation"
9
9
  ],
10
- "version": "15.0.0-beta.11",
10
+ "version": "15.0.0-beta.13",
11
11
  "author": "Pedro Teixeira <pedro.teixeira@gmail.com>",
12
12
  "repository": {
13
13
  "type": "git",
@@ -29,7 +29,7 @@
29
29
  }
30
30
  },
31
31
  "dependencies": {
32
- "@mswjs/interceptors": "^0.41.2"
32
+ "@mswjs/interceptors": "^0.42.1"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@eslint/js": "^9.27.0",
@@ -1,3 +0,0 @@
1
- declare function activate(): void;
2
- declare function deactivate(): void;
3
- export { activate, deactivate };
@@ -1,81 +0,0 @@
1
- // This file is loaded lazily after confirming undici is installed
2
-
3
- import undici from 'undici'
4
- import handleRequest from '../handle-request.js'
5
- import { URL } from 'node:url'
6
- import { convertHeadersToRaw } from '../common.js'
7
-
8
- class NockClient extends undici.Client {
9
- constructor(origin , options ) {
10
- super(origin, options)
11
- }
12
-
13
- dispatch(options , handler ) {
14
- const url = new URL(options.path, options.origin)
15
- if (options.query) {
16
- url.search = new URLSearchParams(options.query).toString()
17
- }
18
-
19
- const decompressedRequest = new Request(url, {
20
- method: options.method,
21
- headers: options.headers,
22
- body: options.body,
23
- duplex: options.body ? 'half' : undefined,
24
- })
25
-
26
- handleRequest(decompressedRequest)
27
- .then(async (response ) => {
28
- if (response) {
29
- handler.onConnect?.((err ) => handler.onError(err), null)
30
- handler.onHeaders?.(
31
- response.status,
32
- convertHeadersToRaw(response.headers),
33
- () => {},
34
- response.statusText,
35
- )
36
- handler.onData?.(Buffer.from(await response.arrayBuffer()))
37
- handler.onComplete?.([]) // responseTrailers
38
- } else {
39
- const dispatcher = options.dispatcher || {
40
- dispatch: super.dispatch.bind(this),
41
- }
42
- dispatcher.dispatch(options, handler)
43
- }
44
- })
45
- .catch((err ) => {
46
- handler.onError?.(err)
47
- })
48
- return true
49
- }
50
- }
51
-
52
- class NockAgent extends undici.Dispatcher {
53
-
54
-
55
-
56
- constructor(options ) {
57
- super()
58
-
59
- this.agent = new undici.Agent({ factory: this.factory.bind(this) })
60
- this.originalOptions = options
61
- }
62
-
63
- dispatch(options , handler ) {
64
- return this.agent.dispatch(options, handler)
65
- }
66
-
67
- factory(origin ) {
68
- const mockOptions = { ...this.originalOptions, agent: this }
69
- return new NockClient(origin, mockOptions)
70
- }
71
- }
72
-
73
- function activate() {
74
- undici.setGlobalDispatcher(new NockAgent())
75
- }
76
-
77
- function deactivate() {
78
- undici.setGlobalDispatcher(new undici.Agent())
79
- }
80
-
81
- export { activate, deactivate }