Skip to content

Commit 3112bb7

Browse files
authored
Add TS rules (#136)
1 parent 5943663 commit 3112bb7

25 files changed

Lines changed: 3278 additions & 34 deletions

build.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,18 @@ const resolveEnd = '__END_REQUIRE_RESOLVE__';
1010
// Require.resolve needs to be dynamic and cannot be statically stringified with JSON.stringify
1111
const stringify = (data) =>
1212
`module.exports = ${JSON.stringify(data, (k, v) => {
13-
if (k === 'parser' && v.startsWith(__dirname)) {
14-
return (
15-
resolveStart +
16-
v // Replace the static node_modules path with a relative path
17-
.replace(join(__dirname, `node_modules${sep}`), '')
18-
.replace(/\/.*$/, '') +
19-
resolveEnd
13+
if (k === 'parser' && typeof v === 'string' && v.startsWith(__dirname)) {
14+
const pathWithoutNodeModules = v.replace(
15+
join(__dirname, `node_modules${sep}`),
16+
''
2017
);
18+
// Takes the file path and changes it to just the name of the package the path was in
19+
const packagePath = pathWithoutNodeModules.startsWith('@')
20+
? // If it is a part of a npm org, then it will be two levels deep: @___/___
21+
/^@[^/]*\/[^/]*/.exec(pathWithoutNodeModules)[0]
22+
: // Otherwise, it will just be one level deep: ___
23+
/^[^/]*/.exec(pathWithoutNodeModules)[0];
24+
return `${resolveStart}${packagePath}${resolveEnd}`;
2125
}
2226
2327
return v;

fixtures/.eslintrc.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const path = require('path');
12
module.exports = {
23
rules: {
34
// These rules are disabled because they are very likely to trigger
@@ -7,4 +8,7 @@ module.exports = {
78
'@cloudfour/node/no-missing-import': 'off',
89
'@cloudfour/node/no-extraneous-import': 'off',
910
},
11+
parserOptions: {
12+
project: path.join(__dirname, 'tsconfig.json'),
13+
},
1014
};

fixtures/got/benchmark/index.ts

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
'use strict';
2+
import { URL } from 'url';
3+
import axios from 'axios';
4+
import fetch from 'node-fetch';
5+
import got from '../source';
6+
import PromisableRequest from '../source/as-promise/core';
7+
import Request, { kIsNormalizedAlready } from '../source/core';
8+
import https = require('https');
9+
import Benchmark = require('benchmark');
10+
import request = require('request');
11+
12+
const { normalizeArguments } = PromisableRequest;
13+
14+
// Configuration
15+
const httpsAgent = new https.Agent({
16+
keepAlive: true,
17+
rejectUnauthorized: false,
18+
});
19+
20+
const url = new URL('https://127.0.0.1:8080');
21+
const urlString = url.toString();
22+
23+
const gotOptions = {
24+
agent: {
25+
https: httpsAgent,
26+
},
27+
https: {
28+
rejectUnauthorized: false,
29+
},
30+
retry: 0,
31+
};
32+
33+
const normalizedGotOptions = normalizeArguments(url, gotOptions);
34+
normalizedGotOptions[kIsNormalizedAlready] = true;
35+
36+
const requestOptions = {
37+
strictSSL: false,
38+
agent: httpsAgent,
39+
};
40+
41+
const fetchOptions = {
42+
agent: httpsAgent,
43+
};
44+
45+
const axiosOptions = {
46+
url: urlString,
47+
httpsAgent,
48+
https: {
49+
rejectUnauthorized: false,
50+
},
51+
};
52+
53+
const axiosStreamOptions: typeof axiosOptions & { responseType: 'stream' } = {
54+
...axiosOptions,
55+
responseType: 'stream',
56+
};
57+
58+
const httpsOptions = {
59+
https: {
60+
rejectUnauthorized: false,
61+
},
62+
agent: httpsAgent,
63+
};
64+
65+
const suite = new Benchmark.Suite();
66+
67+
// Benchmarking
68+
suite
69+
.add('got - promise', {
70+
defer: true,
71+
fn: async (deferred: { resolve: () => void }) => {
72+
await got(url, gotOptions);
73+
deferred.resolve();
74+
},
75+
})
76+
.add('got - stream', {
77+
defer: true,
78+
fn: (deferred: { resolve: () => void }) => {
79+
got
80+
.stream(url, gotOptions)
81+
.resume()
82+
.once('end', () => {
83+
deferred.resolve();
84+
});
85+
},
86+
})
87+
.add('got - promise core', {
88+
defer: true,
89+
fn: (deferred: { resolve: () => void }) => {
90+
const stream = new PromisableRequest(url, gotOptions);
91+
stream.resume().once('end', () => {
92+
deferred.resolve();
93+
});
94+
},
95+
})
96+
.add('got - stream core', {
97+
defer: true,
98+
fn: (deferred: { resolve: () => void }) => {
99+
const stream = new Request(url, gotOptions);
100+
stream.resume().once('end', () => {
101+
deferred.resolve();
102+
});
103+
},
104+
})
105+
.add('got - stream core - normalized options', {
106+
defer: true,
107+
fn: (deferred: { resolve: () => void }) => {
108+
const stream = new Request(undefined as any, normalizedGotOptions);
109+
stream.resume().once('end', () => {
110+
deferred.resolve();
111+
});
112+
},
113+
})
114+
.add('request - callback', {
115+
defer: true,
116+
fn: (deferred: { resolve: () => void }) => {
117+
request(urlString, requestOptions, (error: Error) => {
118+
if (error) {
119+
throw error;
120+
}
121+
122+
deferred.resolve();
123+
});
124+
},
125+
})
126+
.add('request - stream', {
127+
defer: true,
128+
fn: (deferred: { resolve: () => void }) => {
129+
const stream = request(urlString, requestOptions);
130+
stream.resume();
131+
stream.once('end', () => {
132+
deferred.resolve();
133+
});
134+
},
135+
})
136+
.add('node-fetch - promise', {
137+
defer: true,
138+
fn: async (deferred: { resolve: () => void }) => {
139+
const response = await fetch(url, fetchOptions);
140+
await response.text();
141+
142+
deferred.resolve();
143+
},
144+
})
145+
.add('node-fetch - stream', {
146+
defer: true,
147+
fn: async (deferred: { resolve: () => void }) => {
148+
const { body } = await fetch(url, fetchOptions);
149+
150+
body.resume();
151+
body.once('end', () => {
152+
deferred.resolve();
153+
});
154+
},
155+
})
156+
.add('axios - promise', {
157+
defer: true,
158+
fn: async (deferred: { resolve: () => void }) => {
159+
await axios.request(axiosOptions);
160+
deferred.resolve();
161+
},
162+
})
163+
.add('axios - stream', {
164+
defer: true,
165+
fn: async (deferred: { resolve: () => void }) => {
166+
const { data } = await axios.request(axiosStreamOptions);
167+
data.resume();
168+
data.once('end', () => {
169+
deferred.resolve();
170+
});
171+
},
172+
})
173+
.add('https - stream', {
174+
defer: true,
175+
fn: (deferred: { resolve: () => void }) => {
176+
https
177+
.request(urlString, httpsOptions, (response) => {
178+
response.resume();
179+
response.once('end', () => {
180+
deferred.resolve();
181+
});
182+
})
183+
.end();
184+
},
185+
})
186+
.on('cycle', (event: Benchmark.Event) => {
187+
console.log(String(event.target));
188+
})
189+
.on('complete', function (this: any) {
190+
console.log(`Fastest is ${this.filter('fastest').map('name') as string}`);
191+
192+
internalBenchmark();
193+
})
194+
.run();
195+
196+
const internalBenchmark = (): void => {
197+
console.log();
198+
199+
const internalSuite = new Benchmark.Suite();
200+
internalSuite
201+
.add('got - normalize options', {
202+
fn: () => {
203+
normalizeArguments(url, gotOptions);
204+
},
205+
})
206+
.on('cycle', (event: Benchmark.Event) => {
207+
console.log(String(event.target));
208+
});
209+
210+
internalSuite.run();
211+
};
212+
213+
// Results (i7-7700k, CPU governor: performance):
214+
// got - promise x 3,204 ops/sec ±5.27% (73 runs sampled)
215+
// got - stream x 5,045 ops/sec ±3.85% (77 runs sampled)
216+
// got - promise core x 6,499 ops/sec ±3.67% (77 runs sampled)
217+
// got - stream core x 7,047 ops/sec ±2.32% (83 runs sampled)
218+
// got - stream core - normalized options x 7,313 ops/sec ±2.79% (85 runs sampled)
219+
// request - callback x 6,918 ops/sec ±5.76% (73 runs sampled)
220+
// request - stream x 7,746 ops/sec ±3.20% (82 runs sampled)
221+
// node-fetch - promise x 7,011 ops/sec ±7.54% (75 runs sampled)
222+
// node-fetch - stream x 7,941 ops/sec ±4.52% (82 runs sampled)
223+
// axios - promise x 6,788 ops/sec ±3.32% (80 runs sampled)
224+
// axios - stream x 8,584 ops/sec ±2.23% (81 runs sampled)
225+
// https - stream x 10,465 ops/sec ±2.89% (73 runs sampled)
226+
// Fastest is https - stream
227+
228+
// got - normalize options x 166,389 ops/sec ±0.63% (91 runs sampled)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import {
2+
ParseError,
3+
HTTPError,
4+
MaxRedirectsError,
5+
RetryObject,
6+
RetryFunction,
7+
} from './types';
8+
9+
type Returns<T extends (...args: any) => unknown, V> = (
10+
...args: Parameters<T>
11+
) => V;
12+
13+
const retryAfterStatusCodes: ReadonlySet<number> = new Set([413, 429, 503]);
14+
15+
const isErrorWithResponse = (
16+
error: RetryObject['error']
17+
): error is HTTPError | ParseError | MaxRedirectsError =>
18+
error instanceof HTTPError ||
19+
error instanceof ParseError ||
20+
error instanceof MaxRedirectsError;
21+
22+
const calculateRetryDelay: Returns<RetryFunction, number> = ({
23+
attemptCount,
24+
retryOptions,
25+
error,
26+
}) => {
27+
if (attemptCount > retryOptions.limit) {
28+
return 0;
29+
}
30+
31+
const hasMethod = retryOptions.methods.includes(error.options.method);
32+
const hasErrorCode = retryOptions.errorCodes.includes(error.code);
33+
const hasStatusCode =
34+
isErrorWithResponse(error) &&
35+
retryOptions.statusCodes.includes(error.response.statusCode);
36+
if (!hasMethod || (!hasErrorCode && !hasStatusCode)) {
37+
return 0;
38+
}
39+
40+
if (isErrorWithResponse(error)) {
41+
const { response } = error;
42+
if (
43+
response &&
44+
'retry-after' in response.headers &&
45+
retryAfterStatusCodes.has(response.statusCode)
46+
) {
47+
let after = Number(response.headers['retry-after']);
48+
if (Number.isNaN(after)) {
49+
after = Date.parse(response.headers['retry-after']) - Date.now();
50+
} else {
51+
after *= 1000;
52+
}
53+
54+
if (
55+
retryOptions.maxRetryAfter === undefined ||
56+
after > retryOptions.maxRetryAfter
57+
) {
58+
return 0;
59+
}
60+
61+
return after;
62+
}
63+
64+
if (response.statusCode === 413) {
65+
return 0;
66+
}
67+
}
68+
69+
const noise = Math.random() * 100;
70+
return 2 ** (attemptCount - 1) * 1000 + noise;
71+
};
72+
73+
export default calculateRetryDelay;

0 commit comments

Comments
 (0)