nock 15.0.0-beta.12 → 15.0.0-beta.14

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.
@@ -467,6 +467,8 @@ const expand = (input ) => {
467
467
  } else {
468
468
  resultPtr[part] = {}
469
469
  }
470
+ } else if (typeof resultPtr[part] !== 'object') {
471
+ return undefined
470
472
  }
471
473
  resultPtr = resultPtr[part]
472
474
  }
@@ -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
@@ -235,25 +231,6 @@ function activeMocks() {
235
231
  function activate() {
236
232
  if (!_isActive) {
237
233
  builtinMock.activate()
238
-
239
- if (!undiciMock) {
240
- try {
241
- undiciMock = _require('./interceptors/undici.js')
242
- } catch (err ) {
243
- if (
244
- err.code !== 'MODULE_NOT_FOUND' &&
245
- err.code !== 'ERR_MODULE_NOT_FOUND' &&
246
- err.code !== 'ERR_REQUIRE_ESM'
247
- ) {
248
- throw err
249
- }
250
- debug(
251
- 'Undici mocking is disabled because the undici module is not installed',
252
- )
253
- }
254
- }
255
-
256
- undiciMock?.activate()
257
234
  _isActive = true
258
235
  } else {
259
236
  throw new Error('Nock already active')
@@ -263,7 +240,6 @@ function activate() {
263
240
  function deactivate() {
264
241
  if (_isActive) {
265
242
  builtinMock.deactivate()
266
- undiciMock?.deactivate()
267
243
  _isActive = false
268
244
  }
269
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.12",
10
+ "version": "15.0.0-beta.14",
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 }