diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 24943e1..2290457 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-nodejs:latest - digest: sha256:609822e3c09b7a1bd90b99655904609f162cc15acb4704f1edf778284c36f429 + digest: sha256:0d39e59663287ae929c1d4ccf8ebf7cef9946826c9b86eda7e85d8d752dbb584 # created: 2024-10-01T19:34:30.797530443Z diff --git a/.github/release-trigger.yml b/.github/release-trigger.yml index d4ca941..038fba6 100644 --- a/.github/release-trigger.yml +++ b/.github/release-trigger.yml @@ -1 +1,2 @@ enabled: true +multiScmName: teeny-request \ No newline at end of file diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index b46e4c4..a013376 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -8,9 +8,9 @@ branchProtectionRules: - "ci/kokoro: Samples test" - "ci/kokoro: System test" - lint - - test (14) - - test (16) - test (18) + - test (20) + - test (22) - cla/google - windows - OwlBot Post Processor diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4892eb2..791891a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,10 +9,10 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [14, 16, 18, 20] + node: [18, 20, 22] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} - run: node --version @@ -29,10 +29,10 @@ jobs: windows: runs-on: windows-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 14 + node-version: 18 - run: npm install --engine-strict - run: npm test env: @@ -40,19 +40,19 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 14 + node-version: 18 - run: npm install - run: npm run lint docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 14 + node-version: 18 - run: npm install - run: npm run docs - uses: JustinBeckwith/linkinator-action@v1 diff --git a/.kokoro/common.cfg b/.kokoro/common.cfg index 67dd805..3fe2d24 100644 --- a/.kokoro/common.cfg +++ b/.kokoro/common.cfg @@ -16,7 +16,7 @@ build_file: "teeny-request/.kokoro/trampoline_v2.sh" # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/node:14-user" + value: "gcr.io/cloud-devrel-kokoro-resources/node:18-user" } env_vars: { key: "TRAMPOLINE_BUILD_FILE" diff --git a/.kokoro/continuous/node14/common.cfg b/.kokoro/continuous/node18/common.cfg similarity index 89% rename from .kokoro/continuous/node14/common.cfg rename to .kokoro/continuous/node18/common.cfg index 67dd805..3fe2d24 100644 --- a/.kokoro/continuous/node14/common.cfg +++ b/.kokoro/continuous/node18/common.cfg @@ -16,7 +16,7 @@ build_file: "teeny-request/.kokoro/trampoline_v2.sh" # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/node:14-user" + value: "gcr.io/cloud-devrel-kokoro-resources/node:18-user" } env_vars: { key: "TRAMPOLINE_BUILD_FILE" diff --git a/.kokoro/continuous/node14/lint.cfg b/.kokoro/continuous/node18/lint.cfg similarity index 100% rename from .kokoro/continuous/node14/lint.cfg rename to .kokoro/continuous/node18/lint.cfg diff --git a/.kokoro/continuous/node14/samples-test.cfg b/.kokoro/continuous/node18/samples-test.cfg similarity index 100% rename from .kokoro/continuous/node14/samples-test.cfg rename to .kokoro/continuous/node18/samples-test.cfg diff --git a/.kokoro/continuous/node14/system-test.cfg b/.kokoro/continuous/node18/system-test.cfg similarity index 100% rename from .kokoro/continuous/node14/system-test.cfg rename to .kokoro/continuous/node18/system-test.cfg diff --git a/.kokoro/continuous/node14/test.cfg b/.kokoro/continuous/node18/test.cfg similarity index 100% rename from .kokoro/continuous/node14/test.cfg rename to .kokoro/continuous/node18/test.cfg diff --git a/.kokoro/presubmit/node14/common.cfg b/.kokoro/presubmit/node18/common.cfg similarity index 89% rename from .kokoro/presubmit/node14/common.cfg rename to .kokoro/presubmit/node18/common.cfg index 67dd805..3fe2d24 100644 --- a/.kokoro/presubmit/node14/common.cfg +++ b/.kokoro/presubmit/node18/common.cfg @@ -16,7 +16,7 @@ build_file: "teeny-request/.kokoro/trampoline_v2.sh" # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/node:14-user" + value: "gcr.io/cloud-devrel-kokoro-resources/node:18-user" } env_vars: { key: "TRAMPOLINE_BUILD_FILE" diff --git a/.kokoro/presubmit/node14/samples-test.cfg b/.kokoro/presubmit/node18/samples-test.cfg similarity index 100% rename from .kokoro/presubmit/node14/samples-test.cfg rename to .kokoro/presubmit/node18/samples-test.cfg diff --git a/.kokoro/presubmit/node14/system-test.cfg b/.kokoro/presubmit/node18/system-test.cfg similarity index 100% rename from .kokoro/presubmit/node14/system-test.cfg rename to .kokoro/presubmit/node18/system-test.cfg diff --git a/.kokoro/presubmit/node14/test.cfg b/.kokoro/presubmit/node18/test.cfg similarity index 100% rename from .kokoro/presubmit/node14/test.cfg rename to .kokoro/presubmit/node18/test.cfg diff --git a/.kokoro/release/docs-devsite.cfg b/.kokoro/release/docs-devsite.cfg index 3e9f8b9..c04d2b9 100644 --- a/.kokoro/release/docs-devsite.cfg +++ b/.kokoro/release/docs-devsite.cfg @@ -11,7 +11,7 @@ before_action { # doc publications use a Python image. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/node:14-user" + value: "gcr.io/cloud-devrel-kokoro-resources/node:18-user" } # Download trampoline resources. diff --git a/.kokoro/release/docs.cfg b/.kokoro/release/docs.cfg index 6bf2aab..3b724fa 100644 --- a/.kokoro/release/docs.cfg +++ b/.kokoro/release/docs.cfg @@ -11,7 +11,7 @@ before_action { # doc publications use a Python image. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/node:14-user" + value: "gcr.io/cloud-devrel-kokoro-resources/node:18-user" } # Download trampoline resources. diff --git a/.kokoro/release/docs.sh b/.kokoro/release/docs.sh index 1d8f3f4..e9079a6 100755 --- a/.kokoro/release/docs.sh +++ b/.kokoro/release/docs.sh @@ -16,7 +16,7 @@ set -eo pipefail -# build jsdocs (Python is installed on the Node 10 docker image). +# build jsdocs (Python is installed on the Node 18 docker image). if [[ -z "$CREDENTIALS" ]]; then # if CREDENTIALS are explicitly set, assume we're testing locally # and don't set NPM_CONFIG_PREFIX. diff --git a/.kokoro/release/publish.cfg b/.kokoro/release/publish.cfg index 3d21b18..a4e27c7 100644 --- a/.kokoro/release/publish.cfg +++ b/.kokoro/release/publish.cfg @@ -30,7 +30,7 @@ build_file: "teeny-request/.kokoro/trampoline_v2.sh" # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/node:14-user" + value: "gcr.io/cloud-devrel-kokoro-resources/node:18-user" } env_vars: { diff --git a/.kokoro/samples-test.sh b/.kokoro/samples-test.sh index 8c5d108..5287753 100755 --- a/.kokoro/samples-test.sh +++ b/.kokoro/samples-test.sh @@ -16,7 +16,9 @@ set -eo pipefail -export NPM_CONFIG_PREFIX=${HOME}/.npm-global +# Ensure the npm global directory is writable, otherwise rebuild `npm` +mkdir -p $NPM_CONFIG_PREFIX +npm config -g ls || npm i -g npm@`npm --version` # Setup service account credentials. export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/secret_manager/long-door-651-kokoro-system-test-service-account @@ -56,7 +58,7 @@ fi # codecov combines coverage across integration and unit tests. Include # the logic below for any environment you wish to collect coverage for: -COVERAGE_NODE=14 +COVERAGE_NODE=18 if npx check-node-version@3.3.0 --silent --node $COVERAGE_NODE; then NYC_BIN=./node_modules/nyc/bin/nyc.js if [ -f "$NYC_BIN" ]; then diff --git a/.kokoro/system-test.sh b/.kokoro/system-test.sh index 0b3043d..a90d5cf 100755 --- a/.kokoro/system-test.sh +++ b/.kokoro/system-test.sh @@ -49,7 +49,7 @@ npm run system-test # codecov combines coverage across integration and unit tests. Include # the logic below for any environment you wish to collect coverage for: -COVERAGE_NODE=14 +COVERAGE_NODE=18 if npx check-node-version@3.3.0 --silent --node $COVERAGE_NODE; then NYC_BIN=./node_modules/nyc/bin/nyc.js if [ -f "$NYC_BIN" ]; then diff --git a/.kokoro/test.bat b/.kokoro/test.bat index 0bb1240..caf8256 100644 --- a/.kokoro/test.bat +++ b/.kokoro/test.bat @@ -21,7 +21,7 @@ cd .. @rem we upgrade Node.js in the image: SET PATH=%PATH%;/cygdrive/c/Program Files/nodejs/npm -call nvm use v14.17.3 +call nvm use 18 call which node call npm install || goto :error diff --git a/.kokoro/test.sh b/.kokoro/test.sh index 862d478..0d9f639 100755 --- a/.kokoro/test.sh +++ b/.kokoro/test.sh @@ -39,7 +39,7 @@ npm test # codecov combines coverage across integration and unit tests. Include # the logic below for any environment you wish to collect coverage for: -COVERAGE_NODE=14 +COVERAGE_NODE=18 if npx check-node-version@3.3.0 --silent --node $COVERAGE_NODE; then NYC_BIN=./node_modules/nyc/bin/nyc.js if [ -f "$NYC_BIN" ]; then diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh index 4d03112..5d6cfcc 100755 --- a/.kokoro/trampoline_v2.sh +++ b/.kokoro/trampoline_v2.sh @@ -44,7 +44,7 @@ # the project root. # # Here is an example for running this script. -# TRAMPOLINE_IMAGE=gcr.io/cloud-devrel-kokoro-resources/node:10-user \ +# TRAMPOLINE_IMAGE=gcr.io/cloud-devrel-kokoro-resources/node:18-user \ # TRAMPOLINE_BUILD_FILE=.kokoro/system-test.sh \ # .kokoro/trampoline_v2.sh diff --git a/owlbot.py b/owlbot.py index f184210..6bfddf7 100644 --- a/owlbot.py +++ b/owlbot.py @@ -14,4 +14,4 @@ import synthtool.languages.node as node -node.owlbot_main(templates_excludes=["README.md", ".github/ISSUE_TEMPLATE", ".github/scripts", ".github/workflows"]) +node.owlbot_main(templates_excludes=["README.md", ".github/ISSUE_TEMPLATE", ".github/scripts", ".github/workflows/issues-no-repro.yaml", ".github/workflows/response.yaml"]) diff --git a/package.json b/package.json index 994c9a1..8ba4a31 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "./build/src/index.js", "types": "./build/src/index.d.ts", "engines": { - "node": ">=14" + "node": ">=18" }, "scripts": { "test": "c8 mocha build/test", @@ -40,27 +40,26 @@ "dependencies": { "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.9", - "stream-events": "^1.0.5", - "uuid": "^9.0.0" + "node-fetch": "^3.3.2", + "stream-events": "^1.0.5" }, "devDependencies": { "@babel/plugin-proposal-private-methods": "^7.18.6", - "@types/mocha": "^10.0.0", - "@types/node-fetch": "^2.5.7", - "@types/sinon": "^17.0.0", - "@types/uuid": "^9.0.0", - "c8": "^9.0.0", - "codecov": "^3.1.0", - "gts": "^5.0.0", - "jsdoc": "^4.0.0", + "@types/mocha": "^10.0.10", + "@types/node-fetch": "^2.6.12", + "@types/sinon": "^17.0.3", + "@types/uuid": "^10.0.0", + "c8": "^10.1.3", + "codecov": "^3.8.3", + "gts": "^6.0.2", + "jsdoc": "^4.0.4", "jsdoc-fresh": "^3.0.0", "jsdoc-region-tag": "^3.0.0", - "linkinator": "^3.0.0", - "mocha": "^10.0.0", - "nock": "^13.0.0", - "sinon": "^17.0.0", - "typescript": "^5.1.6" + "linkinator": "^6.1.2", + "mocha": "^11.1.0", + "nock": "^14.0.1", + "sinon": "^19.0.2", + "typescript": "^5.7.3" }, "nyc": { "exclude": [ diff --git a/src/TeenyStatistics.ts b/src/TeenyStatistics.ts index 0e3a77d..2182237 100644 --- a/src/TeenyStatistics.ts +++ b/src/TeenyStatistics.ts @@ -153,7 +153,7 @@ export class TeenyStatistics { this._options.concurrentRequests + '. Use the TEENY_REQUEST_WARN_CONCURRENT_REQUESTS environment ' + 'variable or the concurrentRequests option of teeny-request to ' + - 'increase or disable (0) this warning.' + 'increase or disable (0) this warning.', ); warning.type = TeenyStatisticsWarning.CONCURRENT_REQUESTS; warning.value = this._concurrentRequests; @@ -187,7 +187,7 @@ export class TeenyStatistics { let concurrentRequests = this.DEFAULT_WARN_CONCURRENT_REQUESTS; const envConcurrentRequests = Number( - process.env.TEENY_REQUEST_WARN_CONCURRENT_REQUESTS + process.env.TEENY_REQUEST_WARN_CONCURRENT_REQUESTS, ); if (diConcurrentRequests !== undefined) { concurrentRequests = diConcurrentRequests; diff --git a/src/agents.ts b/src/agents.ts index 2ae9e74..3369dcf 100644 --- a/src/agents.ts +++ b/src/agents.ts @@ -66,7 +66,7 @@ function shouldUseProxyForURI(uri: string): boolean { */ export function getAgent( uri: string, - reqOpts: Options + reqOpts: Options, ): HttpAnyAgent | undefined { const isHttp = uri.startsWith('http://'); const proxy = diff --git a/src/index.ts b/src/index.ts index 31d87df..737560a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,14 +17,19 @@ import {Agent, AgentOptions as HttpsAgentOptions} from 'https'; import {AgentOptions as HttpAgentOptions} from 'http'; -import fetch, * as f from 'node-fetch'; +import type * as f from 'node-fetch' with {'resolution-mode': 'import'}; import {PassThrough, Readable, pipeline} from 'stream'; -import * as uuid from 'uuid'; import {getAgent} from './agents'; import {TeenyStatistics} from './TeenyStatistics'; +import {randomUUID} from 'crypto'; // eslint-disable-next-line @typescript-eslint/no-var-requires const streamEvents = require('stream-events'); +import type nodeFetch from 'node-fetch' with {'resolution-mode': 'import'}; + +const fetch = (...args: Parameters) => + import('node-fetch').then(({default: fetch}) => fetch(...args)); + export interface CoreOptions { method?: string; timeout?: number; @@ -152,7 +157,7 @@ function fetchToRequestResponse(opts: f.RequestInit, res: f.Response) { const resHeaders = {} as Headers; res.headers.forEach((value, key) => (resHeaders[key] = value)); - const response = Object.assign(res.body, { + const response = Object.assign(res.body as {}, { statusCode: res.status, statusMessage: res.statusText, request, @@ -198,7 +203,7 @@ function teenyRequest(reqOpts: Options): Request; function teenyRequest(reqOpts: Options, callback: RequestCallback): void; function teenyRequest( reqOpts: Options, - callback?: RequestCallback + callback?: RequestCallback, ): Request | void { const {uri, options} = requestToFetchOptions(reqOpts); @@ -208,7 +213,7 @@ function teenyRequest( // TODO: add support for multipart uploads through streaming throw new Error('Multipart without callback is not implemented.'); } - const boundary: string = uuid.v4(); + const boundary: string = randomUUID(); (options.headers as Headers)['Content-Type'] = `multipart/related; boundary=${boundary}`; options.body = createMultipartStream(boundary, multipart); @@ -232,7 +237,7 @@ function teenyRequest( }, (err: Error) => { callback(err, response, body); - } + }, ); return; } @@ -244,13 +249,13 @@ function teenyRequest( }, err => { callback(err, response, body); - } + }, ); }, err => { teenyRequest.stats.requestFinished(); callback(err, null!, null); - } + }, ); return; } @@ -287,7 +292,7 @@ function teenyRequest( err => { teenyRequest.stats.requestFinished(); requestStream.emit('error', err); - } + }, ); // fetch doesn't supply the raw HTTP stream, instead it @@ -320,7 +325,7 @@ function teenyRequest( }, err => { callback(err, response, body); - } + }, ); return; } @@ -333,13 +338,13 @@ function teenyRequest( }, err => { callback(err, response, body); - } + }, ); }, err => { teenyRequest.stats.requestFinished(); callback(err, null!, null); - } + }, ); return; } diff --git a/test/TeenyStatistics.ts b/test/TeenyStatistics.ts index 90653d2..a554547 100644 --- a/test/TeenyStatistics.ts +++ b/test/TeenyStatistics.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import * as assert from 'assert'; +import assert from 'assert'; import {afterEach, before, beforeEach, describe, it} from 'mocha'; import * as sinon from 'sinon'; import { @@ -65,7 +65,7 @@ describe('TeenyStatistics', () => { it('should have default concurrent requests', () => { assert.strictEqual( TeenyStatistics.DEFAULT_WARN_CONCURRENT_REQUESTS, - 5000 + 5000, ); }); @@ -231,8 +231,8 @@ describe('TeenyStatistics', () => { t.requestStarting(); assert( emitWarnStub.calledOnceWith( - sinon.match.instanceOf(TeenyStatisticsWarning) - ) + sinon.match.instanceOf(TeenyStatisticsWarning), + ), ); }); @@ -246,8 +246,8 @@ describe('TeenyStatistics', () => { t.requestStarting(); assert( emitWarnStub.calledOnceWith( - sinon.match.instanceOf(TeenyStatisticsWarning) - ) + sinon.match.instanceOf(TeenyStatisticsWarning), + ), ); // shouldn't emit on the next call (i.e. still greater than threshold) @@ -271,8 +271,8 @@ describe('TeenyStatistics', () => { t.requestStarting(); assert( emitWarnStub.calledOnceWith( - sinon.match.instanceOf(TeenyStatisticsWarning) - ) + sinon.match.instanceOf(TeenyStatisticsWarning), + ), ); // let's bring the counter back down @@ -298,7 +298,7 @@ describe('TeenyStatistics', () => { assert.strictEqual(warning.value, 5e3); assert.strictEqual( warning.type, - TeenyStatisticsWarning.CONCURRENT_REQUESTS + TeenyStatisticsWarning.CONCURRENT_REQUESTS, ); }); @@ -311,15 +311,15 @@ describe('TeenyStatistics', () => { const errStr: string = emitWarnStub.firstCall.args[0].toString(); assert( errStr.includes('Possible excessive concurrent requests detected.'), - 'describes the nature of the warning' + 'describes the nature of the warning', ); assert( errStr.includes('TEENY_REQUEST_WARN_CONCURRENT_REQUESTS'), - 'mentions env var' + 'mentions env var', ); assert( errStr.includes('concurrentRequests'), - 'mentions concurrentRequests option' + 'mentions concurrentRequests option', ); assert(errStr.search(/\b0\b/) !== -1, 'mentions 0'); }); diff --git a/test/agents.ts b/test/agents.ts index 7b28068..6ea3f8d 100644 --- a/test/agents.ts +++ b/test/agents.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import * as assert from 'assert'; +import assert from 'assert'; import {describe, it, afterEach} from 'mocha'; import * as http from 'http'; import * as https from 'https'; @@ -211,7 +211,7 @@ describe('agents', () => { maxSockets: 1000, }, }, - defaultOptions + defaultOptions, ); const agent = getAgent(uri, options); assert.strictEqual(agent!.maxSockets, 1000); @@ -224,7 +224,7 @@ describe('agents', () => { maxSockets: 1000, }, }, - defaultOptions + defaultOptions, ); const agent = getAgent(uri, options); assert.strictEqual(agent, undefined); @@ -243,7 +243,7 @@ describe('agents', () => { maxSockets: 1000, }, }, - defaultOptions + defaultOptions, ); const agent = getAgent(uri, options); assert.strictEqual(agent!.maxSockets, 1000); @@ -256,7 +256,7 @@ describe('agents', () => { maxSockets: 1000, }, }, - defaultOptions + defaultOptions, ); const agent = getAgent(uri, options); assert.strictEqual(agent, undefined); diff --git a/test/index.ts b/test/index.ts index ab398ff..1dcd69e 100644 --- a/test/index.ts +++ b/test/index.ts @@ -15,10 +15,10 @@ * limitations under the License. */ -import * as assert from 'assert'; +import assert from 'assert'; import {describe, it, afterEach, beforeEach} from 'mocha'; -import * as nock from 'nock'; -import {Readable, PassThrough} from 'stream'; +import nock from 'nock'; +import {Readable} from 'stream'; import * as sinon from 'sinon'; import {teenyRequest} from '../src'; import {TeenyStatistics, TeenyStatisticsWarning} from '../src/TeenyStatistics'; @@ -65,18 +65,18 @@ describe('teeny', () => { nock.cleanAll(); }); - it('should get JSON', done => { + it('should get JSON', async () => { const scope = mockJson(); teenyRequest({uri}, (error, response, body) => { assert.ifError(error); assert.strictEqual(response.statusCode, 200); assert.ok(body.hello); scope.done(); - done(); + // done(); }); }); - it('should set defaults', done => { + it('should set defaults', async () => { const scope = mockJson(); const defaultRequest = teenyRequest.defaults({timeout: 60000}); defaultRequest({uri}, (error, response, body) => { @@ -84,11 +84,10 @@ describe('teeny', () => { assert.strictEqual(response.statusCode, 200); assert.ok(body.hello); scope.done(); - done(); }); }); - it('response event emits object compatible with request module', done => { + it('response event emits object compatible with request module', async () => { const reqHeaders = {fruit: 'banana'}; const resHeaders = {veggies: 'carrots'}; const scope = nock(uri).get('/').reply(202, 'ok', resHeaders); @@ -103,12 +102,13 @@ describe('teeny', () => { }); assert(res instanceof Readable); scope.done(); - done(); }) - .on('error', done); + .on('error', err => { + throw err; + }); }); - it('should include the request in the response', done => { + it('should include the request in the response', async () => { const path = '/?dessert=pie'; const scope = nock(uri).get(path).reply(202); const headers = {dinner: 'tacos'}; @@ -119,11 +119,10 @@ describe('teeny', () => { assert.deepStrictEqual(req.headers, headers); assert.strictEqual(req.href, url); scope.done(); - done(); }); }); - it('should not wrap the error', done => { + it('should not wrap the error', async () => { const scope = nock(uri) .get('/') .reply(200, '🚨', {'content-type': 'application/json'}); @@ -131,11 +130,10 @@ describe('teeny', () => { assert.ok(err); assert.ok(err!.message.match(/^invalid json response body/)); scope.done(); - done(); }); }); - it('should include headers in the response', done => { + it('should include headers in the response', async () => { const headers = {dinner: 'tacos'}; const body = {hello: '🌍'}; const scope = nock(uri).get('/').reply(200, body, headers); @@ -143,22 +141,20 @@ describe('teeny', () => { assert.ifError(err); assert.strictEqual(headers['dinner'], res.headers['dinner']); scope.done(); - done(); }); }); - it('should accept the forever option', done => { + it('should accept the forever option', async () => { const scope = nock(uri).get('/').reply(200); teenyRequest({uri, forever: true}, (err, res) => { assert.ifError(err); // eslint-disable-next-line @typescript-eslint/no-explicit-any assert.strictEqual((res.request.agent as any).keepAlive, true); scope.done(); - done(); }); }); - it('should allow setting compress/gzip to true', done => { + it('should allow setting compress/gzip to true', async () => { const reqheaders = { 'Accept-Encoding': 'gzip,deflate', }; @@ -168,11 +164,10 @@ describe('teeny', () => { teenyRequest({uri, gzip: true}, err => { assert.ifError(err); scope.done(); - done(); }); }); - it('should allow setting compress/gzip to false', done => { + it('should allow setting compress/gzip to false', async () => { const badheaders = ['Accept-Encoding']; const scope = nock(uri, {badheaders}).get('/').reply(200); @@ -180,13 +175,12 @@ describe('teeny', () => { teenyRequest({uri, gzip: false}, err => { assert.ifError(err); scope.done(); - done(); }); }); const envVars = ['http_proxy', 'https_proxy', 'HTTP_PROXY', 'HTTPS_PROXY']; for (const v of envVars) { - it(`should respect ${v} environment variable for proxy config`, done => { + it(`should respect ${v} environment variable for proxy config`, () => { sandbox.stub(process, 'env').value({[v]: 'https://fake.proxy'}); const expectedBody = {hello: '🌎'}; const scope = nock(uri).get('/').reply(200, expectedBody); @@ -195,12 +189,12 @@ describe('teeny', () => { assert.ifError(err); assert.deepStrictEqual(expectedBody, body); assert.ok(res.request.agent instanceof HttpsProxyAgent); - return done(); + return; }); }); } - it('should create http proxy if upstream scheme is http', done => { + it('should create http proxy if upstream scheme is http', async () => { sandbox.stub(process, 'env').value({http_proxy: 'https://fake.proxy'}); const expectedBody = {hello: '🌎'}; const scope = nock('http://example.com').get('/').reply(200, expectedBody); @@ -209,11 +203,11 @@ describe('teeny', () => { assert.ifError(err); assert.deepStrictEqual(expectedBody, body); assert.ok(res.request.agent instanceof HttpProxyAgent); - return done(); + return; }); }); - it('should use proxy if set in request options', done => { + it('should use proxy if set in request options', async () => { const expectedBody = {hello: '🌎'}; const scope = nock(uri).get('/').reply(200, expectedBody); teenyRequest({uri, proxy: 'https://fake.proxy'}, (err, res, body) => { @@ -221,44 +215,52 @@ describe('teeny', () => { assert.ifError(err); assert.deepStrictEqual(expectedBody, body); assert.ok(res.request.agent instanceof HttpsProxyAgent); - return done(); + return; }); }); // see: https://github.com/googleapis/nodejs-storage/issues/798 it('should not throw exception when piped through pumpify', async () => { const scope = mockJson(); - const stream = teenyRequest({uri}).pipe(new PassThrough()); - let content = ''; + const stream = teenyRequest({uri}); + // set the encoding for the returned stream + stream.setEncoding('utf8'); + + // collect the buffers, then concat later for performance + const content: string[] = []; for await (const data of stream) { - content += data; + content.push(data); } - assert.deepStrictEqual(JSON.parse(content), {hello: '🌍'}); + + assert.deepStrictEqual(JSON.parse(content.join('')), {hello: '🌍'}); scope.done(); }); - it('should emit response event when called without callback', done => { + it('should emit response event when called without callback', async () => { const scope = mockJson(); teenyRequest({uri}).on('response', res => { assert.ok(res); scope.done(); - return done(); + return; }); }); - it('should pipe response stream to user', done => { + it('should pipe response stream to user', () => { const scope = mockJson(); teenyRequest({uri}) - .on('error', done) + .on('error', err => { + throw err; + }) .on('data', () => { scope.done(); - done(); }); }); - it('should not pipe response stream to user unless they ask for it', done => { + it('should not pipe response stream to user unless they ask for it', async () => { const scope = mockJson(); - const stream = teenyRequest({uri}).on('error', done); + const stream = teenyRequest({uri}).on('error', err => { + throw err; + }); stream.on('response', responseStream => { // We are using an internal property of Readable to get the number of // active readers. The property changed from `pipesCount: number` in @@ -273,7 +275,6 @@ describe('teeny', () => { responseStream.body._readableState.pipes?.length; assert.strictEqual(numPipes, 1); scope.done(); - done(); }); }); }); @@ -298,7 +299,7 @@ describe('teeny', () => { assert.deepStrictEqual(newOptions, {concurrentRequests: 42}); }); - it('should emit warning on too many concurrent requests', done => { + it('should emit warning on too many concurrent requests', () => { statsStub.setOptions.restore(); statsStub.requestStarting.restore(); teenyRequest.stats.setOptions({concurrentRequests: 1}); @@ -307,32 +308,29 @@ describe('teeny', () => { teenyRequest({uri}, () => { assert.ok(emitWarnStub.calledOnce); scope.done(); - done(); }); }); - it('should track stats, callback mode, success', done => { + it('should track stats, callback mode, success', () => { const scope = mockJson(); teenyRequest({uri}, () => { assert.ok(statsStub.requestStarting.calledOnceWithExactly()); assert.ok(statsStub.requestFinished.calledOnceWithExactly()); scope.done(); - done(); }); }); - it('should track stats, callback mode, failure', done => { + it('should track stats, callback mode, failure', () => { const scope = mockError(); teenyRequest({uri}, err => { assert.ok(err); assert.ok(statsStub.requestStarting.calledOnceWithExactly()); assert.ok(statsStub.requestFinished.calledOnceWithExactly()); scope.done(); - done(); }); }); - it('should track stats, stream mode, success', done => { + it('should track stats, stream mode, success', () => { const scope = mockJson(); const readable = teenyRequest({uri}); assert.ok(statsStub.requestStarting.calledOnceWithExactly()); @@ -340,11 +338,10 @@ describe('teeny', () => { readable.once('response', () => { assert.ok(statsStub.requestFinished.calledOnceWithExactly()); scope.done(); - done(); }); }); - it('should track stats, stream mode, failure', done => { + it('should track stats, stream mode, failure', () => { const scope = mockError(); const readable = teenyRequest({uri}); assert.ok(statsStub.requestStarting.calledOnceWithExactly()); @@ -353,11 +350,10 @@ describe('teeny', () => { assert.ok(err); assert.ok(statsStub.requestFinished.calledOnceWithExactly()); scope.done(); - done(); }); }); - it('should accept a Buffer as the body of a request', done => { + it('should accept a Buffer as the body of a request', () => { const scope = nock(uri).post('/', 'hello').reply(200, '🌍'); teenyRequest( {uri, method: 'POST', body: Buffer.from('hello')}, @@ -366,12 +362,11 @@ describe('teeny', () => { assert.strictEqual(response.statusCode, 200); assert.strictEqual(body, '🌍'); scope.done(); - done(); - } + }, ); }); - it('should accept a plain string as the body of a request', done => { + it('should accept a plain string as the body of a request', () => { const scope = nock(uri).post('/', 'hello').reply(200, '🌍'); teenyRequest( {uri, method: 'POST', body: 'hello'}, @@ -380,12 +375,11 @@ describe('teeny', () => { assert.strictEqual(response.statusCode, 200); assert.strictEqual(body, '🌍'); scope.done(); - done(); - } + }, ); }); - it('should accept json as the body of a request', done => { + it('should accept json as the body of a request', () => { const body = {hello: '🌍'}; const scope = nock(uri).post('/', JSON.stringify(body)).reply(200, '👋'); teenyRequest({uri, method: 'POST', json: body}, (error, response, body) => { @@ -393,7 +387,6 @@ describe('teeny', () => { assert.strictEqual(response.statusCode, 200); assert.strictEqual(body, '👋'); scope.done(); - done(); }); }); @@ -413,11 +406,11 @@ describe('teeny', () => { assert.ok(statsStub.requestFinished.calledOnceWithExactly()); scope.done(); done(); - } + }, ); }); - it.skip('should track stats, multipart mode, failure', done => { + it.skip('should track stats, multipart mode, failure', () => { const scope = mockError(); teenyRequest( { @@ -431,30 +424,27 @@ describe('teeny', () => { assert.ok(statsStub.requestStarting.calledOnceWithExactly()); assert.ok(statsStub.requestFinished.calledOnceWithExactly()); scope.done(); - done(); - } + }, ); }); - it('should throw an exception if uri is an empty string', done => { + it('should throw an exception if uri is an empty string', () => { assert.throws( () => { teenyRequest({uri: ''}); }, /Missing uri or url in reqOpts/, - 'Did not throw with expected message' + 'Did not throw with expected message', ); - done(); }); - it('should throw an exception if url is an empty string', done => { + it('should throw an exception if url is an empty string', () => { assert.throws( () => { teenyRequest({url: ''}); }, /Missing uri or url in reqOpts/, - 'Did not throw with expected message' + 'Did not throw with expected message', ); - done(); }); }); diff --git a/tsconfig.json b/tsconfig.json index 26f33cd..7e87ff7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,9 @@ "extends": "./node_modules/gts/tsconfig-google.json", "compilerOptions": { "rootDir": ".", - "outDir": "build" + "outDir": "build", + "moduleResolution": "nodenext", + "module": "nodenext" }, "include": [ "src/*.ts",