Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

twitter-api-v2

Package Overview
Dependencies
Maintainers
1
Versions
68
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

twitter-api-v2 - npm Package Compare versions

Comparing version 1.10.0 to 1.10.1

dist/types/request-maker.mixin.types.d.ts

5

changelog.md

@@ -0,1 +1,6 @@

1.10.1
------
- Fix: Crash when a v2 paginator is empty and response does not contains a `.meta` property #177
- Fix: "Memory leak" when response are abruptly closed by Twitter or OS, because no close/error listener on response object was attributed
1.10.0

@@ -2,0 +7,0 @@ ------

14

dist/client-mixins/request-handler.helper.d.ts
/// <reference types="node" />
import type { IncomingMessage, ClientRequest } from 'http';
import TweetStream from '../stream/TweetStream';
import { ApiRequestError, ApiResponseError } from '../types';
import { ApiPartialResponseError, ApiRequestError, ApiResponseError } from '../types';
import type { ErrorV1, ErrorV2, TwitterRateLimit, TwitterResponse } from '../types';
import type { TRequestFullData, TRequestFullStreamData } from './request-maker.mixin';
import type { IncomingMessage, ClientRequest } from 'http';
import type { TRequestFullData, TRequestFullStreamData } from '../types/request-maker.mixin.types';
declare type TRequestReadyPayload = {

@@ -15,3 +15,3 @@ req: ClientRequest;

declare type TRequestRejecter = (error: ApiRequestError) => void;
declare type TResponseRejecter = (error: ApiResponseError) => void;
declare type TResponseRejecter = (error: ApiResponseError | ApiPartialResponseError) => void;
interface IBuildErrorParams {

@@ -26,2 +26,3 @@ res: IncomingMessage;

protected req: ClientRequest;
protected res: IncomingMessage;
protected responseData: string;

@@ -33,2 +34,3 @@ constructor(requestData: TRequestFullData | TRequestFullStreamData);

protected createRequestError(error: Error): ApiRequestError;
protected createPartialResponseError(error: Error, abortClose: boolean): ApiPartialResponseError;
protected formatV1Errors(errors: ErrorV1[]): string;

@@ -41,6 +43,8 @@ protected formatV2Error(error: ErrorV2): string;

protected classicResponseHandler(resolve: TResponseResolver<T>, reject: TResponseRejecter, res: IncomingMessage): void;
protected onResponseEndHandler(resolve: TResponseResolver<T>, reject: TResponseRejecter, res: IncomingMessage): void;
protected onResponseEndHandler(resolve: TResponseResolver<T>, reject: TResponseRejecter): void;
protected onResponseCloseHandler(resolve: TResponseResolver<T>, reject: TResponseRejecter): void;
protected streamResponseHandler(resolve: TReadyRequestResolver, reject: TResponseRejecter, res: IncomingMessage): void;
protected debugRequest(): void;
protected buildRequest(): void;
protected registerRequestEventDebugHandlers(req: ClientRequest): void;
makeRequest(): Promise<TwitterResponse<T>>;

@@ -47,0 +51,0 @@ makeRequestAsStream(): Promise<TweetStream<T>>;

@@ -46,2 +46,18 @@ "use strict";

}
createPartialResponseError(error, abortClose) {
const res = this.res;
let message = `Request failed with partial response with HTTP code ${res.statusCode}`;
if (abortClose) {
message += ' (connection abruptly closed)';
}
else {
message += ' (parse error)';
}
return new types_1.ApiPartialResponseError(message, {
request: this.req,
response: this.res,
responseError: error,
rawContent: this.responseData,
});
}
formatV1Errors(errors) {

@@ -90,7 +106,7 @@ return errors

else if (this.isFormEncodedEndpoint()) {
const response_form_entries = {};
const formEntries = {};
for (const [item, value] of new URLSearchParams(data)) {
response_form_entries[item] = value;
formEntries[item] = value;
}
data = response_form_entries;
data = formEntries;
}

@@ -100,2 +116,4 @@ return data;

requestErrorHandler(reject, requestError) {
var _a, _b;
(_b = (_a = this.requestData).requestEventDebugHandler) === null || _b === void 0 ? void 0 : _b.call(_a, 'request-error', { requestError });
reject(this.createRequestError(requestError));

@@ -108,17 +126,35 @@ this.req.removeAllListeners('timeout');

classicResponseHandler(resolve, reject, res) {
this.res = res;
// Register the response data
res.on('data', chunk => this.responseData += chunk);
res.on('end', this.onResponseEndHandler.bind(this, resolve, reject, res));
res.on('end', this.onResponseEndHandler.bind(this, resolve, reject));
res.on('close', this.onResponseCloseHandler.bind(this, resolve, reject));
// Debug handlers
if (this.requestData.requestEventDebugHandler) {
this.requestData.requestEventDebugHandler('response', { res });
res.on('aborted', error => this.requestData.requestEventDebugHandler('response-aborted', { error }));
res.on('error', error => this.requestData.requestEventDebugHandler('response-error', { error }));
res.on('close', () => this.requestData.requestEventDebugHandler('response-close', { data: this.responseData }));
res.on('end', () => this.requestData.requestEventDebugHandler('response-end'));
}
}
onResponseEndHandler(resolve, reject, res) {
onResponseEndHandler(resolve, reject) {
this.req.removeAllListeners('timeout');
const rateLimit = this.getRateLimitFromResponse(res);
const data = this.getParsedResponse(res);
const rateLimit = this.getRateLimitFromResponse(this.res);
let data;
try {
data = this.getParsedResponse(this.res);
}
catch (e) {
reject(this.createPartialResponseError(e, false));
return;
}
// Handle bad error codes
const code = res.statusCode;
const code = this.res.statusCode;
if (code >= 400) {
reject(this.createResponseError({ data, res, rateLimit, code }));
reject(this.createResponseError({ data, res: this.res, rateLimit, code }));
return;
}
if (settings_1.TwitterApiV2Settings.debug) {
settings_1.TwitterApiV2Settings.logger.log(`[${this.requestData.options.method} ${this.hrefPathname}]: Request succeeds with code ${res.statusCode}`);
settings_1.TwitterApiV2Settings.logger.log(`[${this.requestData.options.method} ${this.hrefPathname}]: Request succeeds with code ${this.res.statusCode}`);
settings_1.TwitterApiV2Settings.logger.log('Response body:', data);

@@ -128,6 +164,26 @@ }

data,
headers: res.headers,
headers: this.res.headers,
rateLimit,
});
}
onResponseCloseHandler(resolve, reject) {
this.req.removeAllListeners('timeout');
const res = this.res;
if (res.aborted) {
// Try to parse the request (?)
try {
this.getParsedResponse(this.res);
// Ok, try to resolve normally the request
return this.onResponseEndHandler(resolve, reject);
}
catch (e) {
// Parse error, just drop with content
return reject(this.createPartialResponseError(e, true));
}
}
if (!res.complete) {
return reject(this.createPartialResponseError(new Error('Response has been interrupted before response could be parsed.'), true));
}
// else: end has been called
}
streamResponseHandler(resolve, reject, res) {

@@ -173,2 +229,14 @@ const code = res.statusCode;

}
registerRequestEventDebugHandlers(req) {
req.on('abort', () => this.requestData.requestEventDebugHandler('abort'));
req.on('socket', socket => {
this.requestData.requestEventDebugHandler('socket', { socket });
socket.on('error', error => this.requestData.requestEventDebugHandler('socket-error', { socket, error }));
socket.on('connect', () => this.requestData.requestEventDebugHandler('socket-connect', { socket }));
socket.on('close', withError => this.requestData.requestEventDebugHandler('socket-close', { socket, withError }));
socket.on('end', () => this.requestData.requestEventDebugHandler('socket-end', { socket }));
socket.on('lookup', (...data) => this.requestData.requestEventDebugHandler('socket-lookup', { socket, data }));
socket.on('timeout', () => this.requestData.requestEventDebugHandler('socket-timeout', { socket }));
});
}
makeRequest() {

@@ -184,2 +252,6 @@ this.buildRequest();

}
// Debug handlers
if (this.requestData.requestEventDebugHandler) {
this.registerRequestEventDebugHandlers(req);
}
if (this.requestData.body) {

@@ -186,0 +258,0 @@ req.write(this.requestData.body);

/// <reference types="node" />
import { IClientSettings, TwitterRateLimit, TwitterResponse } from '../types';
import TweetStream from '../stream/TweetStream';
import type { RequestOptions } from 'https';
import OAuth1Helper from './oauth1.helper';
export declare type TRequestFullData = {
url: URL;
options: RequestOptions;
body?: any;
rateLimitSaver?: (rateLimit: TwitterRateLimit) => any;
};
export declare type TRequestFullStreamData = TRequestFullData & {
payloadIsError?: (data: any) => boolean;
};
export declare type TRequestQuery = Record<string, string | number | boolean | string[] | undefined>;
export declare type TRequestStringQuery = Record<string, string>;
export declare type TRequestBody = Record<string, any> | Buffer;
export declare type TBodyMode = 'json' | 'url' | 'form-data' | 'raw';
interface IWriteAuthHeadersArgs {
headers: Record<string, string>;
bodyInSignature: boolean;
url: URL;
method: string;
query: TRequestQuery;
body: TRequestBody;
}
export interface IGetHttpRequestArgs {
url: string;
method: string;
query?: TRequestQuery;
/** The URL parameters, if you specify an endpoint with `:id`, for example. */
params?: TRequestQuery;
body?: TRequestBody;
headers?: Record<string, string>;
forceBodyMode?: TBodyMode;
enableAuth?: boolean;
enableRateLimitSave?: boolean;
timeout?: number;
}
export interface IGetStreamRequestArgs {
payloadIsError?: (data: any) => boolean;
autoConnect?: boolean;
}
interface IGetStreamRequestArgsAsync {
payloadIsError?: (data: any) => boolean;
autoConnect?: true;
}
interface IGetStreamRequestArgsSync {
payloadIsError?: (data: any) => boolean;
autoConnect: false;
}
export declare type TCustomizableRequestArgs = Pick<IGetHttpRequestArgs, 'headers' | 'params' | 'forceBodyMode' | 'enableAuth' | 'enableRateLimitSave'>;
import type { IGetHttpRequestArgs, IGetStreamRequestArgs, IGetStreamRequestArgsAsync, IGetStreamRequestArgsSync, IWriteAuthHeadersArgs } from '../types/request-maker.mixin.types';
export declare abstract class ClientRequestMaker {

@@ -94,2 +47,1 @@ protected _bearerToken?: string;

}
export {};

@@ -39,2 +39,3 @@ "use strict";

rateLimitSaver: enableRateLimitSave ? this.saveRateLimit.bind(this, args.rawUrl) : undefined,
requestEventDebugHandler: requestParams.requestEventDebugHandler,
})

@@ -41,0 +42,0 @@ .makeRequest();

/// <reference types="node" />
import type { RequestOptions } from 'https';
import type { TBodyMode, TRequestBody, TRequestQuery, TRequestStringQuery } from './request-maker.mixin';
import type { TBodyMode, TRequestBody, TRequestQuery, TRequestStringQuery } from '../types/request-maker.mixin.types';
export declare class RequestParamHelpers {

@@ -5,0 +5,0 @@ static readonly JSON_1_1_ENDPOINTS: Set<string>;

import type { IClientSettings, TClientTokens, TwitterApiBasicAuth, TwitterApiOAuth2Init, TwitterApiTokens, TwitterRateLimit, TwitterResponse, UserV1, UserV2Result } from './types';
import { ClientRequestMaker, TCustomizableRequestArgs, TRequestBody, TRequestQuery } from './client-mixins/request-maker.mixin';
import { ClientRequestMaker } from './client-mixins/request-maker.mixin';
import TweetStream from './stream/TweetStream';
import { SharedPromise } from './helpers';
import type { TCustomizableRequestArgs, TRequestBody, TRequestQuery } from './types/request-maker.mixin.types';
export declare type TGetClientRequestArgs = TCustomizableRequestArgs & {

@@ -6,0 +7,0 @@ prefix?: string;

@@ -11,2 +11,1 @@ export { default as default } from './client';

export * from './settings';
export { IGetHttpRequestArgs } from './client-mixins/request-maker.mixin';

@@ -109,5 +109,6 @@ "use strict";

canFetchNextPage(result) {
return !!result.meta.next_token;
var _a;
return !!((_a = result.meta) === null || _a === void 0 ? void 0 : _a.next_token);
}
}
exports.TimelineV2Paginator = TimelineV2Paginator;
/// <reference types="node" />
import { EventEmitter } from 'events';
import type { IncomingMessage, ClientRequest } from 'http';
import { TRequestFullStreamData } from '../client-mixins/request-maker.mixin';
import { ETwitterStreamEvent } from '../types';
import { TRequestFullStreamData } from '../types/request-maker.mixin.types';
import TweetStreamParser from './TweetStreamParser';

@@ -7,0 +7,0 @@ interface ITweetStreamError {

@@ -46,2 +46,3 @@ /// <reference types="node" />

Request = "request",
PartialResponse = "partial-response",
Response = "response"

@@ -61,3 +62,3 @@ }

declare abstract class ApiError extends Error {
abstract type: ETwitterApiError.Request | ETwitterApiError.Response;
abstract type: ETwitterApiError.Request | ETwitterApiError.Response | ETwitterApiError.PartialResponse;
abstract request: ClientRequest;

@@ -81,2 +82,21 @@ error: true;

}
interface IBuildApiPartialRequestError {
readonly request: ClientRequest;
readonly response: IncomingMessage;
readonly rawContent: string;
responseError: Error;
}
export declare class ApiPartialResponseError extends ApiError implements IBuildApiPartialRequestError {
protected _options: any;
type: ETwitterApiError.PartialResponse;
constructor(message: string, options: IBuildApiPartialRequestError);
get request(): ClientRequest;
get response(): IncomingMessage;
get responseError(): Error;
get rawContent(): string;
toJSON(): {
type: ETwitterApiError.PartialResponse;
error: Error;
};
}
interface IBuildApiResponseError {

@@ -83,0 +103,0 @@ code: number;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EApiV2ErrorCode = exports.EApiV1ErrorCode = exports.ApiResponseError = exports.ApiRequestError = exports.ETwitterApiError = void 0;
exports.EApiV2ErrorCode = exports.EApiV1ErrorCode = exports.ApiResponseError = exports.ApiPartialResponseError = exports.ApiRequestError = exports.ETwitterApiError = void 0;
var ETwitterApiError;
(function (ETwitterApiError) {
ETwitterApiError["Request"] = "request";
ETwitterApiError["PartialResponse"] = "partial-response";
ETwitterApiError["Response"] = "response";

@@ -38,2 +39,30 @@ })(ETwitterApiError = exports.ETwitterApiError || (exports.ETwitterApiError = {}));

exports.ApiRequestError = ApiRequestError;
class ApiPartialResponseError extends ApiError {
constructor(message, options) {
super(message);
this.type = ETwitterApiError.PartialResponse;
Error.captureStackTrace(this, this.constructor);
// Do not show on Node stack trace
Object.defineProperty(this, '_options', { value: options });
}
get request() {
return this._options.request;
}
get response() {
return this._options.response;
}
get responseError() {
return this._options.responseError;
}
get rawContent() {
return this._options.rawContent;
}
toJSON() {
return {
type: this.type,
error: this.responseError,
};
}
}
exports.ApiPartialResponseError = ApiPartialResponseError;
class ApiResponseError extends ApiError {

@@ -40,0 +69,0 @@ constructor(message, options) {

@@ -7,1 +7,2 @@ export * from './v1';

export * from './auth.types';
export { IGetHttpRequestArgs } from './request-maker.mixin.types';
{
"name": "twitter-api-v2",
"version": "1.10.0",
"version": "1.10.1",
"description": "Strongly typed, full-featured, light, versatile yet powerful Twitter API v1.1 and v2 client for Node.js.",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

@@ -39,6 +39,6 @@ # Twitter API v2

Here's is a quick example of usage:
Here's a quick example of usage:
```ts
import TwitterApi from 'twitter-api-v2';
import { TwitterApi } from 'twitter-api-v2';

@@ -45,0 +45,0 @@ // Instanciate with desired auth type (here's Bearer v2 auth)

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc