Add typescript definitions and tests

master
pro-src 2019-09-25 19:24:59 -05:00
parent db4af38f2d
commit 3c72e098ae
No known key found for this signature in database
GPG Key ID: 7D149CD9097DFAB4
5 changed files with 299 additions and 7 deletions

View File

@ -14,5 +14,19 @@
"VariableDeclarator": true
}
}]
}
},
"overrides": [
{
"files": ["*.ts"],
"parser": "@typescript-eslint/parser",
"extends": [
"standard",
"plugin:promise/recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"semi": [2, "always"]
}
}
]
}

95
errors.d.ts vendored Normal file
View File

@ -0,0 +1,95 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as rp from 'request-promise/errors';
import cloudscraper = require('.');
import http = require('http');
export interface RequestError extends rp.RequestError {
options: cloudscraper.Options;
errorType: 0;
}
export interface RequestErrorConstructor extends Error {
new(cause: any, options: cloudscraper.Options, response: http.IncomingMessage): RequestError;
(cause: any, options: cloudscraper.Options, response: http.IncomingMessage): RequestError;
prototype: RequestError;
}
export const RequestError: RequestErrorConstructor;
export interface CaptchaError extends rp.RequestError {
options: cloudscraper.Options;
errorType: 1;
}
export interface CaptchaErrorConstructor extends Error {
new(cause: any, options: cloudscraper.Options, response: http.IncomingMessage): RequestError;
(cause: any, options: cloudscraper.Options, response: http.IncomingMessage): RequestError;
prototype: CaptchaError;
}
export const CaptchaError: CaptchaErrorConstructor;
export interface CloudflareError extends rp.RequestError {
options: cloudscraper.Options;
errorType: 2 | 4;
}
export interface CloudflareErrorConstructor extends Error {
new(cause: any, options: cloudscraper.Options, response: http.IncomingMessage): RequestError;
(cause: any, options: cloudscraper.Options, response: http.IncomingMessage): RequestError;
prototype: CloudflareError;
}
export const CloudflareError: CloudflareErrorConstructor;
export interface ParserError extends rp.RequestError {
options: cloudscraper.Options;
errorType: 3;
}
export interface ParserErrorConstructor extends Error {
new(cause: any, options: cloudscraper.Options, response: http.IncomingMessage): RequestError;
(cause: any, options: cloudscraper.Options, response: http.IncomingMessage): RequestError;
prototype: ParserError;
}
export const ParserError: ParserErrorConstructor;
export interface StatusCodeError extends rp.RequestError {
options: cloudscraper.Options;
statusCode: number;
errorType: 5;
}
export interface StatusCodeErrorConstructor extends Error {
new(statusCode: number, body: any, options: cloudscraper.Options, response: http.IncomingMessage): StatusCodeError;
(statusCode: number, body: any, options: cloudscraper.Options, response: http.IncomingMessage): StatusCodeError;
prototype: StatusCodeError;
}
export const StatusCodeError: StatusCodeErrorConstructor;
export interface TransformError extends rp.RequestError {
options: cloudscraper.Options;
errorType: 6;
}
export interface TransformErrorConstructor extends Error {
new(cause: any, options: cloudscraper.Options, response: http.IncomingMessage): TransformError;
(cause: any, options: cloudscraper.Options, response: http.IncomingMessage): TransformError;
prototype: TransformError;
}
export const TransformError: TransformErrorConstructor;

89
index.d.ts vendored Normal file
View File

@ -0,0 +1,89 @@
import { Url } from 'url';
import http = require('http');
import https = require('https');
import Promise = require('bluebird');
import request = require('request');
import rp = require('request-promise');
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import errors = require('./errors');
declare namespace cloudscraper {
interface Cloudscraper extends rp.RequestPromise, BaseOptions {
cloudflareTimeout?: number;
realEncoding: string | null;
// Identify this request as a Cloudscraper request
cloudscraper: boolean;
}
interface Captcha {
submit(error?: Error): void;
url: string; // <- deprecated
siteKey: string;
uri: Url;
form: {
[key: string]: string;
// Secret form value
s: string;
};
}
interface Response extends request.Response {
isCloudflare?: boolean;
isHTML?: boolean;
isCaptcha?: boolean;
// JS Challenge
challenge?: string;
}
interface CaptchaResponse extends Response {
captcha: Captcha;
isCaptcha: true;
}
type Requester =
rp.RequestPromiseAPI
| request.RequestAPI<request.Request, request.CoreOptions, request.RequiredUriUrl>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type CaptchaHandler = (options: Options, response: CaptchaResponse, body?: any) => Promise<any> | void;
interface BaseOptions {
// The default export of either request or request-promise
requester?: Requester;
// Reduce Cloudflare's timeout to cloudflareMaxTimeout if it is excessive
cloudflareMaxTimeout?: number;
// Support only this max challenges in row. If CF returns more, throw an error
challengesToSolve?: number;
// Remove Cloudflare's email protection
decodeEmails?: boolean;
onCaptcha?: CaptchaHandler;
}
interface DefaultOptions extends Required<BaseOptions>, rp.RequestPromiseOptions {
// Override the parsed timeout
cloudflareTimeout?: number;
agentOptions?: (http.AgentOptions | https.AgentOptions) & {
ciphers?: string;
};
}
interface CoreOptions extends BaseOptions, rp.RequestPromiseOptions {
cloudflareTimeout?: number;
realEncoding?: string | null;
}
interface CloudscraperAPI extends request.RequestAPI<Cloudscraper, CoreOptions, request.RequiredUriUrl> {
defaultParams: DefaultOptions;
}
type OptionsWithUri = request.UriOptions & CoreOptions;
type OptionsWithUrl = request.UrlOptions & CoreOptions;
type Options = OptionsWithUri | OptionsWithUrl;
}
// eslint-disable-next-line no-redeclare
declare const cloudscraper: cloudscraper.CloudscraperAPI;
export = cloudscraper;

86
index.test-d.ts Normal file
View File

@ -0,0 +1,86 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { expectType } from 'tsd';
import { URL } from 'url';
import {
Options, Cloudscraper, CaptchaHandler, CoreOptions, DefaultOptions,
CaptchaResponse, Captcha
} from './index';
import Promise = require('bluebird');
import request = require('request');
import rp = require('request-promise');
import cloudscraper = require('./index');
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import errors = require('./errors');
const noop = (): void => {};
expectType<Options>({ uri: '' });
expectType<Options>({ url: '' });
expectType<Options>({ uri: '', requester: request });
expectType<Options>({ uri: '', requester: rp });
expectType<Cloudscraper>(cloudscraper({ uri: '' }));
expectType<Cloudscraper>(cloudscraper.get({ uri: '' }));
expectType<Cloudscraper>(cloudscraper.post({ uri: '' }));
expectType<Cloudscraper>(cloudscraper.put({ uri: '' }));
expectType<Cloudscraper>(cloudscraper.delete({ uri: '' }));
expectType<Cloudscraper>(cloudscraper.del({ uri: '' }));
expectType<Cloudscraper>(cloudscraper.head({ uri: '' }));
expectType<Cloudscraper>(cloudscraper.patch({ uri: '' }));
expectType<Cloudscraper>(cloudscraper(''));
expectType<Cloudscraper>(cloudscraper.get(''));
expectType<Cloudscraper>(cloudscraper.post(''));
expectType<Cloudscraper>(cloudscraper.put(''));
expectType<Cloudscraper>(cloudscraper.delete(''));
expectType<Cloudscraper>(cloudscraper.del(''));
expectType<Cloudscraper>(cloudscraper.head(''));
expectType<Cloudscraper>(cloudscraper.patch(''));
// eslint-disable-next-line promise/always-return
expectType<Promise<any>>(cloudscraper.get({ uri: '' }).then(noop));
expectType<Promise<any>>(cloudscraper.get({ uri: '' }).catch(noop));
expectType<Promise<any>>(cloudscraper.get({ uri: '' }).finally(noop));
expectType<Promise<any>>(cloudscraper.get({ uri: '' }).promise());
expectType<void>(cloudscraper.get({ uri: '' }).cancel());
expectType<CaptchaHandler>((options: Options, response: CaptchaResponse) => {
expectType<Options>(options);
expectType<CaptchaResponse>(response);
const { captcha, isCaptcha } = response;
expectType<Captcha>(captcha);
expectType<true>(isCaptcha);
expectType<Captcha>({
url: '', // <- deprecated
uri: new URL(''),
siteKey: '',
submit: captcha.submit,
form: { s: '' }
});
captcha.submit();
});
expectType<DefaultOptions>(cloudscraper.defaultParams);
expectType<DefaultOptions>({
requester: request,
cloudflareMaxTimeout: 0,
challengesToSolve: 0,
decodeEmails: false,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onCaptcha: (options: Options, response: CaptchaResponse) => {}
});
expectType<CoreOptions>({
requester: request,
cloudflareMaxTimeout: 0,
challengesToSolve: 0,
decodeEmails: false,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onCaptcha: (options: Options, response: CaptchaResponse) => {},
realEncoding: 'utf-8'
});

View File

@ -9,12 +9,15 @@
"files": [
"lib/",
"index.js",
"errors.js"
"index.d.ts",
"errors.js",
"errors.d.ts"
],
"scripts": {
"test": "npm run lint && nyc --reporter=html --reporter=text mocha",
"test": "npm run lint && npm run test:typescript && nyc --reporter=html --reporter=text mocha",
"test:typescript": "tsc *.ts --noEmit && tsd",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"lint": "eslint --ext .json --ext .js ."
"lint": "eslint --ext .json --ext .js --ext .ts ."
},
"repository": {
"type": "git",
@ -41,6 +44,9 @@
"request-promise": "^4.2.4"
},
"devDependencies": {
"@types/request-promise": "^4.1.44",
"@typescript-eslint/eslint-plugin": "^2.3.1",
"@typescript-eslint/parser": "^2.3.1",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"coveralls": "^3.0.3",
@ -55,10 +61,12 @@
"mocha": "^6.1.1",
"nyc": "^14.0.0",
"sinon": "^7.2.4",
"sinon-chai": "^3.3.0"
"sinon-chai": "^3.3.0",
"tsd": "^0.8.0",
"typescript": "^3.6.3"
},
"peerDependencies": {
"request": "^2.88.0",
"brotli": "^1.3.2"
"brotli": "^1.3.2",
"request": "^2.88.0"
}
}