Skip to content

Commit

Permalink
fix: export types independent of @types/request (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
JustinBeckwith authored Jul 14, 2019
1 parent 34f8075 commit fbe2b77
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 31 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
"@types/mocha": "^5.2.5",
"@types/nock": "^10.0.0",
"@types/node-fetch": "^2.1.2",
"@types/request": "^2.47.1",
"@types/uuid": "^3.4.4",
"c8": "^5.0.1",
"codecov": "^3.1.0",
Expand Down
89 changes: 67 additions & 22 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,56 @@
import * as r from 'request'; // Only for type declarations
import fetch, * as f from 'node-fetch';
import {PassThrough} from 'stream';
import {PassThrough, Readable, Duplex} from 'stream';
import * as uuid from 'uuid';

export interface CoreOptions {
method?: 'GET' | 'POST' | 'PUT' | 'HEAD' | 'OPTIONS';
timeout?: number;
gzip?: boolean;
// tslint:disable-next-line no-any
json?: any;
headers?: Headers;
body?: string | {};
useQuerystring?: boolean;
// tslint:disable-next-line no-any
qs?: any;
proxy?: string;
multipart?: RequestPart[];
forever?: boolean;
}

export interface OptionsWithUri extends CoreOptions {
uri: string;
}

export interface OptionsWithUrl extends CoreOptions {
url: string;
}

export type Options = OptionsWithUri | OptionsWithUrl;

export interface Request extends Duplex {
headers: Headers;
href?: string;
}

// tslint:disable-next-line no-any
export interface Response<T = any> {
statusCode: number;
headers: Headers;
body: T;
request: Request;
statusMessage?: string;
}

export interface RequestPart {
body: string | Readable;
}

// tslint:disable-next-line no-any
export interface RequestCallback<T = any> {
(err: Error | null, response: Response, body?: T): void;
}

// tslint:disable-next-line variable-name
const HttpsProxyAgent = require('https-proxy-agent');

Expand All @@ -19,7 +67,7 @@ interface Headers {
* @private
* @param reqOpts Request options
*/
function requestToFetchOptions(reqOpts: r.Options) {
function requestToFetchOptions(reqOpts: Options) {
const options: f.RequestInit = {
method: reqOpts.method || 'GET',
...(reqOpts.timeout && {timeout: reqOpts.timeout}),
Expand All @@ -43,8 +91,8 @@ function requestToFetchOptions(reqOpts: r.Options) {

options.headers = reqOpts.headers as Headers;

let uri = ((reqOpts as r.OptionsWithUri).uri ||
(reqOpts as r.OptionsWithUrl).url) as string;
let uri = ((reqOpts as OptionsWithUri).uri ||
(reqOpts as OptionsWithUrl).url) as string;
if (reqOpts.useQuerystring === true || typeof reqOpts.qs === 'object') {
const qs = require('querystring');
const params = qs.stringify(reqOpts.qs);
Expand All @@ -66,8 +114,8 @@ function requestToFetchOptions(reqOpts: r.Options) {
* @param res The Fetch response
* @returns A `request` response object
*/
function fetchToRequestResponse(opts: r.Options, res: f.Response) {
const request = {} as r.Request;
function fetchToRequestResponse(opts: Options, res: f.Response) {
const request = {} as Request;
request.headers = opts.headers || {};
request.href = res.url;
// headers need to be converted from a map to an obj
Expand All @@ -83,7 +131,7 @@ function fetchToRequestResponse(opts: r.Options, res: f.Response) {
toJSON: () => ({headers: resHeaders}),
});

return response as r.Response;
return response as Response;
}

/**
Expand All @@ -92,7 +140,7 @@ function fetchToRequestResponse(opts: r.Options, res: f.Response) {
* @param boundary
* @param multipart
*/
function createMultipartStream(boundary: string, multipart: r.RequestPart[]) {
function createMultipartStream(boundary: string, multipart: RequestPart[]) {
const finale = `--${boundary}--`;
const stream: PassThrough = new PassThrough();

Expand All @@ -119,15 +167,15 @@ function createMultipartStream(boundary: string, multipart: r.RequestPart[]) {
return stream;
}

function teenyRequest(reqOpts: r.Options): r.Request;
function teenyRequest(reqOpts: r.Options, callback: r.RequestCallback): void;
function teenyRequest(reqOpts: Options): Request;
function teenyRequest(reqOpts: Options, callback: RequestCallback): void;
function teenyRequest(
reqOpts: r.Options,
callback?: r.RequestCallback
): r.Request | void {
reqOpts: Options,
callback?: RequestCallback
): Request | void {
const {uri, options} = requestToFetchOptions(reqOpts);

const multipart = reqOpts.multipart as r.RequestPart[];
const multipart = reqOpts.multipart as RequestPart[];
if (reqOpts.multipart && multipart.length === 2) {
if (!callback) {
console.log('Error, multipart without callback not implemented.');
Expand Down Expand Up @@ -180,7 +228,7 @@ function teenyRequest(

if (callback === undefined) {
// Stream mode
const requestStream = new PassThrough();
const requestStream = new Duplex();
options.compress = false;
fetch(uri, options).then(
res => {
Expand All @@ -201,7 +249,7 @@ function teenyRequest(
// fetch doesn't supply the raw HTTP stream, instead it
// returns a PassThrough piped from the HTTP response
// stream.
return (requestStream as {}) as r.Request;
return requestStream as Request;
}
// GET or POST with callback
fetch(uri, options).then(
Expand Down Expand Up @@ -248,11 +296,8 @@ function teenyRequest(
return;
}

teenyRequest.defaults = (defaults: r.OptionalUriUrl) => {
return (
reqOpts: r.Options,
callback?: r.RequestCallback
): r.Request | void => {
teenyRequest.defaults = (defaults: CoreOptions) => {
return (reqOpts: Options, callback?: RequestCallback): Request | void => {
const opts = {...defaults, ...reqOpts};
if (callback === undefined) {
return teenyRequest(opts);
Expand Down
9 changes: 1 addition & 8 deletions test/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import * as assert from 'assert';
import * as nock from 'nock';
import * as request from 'request';
import {Readable} from 'stream';

import {teenyRequest} from '../src';

nock.disableNetConnect();
Expand Down Expand Up @@ -83,7 +81,7 @@ describe('teeny', () => {
.reply(200, '🚨', {'content-type': 'application/json'});
teenyRequest({uri}, err => {
assert.ok(err);
assert.ok(err.message.match(/^invalid json response body/));
assert.ok(err!.message.match(/^invalid json response body/));
scope.done();
done();
});
Expand All @@ -102,9 +100,4 @@ describe('teeny', () => {
done();
});
});

it('should be castable to `Request`', () => {
const r = teenyRequest as typeof request;
assert.ok(r);
});
});

0 comments on commit fbe2b77

Please sign in to comment.