diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d31fbca..1c631eb1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,35 +13,21 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-latest] node: [16.x, 18.x, 20.x] - experimental: [false] - include: - - os: ubuntu-latest - node: 14 - experimental: true - npm_version: "@8.3.1" - - os: windows-latest - node: 14 - experimental: true - npm_version: "@8.3.1" - - os: macos-latest - node: 14 - experimental: true - npm_version: "@8.3.1" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Set up Node.js ${{ matrix.node }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node }} + - name: Set up Node.js ${{ matrix.node }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node }} - - name: Install typescript - run: npm install -g typescript npm${{ matrix.npm_version }} && npm install + - name: Install typescript + run: npm install -g typescript npm${{ matrix.npm_version }} && npm install - - name: Lint, Compile, Test - run: npm run compile && npm run test -- --coverage --verbose --maxWorkers=2 + - name: Lint, Compile, Test + run: npm run compile && npm run test -- --coverage --verbose --maxWorkers=2 - - name: Run codecov - uses: codecov/codecov-action@v3 - with: - directory: ./coverage + - name: Run codecov + uses: codecov/codecov-action@v3 + with: + directory: ./coverage diff --git a/packages/vetch/__tests__/vetch.test.ts b/packages/vetch/__tests__/vetch.test.ts index 259a7673..b2b9c7cd 100644 --- a/packages/vetch/__tests__/vetch.test.ts +++ b/packages/vetch/__tests__/vetch.test.ts @@ -104,10 +104,7 @@ describe('option configuration', () => { }); test('should default to application/json', async () => { - nock(url) - .matchHeader('accept', 'application/json') - .get('/') - .reply(200, {}); + nock(url).matchHeader('accept', 'application/json').get('/').reply(200, {}); const res = await request({ url }); expect(res.data).toEqual({}); }); @@ -122,19 +119,25 @@ describe('option configuration', () => { test('should allow for our custom user agent', async () => { const options = { reqheaders: { - 'user-agent': (val) => { - return /^@vonage\/server-sdk\/[\d].[\d].[\d].* node\/.*$/.test( - val, - ); + 'user-agent': (val: string) => { + return /^@vonage\/server-sdk\/[\d].[\d].[\d].* node\/.*$/.test(val); }, }, }; nock(url, options).get('/').reply(200); const inst = new Vetch(); - const res = await inst.request({ url }); + await inst.request({ url }); expect(nock.isDone()).toBeTruthy(); }); + + test('should timeout', async () => { + nock(url).get('/').delayConnection(5).reply(200); + const inst = new Vetch({ timeout: 1 }); + await expect(inst.request({ url })).rejects.toThrow( + 'The user aborted a request', + ); + }); }); describe('data handling', () => { diff --git a/packages/vetch/lib/vetch.ts b/packages/vetch/lib/vetch.ts index 794937e4..dd453b98 100644 --- a/packages/vetch/lib/vetch.ts +++ b/packages/vetch/lib/vetch.ts @@ -1,9 +1,8 @@ -import fetch, { Response as fetchResponse } from 'node-fetch'; +import fetch, { Response as fetchResponse, options } from 'node-fetch'; import { stringify } from 'querystring'; import merge from 'lodash.merge'; import http from 'http'; import https from 'https'; -import URL from 'url'; import { VetchError } from './types/vetchError'; import { Headers } from './interfaces/headers'; import { VetchResponse } from './interfaces/vetchResponse'; @@ -26,9 +25,22 @@ export class Vetch { private async _defaultAdapter( opts: VetchOptions, ): Promise> { - const res = await fetch(opts.url, opts); - const data = await this.getResponseData(opts, res); - return this.createResponse(opts, res, data); + const { timeout } = opts; + let timeoutId = null; + const fetchConfig: options = opts; + if (timeout) { + const controller = new AbortController(); + timeoutId = setTimeout(() => controller.abort(), timeout); + fetchConfig.signal = controller.signal; + } + + try { + const res = await fetch(opts.url, fetchConfig); + const data = await this.getResponseData(opts, res); + return this.createResponse(opts, res, data); + } finally { + clearTimeout(timeoutId); + } } async request(opts: VetchOptions = {}): Promise> { @@ -116,10 +128,10 @@ export class Vetch { // Allow a custom timeout to be used const httpAgent = new http.Agent({ - timeout: this.defaults.timeout, + timeout: opts.timeout, }); const httpsAgent = new https.Agent({ - timeout: this.defaults.timeout, + timeout: opts.timeout, }); opts.agent = (parsedUrl: URL): https.Agent | http.Agent => { if (parsedUrl.protocol === 'http:') {