@redocly/cli 1.27.1 → 1.28.0

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.
@@ -4,26 +4,66 @@ import { exitWithError } from '../../utils/miscellaneous';
4
4
  import { getApiRoot, getDestinationProps, handlePush, transformPush } from '../../commands/push';
5
5
  import { ConfigFixture } from '../fixtures/config';
6
6
  import { yellow } from 'colorette';
7
-
8
- jest.mock('fs');
9
- jest.mock('node-fetch', () => ({
10
- default: jest.fn(() => ({
11
- ok: true,
12
- json: jest.fn().mockResolvedValue({}),
13
- })),
7
+ import { Readable } from 'node:stream';
8
+
9
+ // Mock fs operations
10
+ jest.mock('fs', () => ({
11
+ ...jest.requireActual('fs'),
12
+ createReadStream: jest.fn(() => {
13
+ const readable = new Readable();
14
+ readable.push('test data');
15
+ readable.push(null);
16
+ return readable;
17
+ }),
18
+ statSync: jest.fn(() => ({ isDirectory: () => false, size: 10 })),
19
+ readFileSync: jest.fn(() => Buffer.from('test data')),
20
+ existsSync: jest.fn(() => false),
21
+ readdirSync: jest.fn(() => []),
14
22
  }));
23
+
15
24
  jest.mock('@redocly/openapi-core');
16
25
  jest.mock('../../utils/miscellaneous');
17
26
 
27
+ // Mock fetch
28
+ const mockFetch = jest.fn(() =>
29
+ Promise.resolve({
30
+ ok: true,
31
+ status: 200,
32
+ json: () => Promise.resolve({}),
33
+ headers: new Headers(),
34
+ statusText: 'OK',
35
+ redirected: false,
36
+ type: 'default',
37
+ url: '',
38
+ clone: () => ({} as Response),
39
+ body: null,
40
+ bodyUsed: false,
41
+ arrayBuffer: async () => new ArrayBuffer(0),
42
+ blob: async () => new Blob(),
43
+ formData: async () => new FormData(),
44
+ text: async () => '',
45
+ } as Response)
46
+ );
47
+
48
+ const originalFetch = global.fetch;
49
+
18
50
  (getMergedConfig as jest.Mock).mockImplementation((config) => config);
19
51
 
20
52
  describe('push', () => {
21
53
  const redoclyClient = require('@redocly/openapi-core').__redoclyClient;
22
54
 
55
+ beforeAll(() => {
56
+ global.fetch = mockFetch;
57
+ });
58
+
23
59
  beforeEach(() => {
24
60
  jest.spyOn(process.stdout, 'write').mockImplementation(() => true);
25
61
  });
26
62
 
63
+ afterAll(() => {
64
+ global.fetch = originalFetch;
65
+ });
66
+
27
67
  it('pushes definition', async () => {
28
68
  await handlePush({
29
69
  argv: {
@@ -1,12 +1,37 @@
1
1
  import AbortController from 'abort-controller';
2
2
  import fetchWithTimeout from '../utils/fetch-with-timeout';
3
- import nodeFetch from 'node-fetch';
4
3
  import { getProxyAgent } from '@redocly/openapi-core';
5
4
  import { HttpsProxyAgent } from 'https-proxy-agent';
6
5
 
7
- jest.mock('node-fetch');
8
6
  jest.mock('@redocly/openapi-core');
9
7
 
8
+ const signalInstance = new AbortController().signal;
9
+
10
+ const mockFetch = jest.fn(() =>
11
+ Promise.resolve({
12
+ ok: true,
13
+ status: 200,
14
+ json: () => Promise.resolve({}),
15
+ headers: new Headers(),
16
+ statusText: 'OK',
17
+ redirected: false,
18
+ type: 'default',
19
+ url: '',
20
+ clone: () => ({} as Response),
21
+ body: null,
22
+ bodyUsed: false,
23
+ arrayBuffer: async () => new ArrayBuffer(0),
24
+ blob: async () => new Blob(),
25
+ formData: async () => new FormData(),
26
+ text: async () => '',
27
+ signal: signalInstance,
28
+ dispatcher: undefined,
29
+ } as Response)
30
+ );
31
+
32
+ const originalFetch = global.fetch;
33
+ global.fetch = mockFetch;
34
+
10
35
  describe('fetchWithTimeout', () => {
11
36
  beforeAll(() => {
12
37
  // @ts-ignore
@@ -18,36 +43,39 @@ describe('fetchWithTimeout', () => {
18
43
  (getProxyAgent as jest.Mock).mockReturnValueOnce(undefined);
19
44
  });
20
45
 
21
- afterEach(() => {
22
- jest.clearAllMocks();
46
+ afterAll(() => {
47
+ global.fetch = originalFetch;
23
48
  });
24
49
 
25
- it('should call node-fetch with signal', async () => {
50
+ it('should call fetch with signal', async () => {
26
51
  await fetchWithTimeout('url', { timeout: 1000 });
27
52
 
28
53
  expect(global.setTimeout).toHaveBeenCalledTimes(1);
29
- expect(nodeFetch).toHaveBeenCalledWith('url', {
30
- signal: new AbortController().signal,
31
- agent: undefined,
32
- });
54
+ expect(global.fetch).toHaveBeenCalledWith(
55
+ 'url',
56
+ expect.objectContaining({
57
+ signal: expect.any(AbortSignal),
58
+ dispatcher: undefined,
59
+ })
60
+ );
33
61
  expect(global.clearTimeout).toHaveBeenCalledTimes(1);
34
62
  });
35
63
 
36
- it('should call node-fetch with proxy agent', async () => {
64
+ it('should call fetch with proxy agent', async () => {
37
65
  (getProxyAgent as jest.Mock).mockRestore();
38
66
  const proxyAgent = new HttpsProxyAgent('http://localhost');
39
67
  (getProxyAgent as jest.Mock).mockReturnValueOnce(proxyAgent);
40
68
 
41
69
  await fetchWithTimeout('url');
42
70
 
43
- expect(nodeFetch).toHaveBeenCalledWith('url', { agent: proxyAgent });
71
+ expect(global.fetch).toHaveBeenCalledWith('url', { dispatcher: proxyAgent });
44
72
  });
45
73
 
46
- it('should call node-fetch without signal when timeout is not passed', async () => {
74
+ it('should call fetch without signal when timeout is not passed', async () => {
47
75
  await fetchWithTimeout('url');
48
76
 
49
77
  expect(global.setTimeout).not.toHaveBeenCalled();
50
- expect(nodeFetch).toHaveBeenCalledWith('url', { agent: undefined });
78
+ expect(global.fetch).toHaveBeenCalledWith('url', { agent: undefined });
51
79
  expect(global.clearTimeout).not.toHaveBeenCalled();
52
80
  });
53
81
  });
@@ -6,11 +6,23 @@ import { Arguments } from 'yargs';
6
6
  import { handlePush, PushOptions } from '../commands/push';
7
7
  import { detectSpec } from '@redocly/openapi-core';
8
8
 
9
- jest.mock('node-fetch');
9
+ const mockFetch = jest.fn();
10
+ const originalFetch = global.fetch;
11
+
10
12
  jest.mock('../utils/miscellaneous', () => ({
11
13
  sendTelemetry: jest.fn(),
12
14
  loadConfigAndHandleErrors: jest.fn(),
13
15
  }));
16
+
17
+ beforeAll(() => {
18
+ global.fetch = mockFetch;
19
+ });
20
+
21
+ afterAll(() => {
22
+ jest.resetAllMocks();
23
+ global.fetch = originalFetch;
24
+ });
25
+
14
26
  jest.mock('../commands/lint', () => ({
15
27
  handleLint: jest.fn().mockImplementation(({ collectSpecData }) => {
16
28
  collectSpecData({ openapi: '3.1.0' });
@@ -1,15 +1,21 @@
1
- import fetch, { Response } from 'node-fetch';
2
- import * as FormData from 'form-data';
3
1
  import { red, yellow } from 'colorette';
4
2
 
5
3
  import { ReuniteApi, PushPayload, ReuniteApiError } from '../api-client';
6
4
 
7
- jest.mock('node-fetch', () => ({
8
- default: jest.fn(),
9
- }));
5
+ const originalFetch = global.fetch;
6
+
7
+ beforeAll(() => {
8
+ // Reset fetch mock before each test
9
+ global.fetch = jest.fn();
10
+ });
11
+
12
+ afterAll(() => {
13
+ // Restore original fetch after each test
14
+ global.fetch = originalFetch;
15
+ });
10
16
 
11
17
  function mockFetchResponse(response: any) {
12
- (fetch as jest.MockedFunction<typeof fetch>).mockResolvedValue(response as unknown as Response);
18
+ (global.fetch as jest.Mock).mockResolvedValue(response);
13
19
  }
14
20
 
15
21
  describe('ApiClient', () => {
@@ -38,7 +44,7 @@ describe('ApiClient', () => {
38
44
 
39
45
  const result = await apiClient.remotes.getDefaultBranch(testOrg, testProject);
40
46
 
41
- expect(fetch).toHaveBeenCalledWith(
47
+ expect(global.fetch).toHaveBeenCalledWith(
42
48
  `${testDomain}/api/orgs/${testOrg}/projects/${testProject}/source`,
43
49
  {
44
50
  method: 'GET',
@@ -115,7 +121,7 @@ describe('ApiClient', () => {
115
121
 
116
122
  const result = await apiClient.remotes.upsert(testOrg, testProject, remotePayload);
117
123
 
118
- expect(fetch).toHaveBeenCalledWith(
124
+ expect(global.fetch).toHaveBeenCalledWith(
119
125
  `${testDomain}/api/orgs/${testOrg}/projects/${testProject}/remotes`,
120
126
  {
121
127
  method: 'POST',
@@ -213,12 +219,11 @@ describe('ApiClient', () => {
213
219
  });
214
220
 
215
221
  it('should push to remote', async () => {
216
- let passedFormData = new FormData();
222
+ let passedFormData: FormData = new FormData();
217
223
 
218
224
  (fetch as jest.MockedFunction<typeof fetch>).mockImplementationOnce(
219
225
  async (_: any, options: any): Promise<Response> => {
220
226
  passedFormData = options.body as FormData;
221
-
222
227
  return {
223
228
  ok: true,
224
229
  json: jest.fn().mockResolvedValue(responseMock),
@@ -226,14 +231,14 @@ describe('ApiClient', () => {
226
231
  }
227
232
  );
228
233
 
229
- const formData = new FormData();
234
+ const formData = new globalThis.FormData();
230
235
 
231
236
  formData.append('remoteId', testRemoteId);
232
237
  formData.append('commit[message]', pushPayload.commit.message);
233
238
  formData.append('commit[author][name]', pushPayload.commit.author.name);
234
239
  formData.append('commit[author][email]', pushPayload.commit.author.email);
235
240
  formData.append('commit[branchName]', pushPayload.commit.branchName);
236
- formData.append('files[some-file.yaml]', filesMock[0].stream);
241
+ formData.append('files[some-file.yaml]', new Blob([filesMock[0].stream]));
237
242
 
238
243
  const result = await apiClient.remotes.push(testOrg, testProject, pushPayload, filesMock);
239
244
 
@@ -247,10 +252,7 @@ describe('ApiClient', () => {
247
252
  },
248
253
  })
249
254
  );
250
-
251
- expect(
252
- JSON.stringify(passedFormData).replace(new RegExp(passedFormData.getBoundary(), 'g'), '')
253
- ).toEqual(JSON.stringify(formData).replace(new RegExp(formData.getBoundary(), 'g'), ''));
255
+ expect(passedFormData).toEqual(formData);
254
256
  expect(result).toEqual(responseMock);
255
257
  });
256
258
 
@@ -1,12 +1,11 @@
1
1
  import { yellow, red } from 'colorette';
2
- import * as FormData from 'form-data';
3
2
  import fetchWithTimeout, {
4
3
  type FetchWithTimeoutOptions,
5
4
  DEFAULT_FETCH_TIMEOUT,
6
5
  } from '../../utils/fetch-with-timeout';
7
6
 
8
- import type { Response } from 'node-fetch';
9
7
  import type { ReadStream } from 'fs';
8
+ import type { Readable } from 'node:stream';
10
9
  import type {
11
10
  ListRemotesResponse,
12
11
  ProjectSourceResponse,
@@ -178,7 +177,7 @@ class RemotesApi {
178
177
  payload: PushPayload,
179
178
  files: { path: string; stream: ReadStream | Buffer }[]
180
179
  ): Promise<PushResponse> {
181
- const formData = new FormData();
180
+ const formData = new globalThis.FormData();
182
181
 
183
182
  formData.append('remoteId', payload.remoteId);
184
183
  formData.append('commit[message]', payload.commit.message);
@@ -192,7 +191,10 @@ class RemotesApi {
192
191
  payload.commit.createdAt && formData.append('commit[createdAt]', payload.commit.createdAt);
193
192
 
194
193
  for (const file of files) {
195
- formData.append(`files[${file.path}]`, file.stream);
194
+ const blob = Buffer.isBuffer(file.stream)
195
+ ? new Blob([file.stream])
196
+ : new Blob([await streamToBuffer(file.stream)]);
197
+ formData.append(`files[${file.path}]`, blob, file.path);
196
198
  }
197
199
 
198
200
  payload.isMainBranch && formData.append('isMainBranch', 'true');
@@ -369,3 +371,11 @@ export type PushPayload = {
369
371
  };
370
372
  isMainBranch?: boolean;
371
373
  };
374
+
375
+ export async function streamToBuffer(stream: ReadStream | Readable): Promise<Buffer> {
376
+ const chunks: Buffer[] = [];
377
+ for await (const chunk of stream) {
378
+ chunks.push(chunk);
379
+ }
380
+ return Buffer.concat(chunks);
381
+ }
@@ -23,14 +23,17 @@ import { isObject, isString, keysOf } from '../utils/js-utils';
23
23
  import { COMPONENTS, OPENAPI3_METHOD } from './split/types';
24
24
  import { crawl, startsWithComponents } from './split';
25
25
 
26
- import type { Oas3Definition, Document, Oas3Tag, Referenced } from '@redocly/openapi-core';
26
+ import type { Document, Referenced } from '@redocly/openapi-core';
27
27
  import type { BundleResult } from '@redocly/openapi-core/lib/bundle';
28
28
  import type {
29
+ Oas3Definition,
30
+ Oas3_1Definition,
29
31
  Oas3Parameter,
30
32
  Oas3PathItem,
31
33
  Oas3Server,
32
- Oas3_1Definition,
34
+ Oas3Tag,
33
35
  } from '@redocly/openapi-core/lib/typings/openapi';
36
+ import type { StrictObject } from '@redocly/openapi-core/lib/utils';
34
37
  import type { CommandArgs } from '../wrapper';
35
38
  import type { VerifyConfigOptions } from '../types';
36
39
 
@@ -311,7 +314,7 @@ export async function handleJoin({
311
314
  }
312
315
  }
313
316
 
314
- function collectServers(openapi: Oas3Definition) {
317
+ function collectServers(openapi: Oas3Definition | Oas3_1Definition) {
315
318
  const { servers } = openapi;
316
319
  if (servers) {
317
320
  if (!joinedDef.hasOwnProperty('servers')) {
@@ -325,7 +328,10 @@ export async function handleJoin({
325
328
  }
326
329
  }
327
330
 
328
- function collectExternalDocs(openapi: Oas3Definition, { api }: JoinDocumentContext) {
331
+ function collectExternalDocs(
332
+ openapi: Oas3Definition | Oas3_1Definition,
333
+ { api }: JoinDocumentContext
334
+ ) {
329
335
  const { externalDocs } = openapi;
330
336
  if (externalDocs) {
331
337
  if (joinedDef.hasOwnProperty('externalDocs')) {
@@ -339,7 +345,7 @@ export async function handleJoin({
339
345
  }
340
346
 
341
347
  function collectPaths(
342
- openapi: Oas3Definition,
348
+ openapi: Oas3Definition | Oas3_1Definition,
343
349
  {
344
350
  apiFilename,
345
351
  apiTitle,
@@ -567,7 +573,7 @@ export async function handleJoin({
567
573
 
568
574
  function collectWebhooks(
569
575
  oasVersion: SpecVersion,
570
- openapi: Oas3_1Definition,
576
+ openapi: StrictObject<Oas3Definition | Oas3_1Definition>,
571
577
  {
572
578
  apiFilename,
573
579
  apiTitle,
@@ -1,6 +1,5 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
- import fetch from 'node-fetch';
4
3
  import { performance } from 'perf_hooks';
5
4
  import { yellow, green, blue, red } from 'colorette';
6
5
  import { createHash } from 'crypto';
@@ -22,7 +21,10 @@ import {
22
21
  } from '../utils/miscellaneous';
23
22
  import { promptClientToken } from './login';
24
23
  import { handlePush as handleCMSPush } from '../cms/commands/push';
24
+ import { streamToBuffer } from '../cms/api/api-client';
25
25
 
26
+ import type { Readable } from 'node:stream';
27
+ import type { Agent } from 'node:http';
26
28
  import type { Config, BundleOutputFormat, Region } from '@redocly/openapi-core';
27
29
  import type { CommandArgs } from '../wrapper';
28
30
  import type { VerifyConfigOptions } from '../types';
@@ -436,7 +438,7 @@ export function getApiRoot({
436
438
  return api?.root;
437
439
  }
438
440
 
439
- function uploadFileToS3(url: string, filePathOrBuffer: string | Buffer) {
441
+ async function uploadFileToS3(url: string, filePathOrBuffer: string | Buffer) {
440
442
  const fileSizeInBytes =
441
443
  typeof filePathOrBuffer === 'string'
442
444
  ? fs.statSync(filePathOrBuffer).size
@@ -445,12 +447,24 @@ function uploadFileToS3(url: string, filePathOrBuffer: string | Buffer) {
445
447
  const readStream =
446
448
  typeof filePathOrBuffer === 'string' ? fs.createReadStream(filePathOrBuffer) : filePathOrBuffer;
447
449
 
448
- return fetch(url, {
450
+ type NodeFetchRequestInit = RequestInit & {
451
+ dispatcher?: Agent;
452
+ };
453
+
454
+ const requestOptions: NodeFetchRequestInit = {
449
455
  method: 'PUT',
450
456
  headers: {
451
457
  'Content-Length': fileSizeInBytes.toString(),
452
458
  },
453
- body: readStream,
454
- agent: getProxyAgent(),
455
- });
459
+ body: Buffer.isBuffer(readStream)
460
+ ? new Blob([readStream])
461
+ : new Blob([await streamToBuffer(readStream as Readable)]),
462
+ };
463
+
464
+ const proxyAgent = getProxyAgent();
465
+ if (proxyAgent) {
466
+ requestOptions.dispatcher = proxyAgent;
467
+ }
468
+
469
+ return fetch(url, requestOptions);
456
470
  }
@@ -22,20 +22,18 @@ import {
22
22
  OPENAPI3_COMPONENT_NAMES,
23
23
  } from './types';
24
24
 
25
- import type { OasRef } from '@redocly/openapi-core';
25
+ import type { Oas3Definition, Oas3_1Definition, Oas2Definition } from '@redocly/openapi-core';
26
26
  import type {
27
- Definition,
28
- Oas2Definition,
29
27
  Oas3Schema,
30
- Oas3Definition,
31
- Oas3_1Definition,
28
+ Oas3_1Schema,
32
29
  Oas3Components,
30
+ Oas3_1Components,
33
31
  Oas3ComponentName,
34
- ComponentsFiles,
35
- RefObject,
36
32
  Oas3PathItem,
33
+ OasRef,
37
34
  Referenced,
38
- } from './types';
35
+ } from '@redocly/openapi-core/lib/typings/openapi';
36
+ import type { ComponentsFiles, Definition, RefObject } from './types';
39
37
  import type { CommandArgs } from '../../wrapper';
40
38
  import type { VerifyConfigOptions } from '../../types';
41
39
 
@@ -239,7 +237,7 @@ function doesFileDiffer(filename: string, componentData: any) {
239
237
 
240
238
  function removeEmptyComponents(
241
239
  openapi: Oas3Definition | Oas3_1Definition,
242
- componentType: Oas3ComponentName
240
+ componentType: Oas3ComponentName<Oas3Schema | Oas3_1Schema>
243
241
  ) {
244
242
  if (openapi.components && isEmptyObject(openapi.components[componentType])) {
245
243
  delete openapi.components[componentType];
@@ -264,15 +262,17 @@ function getFileNamePath(componentDirPath: string, componentName: string, ext: s
264
262
  }
265
263
 
266
264
  function gatherComponentsFiles(
267
- components: Oas3Components,
265
+ components: Oas3Components | Oas3_1Components,
268
266
  componentsFiles: ComponentsFiles,
269
- componentType: Oas3ComponentName,
267
+ componentType: Oas3ComponentName<Oas3Schema | Oas3_1Schema>,
270
268
  componentName: string,
271
269
  filename: string
272
270
  ) {
273
271
  let inherits: string[] = [];
274
272
  if (componentType === OPENAPI3_COMPONENT.Schemas) {
275
- inherits = ((components?.[componentType]?.[componentName] as Oas3Schema)?.allOf || [])
273
+ inherits = (
274
+ (components?.[componentType]?.[componentName] as Oas3Schema | Oas3_1Schema)?.allOf || []
275
+ )
276
276
  .map(({ $ref }) => $ref)
277
277
  .filter(isTruthy);
278
278
  }
@@ -347,7 +347,9 @@ function iterateComponents(
347
347
  componentTypes.forEach(iterateComponentTypes);
348
348
 
349
349
  // eslint-disable-next-line no-inner-declarations
350
- function iterateAndGatherComponentsFiles(componentType: Oas3ComponentName) {
350
+ function iterateAndGatherComponentsFiles(
351
+ componentType: Oas3ComponentName<Oas3Schema | Oas3_1Schema>
352
+ ) {
351
353
  const componentDirPath = path.join(componentsDir, componentType);
352
354
  for (const componentName of Object.keys(components?.[componentType] || {})) {
353
355
  const filename = getFileNamePath(componentDirPath, componentName, ext);
@@ -356,7 +358,7 @@ function iterateComponents(
356
358
  }
357
359
 
358
360
  // eslint-disable-next-line no-inner-declarations
359
- function iterateComponentTypes(componentType: Oas3ComponentName) {
361
+ function iterateComponentTypes(componentType: Oas3ComponentName<Oas3Schema | Oas3_1Schema>) {
360
362
  const componentDirPath = path.join(componentsDir, componentType);
361
363
  createComponentDir(componentDirPath, componentType);
362
364
  for (const componentName of Object.keys(components?.[componentType] || {})) {
@@ -1,29 +1,6 @@
1
- import {
2
- Oas3Schema,
3
- Oas3_1Schema,
4
- Oas3Definition,
5
- Oas3_1Definition,
6
- Oas3Components,
7
- Oas3PathItem,
8
- Oas3Paths,
9
- Oas3ComponentName,
10
- Oas3_1Webhooks,
11
- Oas2Definition,
12
- Referenced,
13
- } from '@redocly/openapi-core';
14
- export {
15
- Oas3_1Definition,
16
- Oas3Definition,
17
- Oas2Definition,
18
- Oas3Components,
19
- Oas3Paths,
20
- Oas3PathItem,
21
- Oas3ComponentName,
22
- Oas3_1Schema,
23
- Oas3Schema,
24
- Oas3_1Webhooks,
25
- Referenced,
26
- };
1
+ import type { Oas2Definition } from '@redocly/openapi-core';
2
+ import type { Oas3_1Definition, Oas3Definition } from '@redocly/openapi-core/lib/typings/openapi';
3
+
27
4
  export type Definition = Oas3_1Definition | Oas3Definition | Oas2Definition;
28
5
  export interface ComponentsFiles {
29
6
  [schemas: string]: any;
@@ -1,5 +1,3 @@
1
- import nodeFetch, { type RequestInit } from 'node-fetch';
2
- import AbortController from 'abort-controller';
3
1
  import { getProxyAgent } from '@redocly/openapi-core';
4
2
 
5
3
  export const DEFAULT_FETCH_TIMEOUT = 3000;
@@ -10,24 +8,23 @@ export type FetchWithTimeoutOptions = RequestInit & {
10
8
 
11
9
  export default async (url: string, { timeout, ...options }: FetchWithTimeoutOptions = {}) => {
12
10
  if (!timeout) {
13
- return nodeFetch(url, {
11
+ return fetch(url, {
14
12
  ...options,
15
- agent: getProxyAgent(),
16
- });
13
+ dispatcher: getProxyAgent(),
14
+ } as RequestInit);
17
15
  }
18
16
 
19
- const controller = new AbortController();
17
+ const controller = new globalThis.AbortController();
20
18
  const timeoutId = setTimeout(() => {
21
19
  controller.abort();
22
20
  }, timeout);
23
21
 
24
- const res = await nodeFetch(url, {
22
+ const res = await fetch(url, {
25
23
  signal: controller.signal,
26
24
  ...options,
27
- agent: getProxyAgent(),
28
- });
25
+ dispatcher: getProxyAgent(),
26
+ } as RequestInit);
29
27
 
30
28
  clearTimeout(timeoutId);
31
-
32
29
  return res;
33
30
  };