Skip to content

Commit

Permalink
Improve internal types
Browse files Browse the repository at this point in the history
  • Loading branch information
szmarczak committed Dec 19, 2019
1 parent fa60b5f commit 1d012a5
Show file tree
Hide file tree
Showing 13 changed files with 59 additions and 52 deletions.
17 changes: 8 additions & 9 deletions source/as-promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import is from '@sindresorhus/is';
import {ParseError, ReadError, HTTPError} from './errors';
import {normalizeArguments, mergeOptions} from './normalize-arguments';
import requestAsEventEmitter, {proxyEvents} from './request-as-event-emitter';
import {CancelableRequest, GeneralError, NormalizedOptions, Response} from './utils/types';
import {CancelableRequest, GeneralError, NormalizedOptions, Response} from './types';

const parseBody = (body: Buffer, responseType: NormalizedOptions['responseType'], encoding: NormalizedOptions['encoding']): unknown => {
if (responseType === 'json') {
Expand All @@ -27,7 +27,6 @@ export default function asPromise<T>(options: NormalizedOptions): CancelableRequ
const proxy = new EventEmitter();
let body: Buffer;

// @ts-ignore `.json()`, `.buffer()` and `.text()` are added later
const promise = new PCancelable<Response | Response['body']>((resolve, reject, onCancel) => {
const emitter = requestAsEventEmitter(options);
onCancel(emitter.abort);
Expand Down Expand Up @@ -84,10 +83,10 @@ export default function asPromise<T>(options: NormalizedOptions): CancelableRequ

try {
for (const [index, hook] of options.hooks.afterResponse.entries()) {
// @ts-ignore Promise is not assignable to CancelableRequest
// @ts-ignore TS doesn't notice that CancelableRequest is a Promise
// eslint-disable-next-line no-await-in-loop
response = await hook(response, async (updatedOptions: NormalizedOptions) => {
updatedOptions = normalizeArguments(mergeOptions(options, {
response = await hook(response, async (updatedOptions): CancelableRequest<Response> => {
const typedOptions = normalizeArguments(mergeOptions(options, {
...updatedOptions,
retry: {
calculateDelay: () => 0
Expand All @@ -98,21 +97,21 @@ export default function asPromise<T>(options: NormalizedOptions): CancelableRequ

// Remove any further hooks for that request, because we'll call them anyway.
// The loop continues. We don't want duplicates (asPromise recursion).
updatedOptions.hooks.afterResponse = options.hooks.afterResponse.slice(0, index);
typedOptions.hooks.afterResponse = options.hooks.afterResponse.slice(0, index);

for (const hook of options.hooks.beforeRetry) {
// eslint-disable-next-line no-await-in-loop
await hook(updatedOptions);
await hook(typedOptions);
}

const promise = asPromise(updatedOptions);
const promise = asPromise(typedOptions);

onCancel(() => {
promise.catch(() => {});
promise.cancel();
});

return promise;
return promise as unknown as CancelableRequest<Response>;
});
}
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion source/as-stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {IncomingMessage, ServerResponse} from 'http';
import {Duplex as DuplexStream, PassThrough as PassThroughStream} from 'stream';
import {HTTPError, ReadError} from './errors';
import requestAsEventEmitter, {proxyEvents} from './request-as-event-emitter';
import {GeneralError, GotEvents, NormalizedOptions, Response} from './utils/types';
import {GeneralError, GotEvents, NormalizedOptions, Response} from './types';

export class ProxyStream<T = unknown> extends DuplexStream implements GotEvents<ProxyStream<T>> {
isFromCache?: boolean;
Expand Down
2 changes: 1 addition & 1 deletion source/calculate-retry-delay.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import is from '@sindresorhus/is';
import {HTTPError, ParseError, MaxRedirectsError} from './errors';
import {RetryFunction, RetryObject} from './utils/types';
import {RetryFunction, RetryObject} from './types';

const retryAfterStatusCodes: ReadonlySet<number> = new Set([413, 429, 503]);

Expand Down
12 changes: 4 additions & 8 deletions source/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
Options,
Response,
URLOrOptions
} from './utils/types';
} from './types';

export type HTTPAlias =
| 'get'
Expand Down Expand Up @@ -90,7 +90,6 @@ export const defaultHandler: HandlerFunction = (options, next) => next(options);

const create = (defaults: Defaults): Got => {
// Proxy properties from next handlers
// @ts-ignore Internal use only.
defaults._rawHandlers = defaults.handlers;
defaults.handlers = defaults.handlers.map(fn => ((options, next) => {
// This will be assigned by assigning result
Expand All @@ -115,7 +114,6 @@ const create = (defaults: Defaults): Got => {
const iterateHandlers = (newOptions: NormalizedOptions): GotReturn => {
return defaults.handlers[iteration++](
newOptions,
// @ts-ignore TS doesn't know that it calls `getPromiseOrStream` at the end
iteration === defaults.handlers.length ? getPromiseOrStream : iterateHandlers
) as GotReturn;
};
Expand All @@ -136,15 +134,13 @@ const create = (defaults: Defaults): Got => {

got.extend = (...instancesOrOptions) => {
const optionsArray: Options[] = [defaults.options];
// @ts-ignore Internal use only.
let handlers: HandlerFunction[] = [...defaults._rawHandlers];
let handlers: HandlerFunction[] = [...defaults._rawHandlers!];
let mutableDefaults: boolean | undefined;

for (const value of instancesOrOptions) {
if (isGotInstance(value)) {
optionsArray.push(value.defaults.options);
// @ts-ignore Internal use only.
handlers.push(...value.defaults._rawHandlers);
handlers.push(...value.defaults._rawHandlers!);
mutableDefaults = value.defaults.mutableDefaults;
} else {
optionsArray.push(value);
Expand Down Expand Up @@ -174,7 +170,7 @@ const create = (defaults: Defaults): Got => {
got.stream = (url, options) => got(url, {...options, isStream: true});

for (const method of aliases) {
// @ts-ignore GotReturn<Response> does not equal GotReturn<T>
// @ts-ignore Cannot properly type a function with multiple definitions yet
got[method] = (url: URLOrOptions, options?: Options): GotReturn => got(url, {...options, method});
got.stream[method] = (url, options) => got.stream(url, {...options, method});
}
Expand Down
6 changes: 3 additions & 3 deletions source/errors.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import is from '@sindresorhus/is';
import {Timings} from '@szmarczak/http-timer';
import {TimeoutError as TimedOutError} from './utils/timed-out';
import {ErrorCode, Response, NormalizedOptions} from './utils/types';
import {Response, NormalizedOptions} from './types';

export class GotError extends Error {
code?: ErrorCode;
code?: string;
stack!: string;
declare readonly options: NormalizedOptions;

constructor(message: string, error: Partial<Error & {code?: ErrorCode}>, options: NormalizedOptions) {
constructor(message: string, error: Partial<Error & {code?: string}>, options: NormalizedOptions) {
super(message);
Error.captureStackTrace(this, this.constructor);
this.name = 'GotError';
Expand Down
2 changes: 1 addition & 1 deletion source/get-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import stream = require('stream');
import {IncomingMessage} from 'http';
import {promisify} from 'util';
import {createProgressStream} from './progress';
import {NormalizedOptions} from './utils/types';
import {NormalizedOptions} from './types';

const pipeline = promisify(stream.pipeline);

Expand Down
4 changes: 2 additions & 2 deletions source/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import create, {defaultHandler} from './create';
import {Defaults} from './utils/types';
import {Defaults} from './types';

const defaults: Defaults = {
options: {
Expand Down Expand Up @@ -79,7 +79,7 @@ module.exports = got;
module.exports.default = got;

// Export types
export * from './utils/types';
export * from './types';

export {
Got,
Expand Down
2 changes: 1 addition & 1 deletion source/known-hook-events.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {CancelableRequest, GeneralError, NormalizedOptions, Options, Response} from './utils/types';
import {CancelableRequest, GeneralError, NormalizedOptions, Options, Response} from './types';

/**
Called with plain request options, right before their normalization. This is especially useful in conjunction with `got.extend()` when the input needs custom handling.
Expand Down
7 changes: 3 additions & 4 deletions source/normalize-arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
Options,
RequestFunction,
URLOrOptions
} from './utils/types';
} from './types';

// `preNormalizeArguments` normalizes these options: `headers`, `prefixUrl`, `hooks`, `timeout`, `retry` and `method`.
// `normalizeArguments` is *only* called on `got(...)`. It normalizes the URL and performs `mergeOptions(...)`.
Expand Down Expand Up @@ -132,7 +132,6 @@ export const preNormalizeArguments = (options: Options, defaults?: NormalizedOpt
}

if (options.retry.maxRetryAfter === undefined) {
// @ts-ignore We assign if it is undefined, so this IS correct
options.retry.maxRetryAfter = Math.min(
...[options.timeout.request, options.timeout.connect].filter((n): n is number => !is.nullOrUndefined(n))
);
Expand All @@ -157,7 +156,7 @@ export const preNormalizeArguments = (options: Options, defaults?: NormalizedOpt
// Better memory management, so we don't have to generate a new object every time
if (options.cache) {
(options as NormalizedOptions).cacheableRequest = new CacheableRequest(
// @ts-ignore Types broke on infer
// @ts-ignore Cannot properly type a function with multiple definitions yet
(requestOptions, handler) => requestOptions.request(requestOptions, handler),
options.cache
);
Expand All @@ -170,7 +169,7 @@ export const preNormalizeArguments = (options: Options, defaults?: NormalizedOpt
// Horrible `tough-cookie` check
if (setCookie.length === 4 && getCookieString.length === 0) {
if (!Reflect.has(setCookie, promisify.custom)) {
// @ts-ignore We check for non-promisified setCookie, so this IS correct
// @ts-ignore TS is dumb.
setCookie = promisify(setCookie.bind(options.cookieJar));
getCookieString = promisify(getCookieString.bind(options.cookieJar));
}
Expand Down
7 changes: 3 additions & 4 deletions source/request-as-event-emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import getResponse from './get-response';
import {normalizeRequestArguments} from './normalize-arguments';
import {createProgressStream} from './progress';
import timedOut, {TimeoutError as TimedOutTimeoutError} from './utils/timed-out';
import {GeneralError, NormalizedOptions, Response} from './utils/types';
import {GeneralError, NormalizedOptions, Response} from './types';
import urlToOptions from './utils/url-to-options';

const setImmediateAsync = async (): Promise<void> => new Promise(resolve => setImmediate(resolve));
Expand Down Expand Up @@ -82,8 +82,7 @@ export default (options: NormalizedOptions): RequestAsEventEmitter => {
delete typedResponse.fromCache;

if (!typedResponse.isFromCache) {
// @ts-ignore Node.js typings haven't been updated yet
typedResponse.ip = response.socket.remoteAddress;
typedResponse.ip = response.socket.remoteAddress!;
}

const rawCookies = typedResponse.headers['set-cookie'];
Expand Down Expand Up @@ -226,7 +225,7 @@ export default (options: NormalizedOptions): RequestAsEventEmitter => {
...urlToOptions(options.url)
};

// @ts-ignore ResponseLike missing socket field, should be fixed upstream
// @ts-ignore `cacheable-request` has got invalid types
const cacheRequest = options.cacheableRequest!(httpOptions, handleResponse);

cacheRequest.once('error', (error: GeneralError) => {
Expand Down
18 changes: 4 additions & 14 deletions source/utils/types.ts → source/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import {Readable as ReadableStream} from 'stream';
import {Timings} from '@szmarczak/http-timer';
import CacheableLookup from 'cacheable-lookup';
import {Except, Merge, Promisable} from 'type-fest';
import {GotReturn} from '../create';
import {GotError, HTTPError, MaxRedirectsError, ParseError} from '../errors';
import {Hooks} from '../known-hook-events';
import {URLOptions} from './options-to-url';
import {GotReturn} from './create';
import {GotError, HTTPError, MaxRedirectsError, ParseError} from './errors';
import {Hooks} from './known-hook-events';
import {URLOptions} from './utils/options-to-url';

export type GeneralError = Error | GotError | HTTPError | MaxRedirectsError | ParseError;

Expand All @@ -34,16 +34,6 @@ export type Method =
| 'options'
| 'trace';

export type ErrorCode =
| 'ETIMEDOUT'
| 'ECONNRESET'
| 'EADDRINUSE'
| 'ECONNREFUSED'
| 'EPIPE'
| 'ENOTFOUND'
| 'ENETUNREACH'
| 'EAI_AGAIN';

export type ResponseType = 'json' | 'buffer' | 'text';

export interface Response<BodyType = unknown> extends http.IncomingMessage {
Expand Down
11 changes: 8 additions & 3 deletions source/utils/get-body-size.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import {ReadStream, stat} from 'fs';
import {promisify} from 'util';
import {ClientRequestArgs} from 'http';
import is from '@sindresorhus/is';
import isFormData from './is-form-data';
import {Options} from './types';

interface Options {
body?: unknown;
headers: ClientRequestArgs['headers'];
}

const statAsync = promisify(stat);

export default async (options: Options): Promise<number | undefined> => {
const {body, headers, isStream} = options;
const {body, headers} = options;

if (headers && 'content-length' in headers) {
return Number(headers['content-length']);
}

if (!body && !isStream) {
if (!body) {
return 0;
}

Expand Down
21 changes: 20 additions & 1 deletion source/utils/timed-out.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import net = require('net');
import {ClientRequest, IncomingMessage} from 'http';
import {Delays, ErrorCode} from './types';
import unhandler from './unhandle';

const reentry: unique symbol = Symbol('reentry');
Expand All @@ -12,6 +11,26 @@ interface TimedOutOptions {
protocol?: string;
}

export interface Delays {
lookup?: number;
connect?: number;
secureConnect?: number;
socket?: number;
response?: number;
send?: number;
request?: number;
}

export type ErrorCode =
| 'ETIMEDOUT'
| 'ECONNRESET'
| 'EADDRINUSE'
| 'ECONNREFUSED'
| 'EPIPE'
| 'ENOTFOUND'
| 'ENETUNREACH'
| 'EAI_AGAIN';

export class TimeoutError extends Error {
code: ErrorCode;

Expand Down

0 comments on commit 1d012a5

Please sign in to comment.