This commit is contained in:
Yamozha
2021-04-02 02:24:13 +03:00
parent c23950b545
commit 7256d79e2c
31493 changed files with 3036630 additions and 0 deletions

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
import type {TurboModule} from '../TurboModule/RCTExport';
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+connect: (
url: string,
protocols: ?Array<string>,
options: {|headers?: Object|},
socketID: number,
) => void;
+send: (message: string, forSocketID: number) => void;
+sendBinary: (base64String: string, forSocketID: number) => void;
+ping: (socketID: number) => void;
+close: (code: number, reason: string, socketID: number) => void;
// RCTEventEmitter
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'WebSocketModule',
): Spec);

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTDefines.h>
#if RCT_DEV // Only supported in dev mode
@class RCTReconnectingWebSocket;
@protocol RCTReconnectingWebSocketDelegate
- (void)reconnectingWebSocketDidOpen:(RCTReconnectingWebSocket *)webSocket;
- (void)reconnectingWebSocket:(RCTReconnectingWebSocket *)webSocket didReceiveMessage:(id)message;
/** Sent when the socket has closed due to error or clean shutdown. An automatic reconnect will start shortly. */
- (void)reconnectingWebSocketDidClose:(RCTReconnectingWebSocket *)webSocket;
@end
@interface RCTReconnectingWebSocket : NSObject
/** Delegate will be messaged on the given queue (required). */
- (instancetype)initWithURL:(NSURL *)url queue:(dispatch_queue_t)queue;
@property (nonatomic, weak) id<RCTReconnectingWebSocketDelegate> delegate;
- (void)send:(id)data;
- (void)start;
- (void)stop;
- (instancetype)initWithURL:(NSURL *)url __deprecated_msg("Use initWithURL:queue: instead");
/** @brief Must be set before -start to have effect */
@property (nonatomic, strong) dispatch_queue_t delegateDispatchQueue __deprecated_msg("Use initWithURL:queue: instead");
@end
#endif

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTReconnectingWebSocket.h>
#import <React/RCTConvert.h>
#import <React/RCTDefines.h>
#import <React/RCTSRWebSocket.h>
#if RCT_DEV // Only supported in dev mode
@interface RCTReconnectingWebSocket () <RCTSRWebSocketDelegate>
@end
@implementation RCTReconnectingWebSocket {
NSURL *_url;
RCTSRWebSocket *_socket;
BOOL _stopped;
}
- (instancetype)initWithURL:(NSURL *)url queue:(dispatch_queue_t)queue
{
if (self = [super init]) {
_url = url;
_delegateDispatchQueue = queue;
}
return self;
}
- (instancetype)initWithURL:(NSURL *)url
{
return [self initWithURL:url queue:dispatch_get_main_queue()];
}
- (void)send:(id)data
{
[_socket send:data];
}
- (void)start
{
[self stop];
_stopped = NO;
_socket = [[RCTSRWebSocket alloc] initWithURL:_url];
_socket.delegate = self;
[_socket setDelegateDispatchQueue:_delegateDispatchQueue];
[_socket open];
}
- (void)stop
{
_stopped = YES;
_socket.delegate = nil;
[_socket closeWithCode:1000 reason:@"Invalidated"];
_socket = nil;
}
- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message
{
[_delegate reconnectingWebSocket:self didReceiveMessage:message];
}
- (void)reconnect
{
if (_stopped) {
return;
}
__weak RCTSRWebSocket *socket = _socket;
__weak __typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf start];
if (!socket) {
[weakSelf reconnect];
}
});
}
- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket
{
[_delegate reconnectingWebSocketDidOpen:self];
}
- (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error
{
[_delegate reconnectingWebSocketDidClose:self];
if ([error code] != ECONNREFUSED) {
[self reconnect];
}
}
- (void)webSocket:(RCTSRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean
{
[_delegate reconnectingWebSocketDidClose:self];
[self reconnect];
}
@end
#endif

View File

@ -0,0 +1,132 @@
//
// Copyright 2012 Square Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#import <Security/SecCertificate.h>
typedef NS_ENUM(unsigned int, RCTSRReadyState) {
RCTSR_CONNECTING = 0,
RCTSR_OPEN = 1,
RCTSR_CLOSING = 2,
RCTSR_CLOSED = 3,
};
typedef NS_ENUM(NSInteger, RCTSRStatusCode) {
RCTSRStatusCodeNormal = 1000,
RCTSRStatusCodeGoingAway = 1001,
RCTSRStatusCodeProtocolError = 1002,
RCTSRStatusCodeUnhandledType = 1003,
// 1004 reserved.
RCTSRStatusNoStatusReceived = 1005,
// 1004-1006 reserved.
RCTSRStatusCodeInvalidUTF8 = 1007,
RCTSRStatusCodePolicyViolated = 1008,
RCTSRStatusCodeMessageTooBig = 1009,
};
@class RCTSRWebSocket;
extern NSString *const RCTSRWebSocketErrorDomain;
extern NSString *const RCTSRHTTPResponseErrorKey;
#pragma mark - RCTSRWebSocketDelegate
@protocol RCTSRWebSocketDelegate;
#pragma mark - RCTSRWebSocket
@interface RCTSRWebSocket : NSObject <NSStreamDelegate>
@property (nonatomic, weak) id<RCTSRWebSocketDelegate> delegate;
@property (nonatomic, readonly) RCTSRReadyState readyState;
@property (nonatomic, readonly, strong) NSURL *url;
// This returns the negotiated protocol.
// It will be nil until after the handshake completes.
@property (nonatomic, readonly, copy) NSString *protocol;
// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol.
- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray<NSString *> *)protocols NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithURLRequest:(NSURLRequest *)request;
// Some helper constructors.
- (instancetype)initWithURL:(NSURL *)url protocols:(NSArray<NSString *> *)protocols;
- (instancetype)initWithURL:(NSURL *)url;
// Delegate queue will be dispatch_main_queue by default.
// You cannot set both OperationQueue and dispatch_queue.
- (void)setDelegateOperationQueue:(NSOperationQueue *)queue;
- (void)setDelegateDispatchQueue:(dispatch_queue_t)queue;
// By default, it will schedule itself on +[NSRunLoop RCTSR_networkRunLoop] using defaultModes.
- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
// RCTSRWebSockets are intended for one-time-use only. Open should be called once and only once.
- (void)open;
- (void)close;
- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason;
// Send a UTF8 String or Data.
- (void)send:(id)data;
// Send Data (can be nil) in a ping message.
- (void)sendPing:(NSData *)data;
@end
#pragma mark - RCTSRWebSocketDelegate
@protocol RCTSRWebSocketDelegate <NSObject>
// message will either be an NSString if the server is using text
// or NSData if the server is using binary.
- (void)webSocket:(RCTSRWebSocket *)webSocket didReceiveMessage:(id)message;
@optional
- (void)webSocketDidOpen:(RCTSRWebSocket *)webSocket;
- (void)webSocket:(RCTSRWebSocket *)webSocket didFailWithError:(NSError *)error;
- (void)webSocket:(RCTSRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
- (void)webSocket:(RCTSRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload;
@end
#pragma mark - NSURLRequest (CertificateAdditions)
@interface NSURLRequest (CertificateAdditions)
@property (nonatomic, readonly, copy) NSArray *RCTSR_SSLPinnedCertificates;
@end
#pragma mark - NSMutableURLRequest (CertificateAdditions)
@interface NSMutableURLRequest (CertificateAdditions)
@property (nonatomic, copy) NSArray *RCTSR_SSLPinnedCertificates;
@end
#pragma mark - NSRunLoop (RCTSRWebSocket)
@interface NSRunLoop (RCTSRWebSocket)
+ (NSRunLoop *)RCTSR_networkRunLoop;
@end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,277 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
const Blob = require('../Blob/Blob');
const BlobManager = require('../Blob/BlobManager');
const EventTarget = require('event-target-shim');
const NativeEventEmitter = require('../EventEmitter/NativeEventEmitter');
const WebSocketEvent = require('./WebSocketEvent');
const base64 = require('base64-js');
const binaryToBase64 = require('../Utilities/binaryToBase64');
const invariant = require('invariant');
import type EventSubscription from '../vendor/emitter/EventSubscription';
import NativeWebSocketModule from './NativeWebSocketModule';
type ArrayBufferView =
| Int8Array
| Uint8Array
| Uint8ClampedArray
| Int16Array
| Uint16Array
| Int32Array
| Uint32Array
| Float32Array
| Float64Array
| DataView;
type BinaryType = 'blob' | 'arraybuffer';
const CONNECTING = 0;
const OPEN = 1;
const CLOSING = 2;
const CLOSED = 3;
const CLOSE_NORMAL = 1000;
const WEBSOCKET_EVENTS = ['close', 'error', 'message', 'open'];
let nextWebSocketId = 0;
/**
* Browser-compatible WebSockets implementation.
*
* See https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
* See https://github.com/websockets/ws
*/
class WebSocket extends (EventTarget(...WEBSOCKET_EVENTS): any) {
static CONNECTING: number = CONNECTING;
static OPEN: number = OPEN;
static CLOSING: number = CLOSING;
static CLOSED: number = CLOSED;
CONNECTING: number = CONNECTING;
OPEN: number = OPEN;
CLOSING: number = CLOSING;
CLOSED: number = CLOSED;
_socketId: number;
_eventEmitter: NativeEventEmitter;
_subscriptions: Array<EventSubscription>;
_binaryType: ?BinaryType;
onclose: ?Function;
onerror: ?Function;
onmessage: ?Function;
onopen: ?Function;
bufferedAmount: number;
extension: ?string;
protocol: ?string;
readyState: number = CONNECTING;
url: ?string;
constructor(
url: string,
protocols: ?string | ?Array<string>,
options: ?{headers?: {origin?: string, ...}, ...},
) {
super();
if (typeof protocols === 'string') {
protocols = [protocols];
}
const {headers = {}, ...unrecognized} = options || {};
// Preserve deprecated backwards compatibility for the 'origin' option
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.68 was deployed. To see the error delete this
* comment and run Flow. */
if (unrecognized && typeof unrecognized.origin === 'string') {
console.warn(
'Specifying `origin` as a WebSocket connection option is deprecated. Include it under `headers` instead.',
);
/* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This
* comment suppresses an error found when Flow v0.54 was deployed. To see
* the error delete this comment and run Flow. */
headers.origin = unrecognized.origin;
/* $FlowFixMe(>=0.54.0 site=react_native_fb,react_native_oss) This
* comment suppresses an error found when Flow v0.54 was deployed. To see
* the error delete this comment and run Flow. */
delete unrecognized.origin;
}
// Warn about and discard anything else
if (Object.keys(unrecognized).length > 0) {
console.warn(
'Unrecognized WebSocket connection option(s) `' +
Object.keys(unrecognized).join('`, `') +
'`. ' +
'Did you mean to put these under `headers`?',
);
}
if (!Array.isArray(protocols)) {
protocols = null;
}
this._eventEmitter = new NativeEventEmitter(NativeWebSocketModule);
this._socketId = nextWebSocketId++;
this._registerEvents();
NativeWebSocketModule.connect(url, protocols, {headers}, this._socketId);
}
get binaryType(): ?BinaryType {
return this._binaryType;
}
set binaryType(binaryType: BinaryType): void {
if (binaryType !== 'blob' && binaryType !== 'arraybuffer') {
throw new Error("binaryType must be either 'blob' or 'arraybuffer'");
}
if (this._binaryType === 'blob' || binaryType === 'blob') {
invariant(
BlobManager.isAvailable,
'Native module BlobModule is required for blob support',
);
if (binaryType === 'blob') {
BlobManager.addWebSocketHandler(this._socketId);
} else {
BlobManager.removeWebSocketHandler(this._socketId);
}
}
this._binaryType = binaryType;
}
close(code?: number, reason?: string): void {
if (this.readyState === this.CLOSING || this.readyState === this.CLOSED) {
return;
}
this.readyState = this.CLOSING;
this._close(code, reason);
}
send(data: string | ArrayBuffer | ArrayBufferView | Blob): void {
if (this.readyState === this.CONNECTING) {
throw new Error('INVALID_STATE_ERR');
}
if (data instanceof Blob) {
invariant(
BlobManager.isAvailable,
'Native module BlobModule is required for blob support',
);
BlobManager.sendOverSocket(data, this._socketId);
return;
}
if (typeof data === 'string') {
NativeWebSocketModule.send(data, this._socketId);
return;
}
if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
NativeWebSocketModule.sendBinary(binaryToBase64(data), this._socketId);
return;
}
throw new Error('Unsupported data type');
}
ping(): void {
if (this.readyState === this.CONNECTING) {
throw new Error('INVALID_STATE_ERR');
}
NativeWebSocketModule.ping(this._socketId);
}
_close(code?: number, reason?: string): void {
// See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
const statusCode = typeof code === 'number' ? code : CLOSE_NORMAL;
const closeReason = typeof reason === 'string' ? reason : '';
NativeWebSocketModule.close(statusCode, closeReason, this._socketId);
if (BlobManager.isAvailable && this._binaryType === 'blob') {
BlobManager.removeWebSocketHandler(this._socketId);
}
}
_unregisterEvents(): void {
this._subscriptions.forEach(e => e.remove());
this._subscriptions = [];
}
_registerEvents(): void {
this._subscriptions = [
this._eventEmitter.addListener('websocketMessage', ev => {
if (ev.id !== this._socketId) {
return;
}
let data = ev.data;
switch (ev.type) {
case 'binary':
data = base64.toByteArray(ev.data).buffer;
break;
case 'blob':
data = BlobManager.createFromOptions(ev.data);
break;
}
this.dispatchEvent(new WebSocketEvent('message', {data}));
}),
this._eventEmitter.addListener('websocketOpen', ev => {
if (ev.id !== this._socketId) {
return;
}
this.readyState = this.OPEN;
this.protocol = ev.protocol;
this.dispatchEvent(new WebSocketEvent('open'));
}),
this._eventEmitter.addListener('websocketClosed', ev => {
if (ev.id !== this._socketId) {
return;
}
this.readyState = this.CLOSED;
this.dispatchEvent(
new WebSocketEvent('close', {
code: ev.code,
reason: ev.reason,
}),
);
this._unregisterEvents();
this.close();
}),
this._eventEmitter.addListener('websocketFailed', ev => {
if (ev.id !== this._socketId) {
return;
}
this.readyState = this.CLOSED;
this.dispatchEvent(
new WebSocketEvent('error', {
message: ev.message,
}),
);
this.dispatchEvent(
new WebSocketEvent('close', {
message: ev.message,
}),
);
this._unregisterEvents();
this.close();
}),
];
}
}
module.exports = WebSocket;

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
/**
* Event object passed to the `onopen`, `onclose`, `onmessage`, `onerror`
* callbacks of `WebSocket`.
*
* The `type` property is "open", "close", "message", "error" respectively.
*
* In case of "message", the `data` property contains the incoming data.
*/
class WebSocketEvent {
constructor(type, eventInitDict) {
this.type = type.toString();
Object.assign(this, eventInitDict);
}
}
module.exports = WebSocketEvent;

View File

@ -0,0 +1,228 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
const NativeEventEmitter = require('../EventEmitter/NativeEventEmitter');
import NativeWebSocketModule from './NativeWebSocketModule';
const base64 = require('base64-js');
const originalRCTWebSocketConnect = NativeWebSocketModule.connect;
const originalRCTWebSocketSend = NativeWebSocketModule.send;
const originalRCTWebSocketSendBinary = NativeWebSocketModule.sendBinary;
const originalRCTWebSocketClose = NativeWebSocketModule.close;
let eventEmitter: NativeEventEmitter;
let subscriptions: Array<EventSubscription>;
let closeCallback;
let sendCallback;
let connectCallback;
let onOpenCallback;
let onMessageCallback;
let onErrorCallback;
let onCloseCallback;
let isInterceptorEnabled = false;
/**
* A network interceptor which monkey-patches RCTWebSocketModule methods
* to gather all websocket network requests/responses, in order to show
* their information in the React Native inspector development tool.
*/
const WebSocketInterceptor = {
/**
* Invoked when RCTWebSocketModule.close(...) is called.
*/
setCloseCallback(callback) {
closeCallback = callback;
},
/**
* Invoked when RCTWebSocketModule.send(...) or sendBinary(...) is called.
*/
setSendCallback(callback) {
sendCallback = callback;
},
/**
* Invoked when RCTWebSocketModule.connect(...) is called.
*/
setConnectCallback(callback) {
connectCallback = callback;
},
/**
* Invoked when event "websocketOpen" happens.
*/
setOnOpenCallback(callback) {
onOpenCallback = callback;
},
/**
* Invoked when event "websocketMessage" happens.
*/
setOnMessageCallback(callback) {
onMessageCallback = callback;
},
/**
* Invoked when event "websocketFailed" happens.
*/
setOnErrorCallback(callback) {
onErrorCallback = callback;
},
/**
* Invoked when event "websocketClosed" happens.
*/
setOnCloseCallback(callback) {
onCloseCallback = callback;
},
isInterceptorEnabled() {
return isInterceptorEnabled;
},
_unregisterEvents() {
subscriptions.forEach(e => e.remove());
subscriptions = [];
},
/**
* Add listeners to the RCTWebSocketModule events to intercept them.
*/
_registerEvents() {
subscriptions = [
eventEmitter.addListener('websocketMessage', ev => {
if (onMessageCallback) {
onMessageCallback(
ev.id,
ev.type === 'binary'
? WebSocketInterceptor._arrayBufferToString(ev.data)
: ev.data,
);
}
}),
eventEmitter.addListener('websocketOpen', ev => {
if (onOpenCallback) {
onOpenCallback(ev.id);
}
}),
eventEmitter.addListener('websocketClosed', ev => {
if (onCloseCallback) {
onCloseCallback(ev.id, {code: ev.code, reason: ev.reason});
}
}),
eventEmitter.addListener('websocketFailed', ev => {
if (onErrorCallback) {
onErrorCallback(ev.id, {message: ev.message});
}
}),
];
},
enableInterception() {
if (isInterceptorEnabled) {
return;
}
eventEmitter = new NativeEventEmitter(NativeWebSocketModule);
WebSocketInterceptor._registerEvents();
// Override `connect` method for all RCTWebSocketModule requests
// to intercept the request url, protocols, options and socketId,
// then pass them through the `connectCallback`.
NativeWebSocketModule.connect = function(
url,
protocols,
options,
socketId,
) {
if (connectCallback) {
connectCallback(url, protocols, options, socketId);
}
originalRCTWebSocketConnect.apply(this, arguments);
};
// Override `send` method for all RCTWebSocketModule requests to intercept
// the data sent, then pass them through the `sendCallback`.
NativeWebSocketModule.send = function(data, socketId) {
if (sendCallback) {
sendCallback(data, socketId);
}
originalRCTWebSocketSend.apply(this, arguments);
};
// Override `sendBinary` method for all RCTWebSocketModule requests to
// intercept the data sent, then pass them through the `sendCallback`.
NativeWebSocketModule.sendBinary = function(data, socketId) {
if (sendCallback) {
sendCallback(WebSocketInterceptor._arrayBufferToString(data), socketId);
}
originalRCTWebSocketSendBinary.apply(this, arguments);
};
// Override `close` method for all RCTWebSocketModule requests to intercept
// the close information, then pass them through the `closeCallback`.
NativeWebSocketModule.close = function() {
if (closeCallback) {
if (arguments.length === 3) {
closeCallback(arguments[0], arguments[1], arguments[2]);
} else {
closeCallback(null, null, arguments[0]);
}
}
originalRCTWebSocketClose.apply(this, arguments);
};
isInterceptorEnabled = true;
},
_arrayBufferToString(data) {
const value = base64.toByteArray(data).buffer;
if (value === undefined || value === null) {
return '(no value)';
}
if (
typeof ArrayBuffer !== 'undefined' &&
typeof Uint8Array !== 'undefined' &&
value instanceof ArrayBuffer
) {
return `ArrayBuffer {${String(Array.from(new Uint8Array(value)))}}`;
}
return value;
},
// Unpatch RCTWebSocketModule methods and remove the callbacks.
disableInterception() {
if (!isInterceptorEnabled) {
return;
}
isInterceptorEnabled = false;
NativeWebSocketModule.send = originalRCTWebSocketSend;
NativeWebSocketModule.sendBinary = originalRCTWebSocketSendBinary;
NativeWebSocketModule.close = originalRCTWebSocketClose;
NativeWebSocketModule.connect = originalRCTWebSocketConnect;
connectCallback = null;
closeCallback = null;
sendCallback = null;
onOpenCallback = null;
onMessageCallback = null;
onCloseCallback = null;
onErrorCallback = null;
WebSocketInterceptor._unregisterEvents();
},
};
module.exports = WebSocketInterceptor;

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
// Jest fatals for the following statement (minimal repro case)
//
// exports.something = Symbol;
//
// Until it is fixed, mocking the entire node module makes the
// problem go away.
'use strict';
function EventTarget() {
// Support both EventTarget and EventTarget(...)
// as a super class, just like the original module does.
if (arguments.length > 0) {
return EventTarget;
}
}
module.exports = EventTarget;