TypeScript strict mode (#2388)

This commit is contained in:
James Newton-King 2018-05-31 12:04:48 +12:00 committed by GitHub
parent 8d680db112
commit 30a59f6df7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 313 additions and 233 deletions

View File

@ -350,7 +350,7 @@ describe("hubConnection", () => {
.build();
hubConnection.onclose((error) => {
expect(error.message).toEqual("Server returned an error on close: Connection closed with an error. InvalidOperationException: Unable to resolve service for type 'System.Object' while attempting to activate 'FunctionalTests.UncreatableHub'.");
expect(error!.message).toEqual("Server returned an error on close: Connection closed with an error. InvalidOperationException: Unable to resolve service for type 'System.Object' while attempting to activate 'FunctionalTests.UncreatableHub'.");
done();
});
hubConnection.start();
@ -632,7 +632,12 @@ describe("hubConnection", () => {
const defaultClient = new DefaultHttpClient(TestLogger.instance);
class TestClient extends HttpClient {
public pollPromise: Promise<HttpResponse>;
public pollPromise: Promise<HttpResponse> | null;
constructor() {
super();
this.pollPromise = null;
}
public send(request: HttpRequest): Promise<HttpResponse> {
if (request.method === "GET") {

View File

@ -56,7 +56,7 @@ class WebDriverReporter implements jasmine.CustomReporter {
// Just report the first failure
this.taplog(" ---");
if (result.failedExpectations.length > 0) {
if (result.failedExpectations && result.failedExpectations.length > 0) {
this.taplog(" - messages:");
for (const expectation of result.failedExpectations) {
// Include YAML block with failed expectations

View File

@ -37,7 +37,19 @@ export class MessagePackHubProtocol implements IHubProtocol {
if (logger === null) {
logger = NullLogger.instance;
}
return BinaryMessageFormat.parse(input).map((m) => this.parseMessage(m, logger));
const messages = BinaryMessageFormat.parse(input);
const hubMessages = [];
for (const message of messages) {
const parsedMessage = this.parseMessage(message, logger);
// Can be null for an unknown message. Unknown message is logged in parseMessage
if (parsedMessage) {
hubMessages.push(parsedMessage);
}
}
return hubMessages;
}
/** Writes the specified HubMessage to an ArrayBuffer and returns it.
@ -61,7 +73,7 @@ export class MessagePackHubProtocol implements IHubProtocol {
}
}
private parseMessage(input: Uint8Array, logger: ILogger): HubMessage {
private parseMessage(input: Uint8Array, logger: ILogger): HubMessage | null {
if (input.length === 0) {
throw new Error("Invalid payload.");
}
@ -173,24 +185,27 @@ export class MessagePackHubProtocol implements IHubProtocol {
throw new Error("Invalid payload for Completion message.");
}
const completionMessage = {
error: null as string,
headers,
invocationId: properties[2],
result: null as any,
type: MessageType.Completion,
};
let error: string | undefined;
let result: any;
switch (resultKind) {
case errorResult:
completionMessage.error = properties[4];
error = properties[4];
break;
case nonVoidResult:
completionMessage.result = properties[4];
result = properties[4];
break;
}
return completionMessage as CompletionMessage;
const completionMessage: CompletionMessage = {
error,
headers,
invocationId: properties[2],
result,
type: MessageType.Completion,
};
return completionMessage;
}
private writeInvocation(invocationMessage: InvocationMessage): ArrayBuffer {

View File

@ -66,12 +66,10 @@ describe("MessageHubProtocol", () => {
error: "Err",
headers: {},
invocationId: "abc",
result: null,
type: MessageType.Completion,
} as CompletionMessage],
[[0x0b, 0x95, 0x03, 0x80, 0xa3, 0x61, 0x62, 0x63, 0x03, 0xa2, 0x4f, 0x4b],
{
error: null,
headers: {},
invocationId: "abc",
result: "OK",
@ -79,15 +77,12 @@ describe("MessageHubProtocol", () => {
} as CompletionMessage],
[[0x08, 0x94, 0x03, 0x80, 0xa3, 0x61, 0x62, 0x63, 0x02],
{
error: null,
headers: {},
invocationId: "abc",
result: null,
type: MessageType.Completion,
} as CompletionMessage],
[[0x0E, 0x95, 0x03, 0x80, 0xa3, 0x61, 0x62, 0x63, 0x03, 0xD6, 0xFF, 0x5A, 0x4A, 0x1A, 0x50],
{
error: null,
headers: {},
invocationId: "abc",
result: new Date(Date.UTC(2018, 0, 1, 11, 24, 0)),
@ -96,10 +91,8 @@ describe("MessageHubProtocol", () => {
// extra property at the end should be ignored (testing older protocol client working with newer protocol server)
[[0x09, 0x95, 0x03, 0x80, 0xa3, 0x61, 0x62, 0x63, 0x02, 0x00],
{
error: null,
headers: {},
invocationId: "abc",
result: null,
type: MessageType.Completion,
} as CompletionMessage],
] as Array<[number[], CompletionMessage]>).forEach(([payload, expectedMessage]) =>
@ -174,7 +167,6 @@ describe("MessageHubProtocol", () => {
type: MessageType.StreamItem,
} as StreamItemMessage,
{
error: null,
headers: {},
invocationId: "abc",
result: "OK",

View File

@ -8,7 +8,7 @@
// Not exported from index.
export class AbortController implements AbortSignal {
private isAborted: boolean = false;
public onabort: () => void;
public onabort: (() => void) | null = null;
public abort() {
if (!this.isAborted) {
@ -33,5 +33,5 @@ export interface AbortSignal {
/** Indicates if the request has been aborted. */
aborted: boolean;
/** Set this to a handler that will be invoked when the request is aborted. */
onabort: () => void;
onabort: (() => void) | null;
}

View File

@ -166,15 +166,27 @@ export class DefaultHttpClient extends HttpClient {
const xhr = new XMLHttpRequest();
if (!request.method) {
reject(new Error("No method defined."));
return;
}
if (!request.url) {
reject(new Error("No url defined."));
return;
}
xhr.open(request.method, request.url, true);
xhr.withCredentials = true;
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
// Explicitly setting the Content-Type header for React Native on Android platform.
xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
if (request.headers) {
Object.keys(request.headers)
.forEach((header) => xhr.setRequestHeader(header, request.headers[header]));
const headers = request.headers;
if (headers) {
Object.keys(headers)
.forEach((header) => {
xhr.setRequestHeader(header, headers[header]);
});
}
if (request.responseType) {

View File

@ -37,14 +37,14 @@ export class HttpConnection implements IConnection {
private readonly httpClient: HttpClient;
private readonly logger: ILogger;
private readonly options: IHttpConnectionOptions;
private transport: ITransport;
private startPromise: Promise<void>;
private transport?: ITransport;
private startPromise?: Promise<void>;
private stopError?: Error;
private accessTokenFactory?: () => string | Promise<string>;
public readonly features: any = {};
public onreceive: (data: string | ArrayBuffer) => void;
public onclose: (e?: Error) => void;
public onreceive: ((data: string | ArrayBuffer) => void) | null;
public onclose: ((e?: Error) => void) | null;
constructor(url: string, options: IHttpConnectionOptions = {}) {
Arg.isRequired(url, "url");
@ -53,12 +53,13 @@ export class HttpConnection implements IConnection {
this.baseUrl = this.resolveUrl(url);
options = options || {};
options.accessTokenFactory = options.accessTokenFactory || (() => null);
options.logMessageContent = options.logMessageContent || false;
this.httpClient = options.httpClient || new DefaultHttpClient(this.logger);
this.connectionState = ConnectionState.Disconnected;
this.options = options;
this.onreceive = null;
this.onclose = null;
}
public start(): Promise<void>;
@ -85,7 +86,8 @@ export class HttpConnection implements IConnection {
throw new Error("Cannot send data if the connection is not in the 'Connected' State.");
}
return this.transport.send(data);
// Transport will not be null if state is connected
return this.transport!.send(data);
}
public async stop(error?: Error): Promise<void> {
@ -101,7 +103,7 @@ export class HttpConnection implements IConnection {
if (this.transport) {
this.stopError = error;
await this.transport.stop();
this.transport = null;
this.transport = undefined;
}
}
@ -118,12 +120,12 @@ export class HttpConnection implements IConnection {
this.transport = this.constructTransport(HttpTransportType.WebSockets);
// We should just call connect directly in this case.
// No fallback or negotiate in this case.
await this.transport.connect(url, transferFormat);
await this.transport!.connect(url, transferFormat);
} else {
throw Error("Negotiation can only be skipped when using the WebSocket transport directly.");
}
} else {
let negotiateResponse: INegotiateResponse = null;
let negotiateResponse: INegotiateResponse | null = null;
let redirects = 0;
do {
@ -159,8 +161,8 @@ export class HttpConnection implements IConnection {
this.features.inherentKeepAlive = true;
}
this.transport.onreceive = this.onreceive;
this.transport.onclose = (e) => this.stopConnection(e);
this.transport!.onreceive = this.onreceive;
this.transport!.onclose = (e) => this.stopConnection(e);
// only change the state if we were connecting to not overwrite
// the state if the connection is already marked as Disconnected
@ -168,18 +170,20 @@ export class HttpConnection implements IConnection {
} catch (e) {
this.logger.log(LogLevel.Error, "Failed to start the connection: " + e);
this.connectionState = ConnectionState.Disconnected;
this.transport = null;
this.transport = undefined;
throw e;
}
}
private async getNegotiationResponse(url: string): Promise<INegotiateResponse> {
const token = await this.accessTokenFactory();
let headers;
if (token) {
headers = {
["Authorization"]: `Bearer ${token}`,
};
if (this.accessTokenFactory) {
const token = await this.accessTokenFactory();
if (token) {
headers = {
["Authorization"]: `Bearer ${token}`,
};
}
}
const negotiateUrl = this.resolveNegotiateUrl(url);
@ -201,11 +205,14 @@ export class HttpConnection implements IConnection {
}
}
private createConnectUrl(url: string, connectionId: string) {
private createConnectUrl(url: string, connectionId: string | null | undefined) {
if (!connectionId) {
return url;
}
return url + (url.indexOf("?") === -1 ? "?" : "&") + `id=${connectionId}`;
}
private async createTransport(url: string, requestedTransport: HttpTransportType | ITransport, negotiateResponse: INegotiateResponse, requestedTransferFormat: TransferFormat): Promise<void> {
private async createTransport(url: string, requestedTransport: HttpTransportType | ITransport | undefined, negotiateResponse: INegotiateResponse, requestedTransferFormat: TransferFormat): Promise<void> {
let connectUrl = this.createConnectUrl(url, negotiateResponse.connectionId);
if (this.isITransport(requestedTransport)) {
this.logger.log(LogLevel.Debug, "Connection was provided an instance of ITransport, using that directly.");
@ -218,24 +225,24 @@ export class HttpConnection implements IConnection {
return;
}
const transports = negotiateResponse.availableTransports;
const transports = negotiateResponse.availableTransports || [];
for (const endpoint of transports) {
this.connectionState = ConnectionState.Connecting;
const transport = this.resolveTransport(endpoint, requestedTransport, requestedTransferFormat);
if (typeof transport === "number") {
this.transport = this.constructTransport(transport);
if (negotiateResponse.connectionId === null) {
if (!negotiateResponse.connectionId) {
negotiateResponse = await this.getNegotiationResponse(url);
connectUrl = this.createConnectUrl(url, negotiateResponse.connectionId);
}
try {
await this.transport.connect(connectUrl, requestedTransferFormat);
await this.transport!.connect(connectUrl, requestedTransferFormat);
this.changeState(ConnectionState.Connecting, ConnectionState.Connected);
return;
} catch (ex) {
this.logger.log(LogLevel.Error, `Failed to start the transport '${HttpTransportType[transport]}': ${ex}`);
this.connectionState = ConnectionState.Disconnected;
negotiateResponse.connectionId = null;
negotiateResponse.connectionId = undefined;
}
}
}
@ -246,17 +253,17 @@ export class HttpConnection implements IConnection {
private constructTransport(transport: HttpTransportType) {
switch (transport) {
case HttpTransportType.WebSockets:
return new WebSocketTransport(this.accessTokenFactory, this.logger, this.options.logMessageContent);
return new WebSocketTransport(this.accessTokenFactory, this.logger, this.options.logMessageContent || false);
case HttpTransportType.ServerSentEvents:
return new ServerSentEventsTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent);
return new ServerSentEventsTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false);
case HttpTransportType.LongPolling:
return new LongPollingTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent);
return new LongPollingTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false);
default:
throw new Error(`Unknown transport: ${transport}.`);
}
}
private resolveTransport(endpoint: IAvailableTransport, requestedTransport: HttpTransportType, requestedTransferFormat: TransferFormat): HttpTransportType | null {
private resolveTransport(endpoint: IAvailableTransport, requestedTransport: HttpTransportType | undefined, requestedTransferFormat: TransferFormat): HttpTransportType | null {
const transport = HttpTransportType[endpoint.transport];
if (transport === null || transport === undefined) {
this.logger.log(LogLevel.Debug, `Skipping transport '${endpoint.transport}' because it is not supported by this client.`);
@ -294,7 +301,7 @@ export class HttpConnection implements IConnection {
}
private async stopConnection(error?: Error): Promise<void> {
this.transport = null;
this.transport = undefined;
// If we have a stopError, it takes precedence over the error from the transport
error = this.stopError || error;
@ -346,6 +353,6 @@ export class HttpConnection implements IConnection {
}
}
function transportMatches(requestedTransport: HttpTransportType, actualTransport: HttpTransportType) {
function transportMatches(requestedTransport: HttpTransportType | undefined, actualTransport: HttpTransportType) {
return !requestedTransport || ((actualTransport & requestedTransport) !== 0);
}

View File

@ -27,12 +27,12 @@ export class HubConnection {
private readonly logger: ILogger;
private protocol: IHubProtocol;
private handshakeProtocol: HandshakeProtocol;
private callbacks: { [invocationId: string]: (invocationEvent: StreamItemMessage | CompletionMessage, error?: Error) => void };
private callbacks: { [invocationId: string]: (invocationEvent: StreamItemMessage | CompletionMessage | null, error?: Error) => void };
private methods: { [name: string]: Array<(...args: any[]) => void> };
private id: number;
private closedCallbacks: Array<(error?: Error) => void>;
private timeoutHandle: NodeJS.Timer;
private pingServerHandle: NodeJS.Timer;
private timeoutHandle?: NodeJS.Timer;
private pingServerHandle?: NodeJS.Timer;
private receivedHandshakeResponse: boolean;
private connectionState: HubConnectionState;
@ -79,6 +79,7 @@ export class HubConnection {
this.methods = {};
this.closedCallbacks = [];
this.id = 0;
this.receivedHandshakeResponse = false;
this.connectionState = HubConnectionState.Disconnected;
this.cachedPingMessage = this.protocol.writeMessage({ type: MessageType.Ping });
@ -150,20 +151,21 @@ export class HubConnection {
return this.sendMessage(cancelMessage);
});
this.callbacks[invocationDescriptor.invocationId] = (invocationEvent: CompletionMessage | StreamItemMessage, error?: Error) => {
this.callbacks[invocationDescriptor.invocationId] = (invocationEvent: CompletionMessage | StreamItemMessage | null, error?: Error) => {
if (error) {
subject.error(error);
return;
}
if (invocationEvent.type === MessageType.Completion) {
if (invocationEvent.error) {
subject.error(new Error(invocationEvent.error));
} else if (invocationEvent) {
// invocationEvent will not be null when an error is not passed to the callback
if (invocationEvent.type === MessageType.Completion) {
if (invocationEvent.error) {
subject.error(new Error(invocationEvent.error));
} else {
subject.complete();
}
} else {
subject.complete();
subject.next((invocationEvent.item) as T);
}
} else {
subject.next((invocationEvent.item) as T);
}
};
@ -215,20 +217,22 @@ export class HubConnection {
const invocationDescriptor = this.createInvocation(methodName, args, false);
const p = new Promise<any>((resolve, reject) => {
this.callbacks[invocationDescriptor.invocationId] = (invocationEvent: StreamItemMessage | CompletionMessage, error?: Error) => {
// invocationId will always have a value for a non-blocking invocation
this.callbacks[invocationDescriptor.invocationId!] = (invocationEvent: StreamItemMessage | CompletionMessage | null, error?: Error) => {
if (error) {
reject(error);
return;
}
if (invocationEvent.type === MessageType.Completion) {
const completionMessage = invocationEvent as CompletionMessage;
if (completionMessage.error) {
reject(new Error(completionMessage.error));
} else if (invocationEvent) {
// invocationEvent will not be null when an error is not passed to the callback
if (invocationEvent.type === MessageType.Completion) {
if (invocationEvent.error) {
reject(new Error(invocationEvent.error));
} else {
resolve(invocationEvent.result);
}
} else {
resolve(completionMessage.result);
reject(new Error(`Unexpected message type: ${invocationEvent.type}`));
}
} else {
reject(new Error(`Unexpected message type: ${invocationEvent.type}`));
}
};
@ -237,7 +241,8 @@ export class HubConnection {
this.sendMessage(message)
.catch((e) => {
reject(e);
delete this.callbacks[invocationDescriptor.invocationId];
// invocationId will always have a value for a non-blocking invocation
delete this.callbacks[invocationDescriptor.invocationId!];
});
});
@ -349,7 +354,7 @@ export class HubConnection {
break;
case MessageType.Close:
this.logger.log(LogLevel.Information, "Close message received from server.");
this.connection.stop(message.error ? new Error("Server returned an error on close: " + message.error) : null);
this.connection.stop(message.error ? new Error("Server returned an error on close: " + message.error) : undefined);
break;
default:
this.logger.log(LogLevel.Warning, "Invalid message type: " + message.type);
@ -428,7 +433,7 @@ export class HubConnection {
Object.keys(callbacks)
.forEach((key) => {
const callback = callbacks[key];
callback(undefined, error ? error : new Error("Invocation canceled due to connection being closed."));
callback(null, error ? error : new Error("Invocation canceled due to connection being closed."));
});
this.cleanupTimeout();

View File

@ -14,13 +14,13 @@ import { Arg, ConsoleLogger } from "./Utils";
/** A builder for configuring {@link HubConnection} instances. */
export class HubConnectionBuilder {
/** @internal */
public protocol: IHubProtocol;
public protocol?: IHubProtocol;
/** @internal */
public httpConnectionOptions: IHttpConnectionOptions;
public httpConnectionOptions?: IHttpConnectionOptions;
/** @internal */
public url: string;
public url?: string;
/** @internal */
public logger: ILogger;
public logger?: ILogger;
/** Configures console logging for the {@link HubConnection}.
*

View File

@ -10,6 +10,6 @@ export interface IConnection {
send(data: string | ArrayBuffer): Promise<void>;
stop(error?: Error): Promise<void>;
onreceive: (data: string | ArrayBuffer) => void;
onclose: (error?: Error) => void;
onreceive: ((data: string | ArrayBuffer) => void) | null;
onclose: ((error?: Error) => void) | null;
}

View File

@ -27,6 +27,6 @@ export interface ITransport {
connect(url: string, transferFormat: TransferFormat): Promise<void>;
send(data: any): Promise<void>;
stop(): Promise<void>;
onreceive: (data: string | ArrayBuffer) => void;
onclose: (error?: Error) => void;
onreceive: ((data: string | ArrayBuffer) => void) | null;
onclose: ((error?: Error) => void) | null;
}

View File

@ -11,28 +11,36 @@ import { Arg, getDataDetail, sendMessage } from "./Utils";
// Not exported from 'index', this type is internal.
export class LongPollingTransport implements ITransport {
private readonly httpClient: HttpClient;
private readonly accessTokenFactory: () => string | Promise<string>;
private readonly accessTokenFactory: (() => string | Promise<string>) | undefined;
private readonly logger: ILogger;
private readonly logMessageContent: boolean;
private readonly pollAbort: AbortController;
private url: string;
private pollXhr: XMLHttpRequest;
private pollAbort: AbortController;
private url?: string;
private pollXhr?: XMLHttpRequest;
private running: boolean;
private receiving: Promise<void>;
private closeError: Error;
private receiving?: Promise<void>;
private closeError?: Error;
public onreceive: ((data: string | ArrayBuffer) => void) | null;
public onclose: ((error?: Error) => void) | null;
// This is an internal type, not exported from 'index' so this is really just internal.
public get pollAborted() {
return this.pollAbort.aborted;
}
constructor(httpClient: HttpClient, accessTokenFactory: () => string | Promise<string>, logger: ILogger, logMessageContent: boolean) {
constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise<string>) | undefined, logger: ILogger, logMessageContent: boolean) {
this.httpClient = httpClient;
this.accessTokenFactory = accessTokenFactory || (() => null);
this.accessTokenFactory = accessTokenFactory;
this.logger = logger;
this.pollAbort = new AbortController();
this.logMessageContent = logMessageContent;
this.running = false;
this.onreceive = null;
this.onclose = null;
}
public async connect(url: string, transferFormat: TransferFormat): Promise<void> {
@ -59,7 +67,7 @@ export class LongPollingTransport implements ITransport {
pollOptions.responseType = "arraybuffer";
}
const token = await this.accessTokenFactory();
const token = await this.getAccessToken();
this.updateHeaderToken(pollOptions, token);
// Make initial long polling request
@ -71,7 +79,7 @@ export class LongPollingTransport implements ITransport {
this.logger.log(LogLevel.Error, `(LongPolling transport) Unexpected response code: ${response.statusCode}`);
// Mark running as false so that the poll immediately ends and runs the close logic
this.closeError = new HttpError(response.statusText, response.statusCode);
this.closeError = new HttpError(response.statusText || "", response.statusCode);
this.running = false;
} else {
this.running = true;
@ -80,7 +88,18 @@ export class LongPollingTransport implements ITransport {
this.receiving = this.poll(this.url, pollOptions);
}
private updateHeaderToken(request: HttpRequest, token: string) {
private async getAccessToken(): Promise<string | null> {
if (this.accessTokenFactory) {
return await this.accessTokenFactory();
}
return null;
}
private updateHeaderToken(request: HttpRequest, token: string | null) {
if (!request.headers) {
request.headers = {};
}
if (token) {
// tslint:disable-next-line:no-string-literal
request.headers["Authorization"] = `Bearer ${token}`;
@ -97,7 +116,7 @@ export class LongPollingTransport implements ITransport {
try {
while (this.running) {
// We have to get the access token on each poll, in case it changes
const token = await this.accessTokenFactory();
const token = await this.getAccessToken();
this.updateHeaderToken(pollOptions, token);
try {
@ -113,7 +132,7 @@ export class LongPollingTransport implements ITransport {
this.logger.log(LogLevel.Error, `(LongPolling transport) Unexpected response code: ${response.statusCode}`);
// Unexpected status code
this.closeError = new HttpError(response.statusText, response.statusCode);
this.closeError = new HttpError(response.statusText || "", response.statusCode);
this.running = false;
} else {
// Process the response
@ -158,7 +177,7 @@ export class LongPollingTransport implements ITransport {
if (!this.running) {
return Promise.reject(new Error("Cannot send until the transport is connected"));
}
return sendMessage(this.logger, "LongPolling", this.httpClient, this.url, this.accessTokenFactory, data, this.logMessageContent);
return sendMessage(this.logger, "LongPolling", this.httpClient, this.url!, this.accessTokenFactory, data, this.logMessageContent);
}
public async stop(): Promise<void> {
@ -177,9 +196,9 @@ export class LongPollingTransport implements ITransport {
const deleteOptions: HttpRequest = {
headers: {},
};
const token = await this.accessTokenFactory();
const token = await this.getAccessToken();
this.updateHeaderToken(deleteOptions, token);
await this.httpClient.delete(this.url, deleteOptions);
await this.httpClient.delete(this.url!, deleteOptions);
this.logger.log(LogLevel.Trace, "(LongPolling transport) DELETE request sent.");
} finally {
@ -201,7 +220,4 @@ export class LongPollingTransport implements ITransport {
this.onclose(this.closeError);
}
}
public onreceive: (data: string | ArrayBuffer) => void;
public onclose: (error?: Error) => void;
}

View File

@ -8,17 +8,23 @@ import { Arg, getDataDetail, sendMessage } from "./Utils";
export class ServerSentEventsTransport implements ITransport {
private readonly httpClient: HttpClient;
private readonly accessTokenFactory: () => string | Promise<string>;
private readonly accessTokenFactory: (() => string | Promise<string>) | undefined;
private readonly logger: ILogger;
private readonly logMessageContent: boolean;
private eventSource: EventSource;
private url: string;
private eventSource?: EventSource;
private url?: string;
constructor(httpClient: HttpClient, accessTokenFactory: () => string | Promise<string>, logger: ILogger, logMessageContent: boolean) {
public onreceive: ((data: string | ArrayBuffer) => void) | null;
public onclose: ((error?: Error) => void) | null;
constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise<string>) | undefined, logger: ILogger, logMessageContent: boolean) {
this.httpClient = httpClient;
this.accessTokenFactory = accessTokenFactory || (() => null);
this.accessTokenFactory = accessTokenFactory;
this.logger = logger;
this.logMessageContent = logMessageContent;
this.onreceive = null;
this.onclose = null;
}
public async connect(url: string, transferFormat: TransferFormat): Promise<void> {
@ -32,9 +38,11 @@ export class ServerSentEventsTransport implements ITransport {
this.logger.log(LogLevel.Trace, "(SSE transport) Connecting");
const token = await this.accessTokenFactory();
if (token) {
url += (url.indexOf("?") < 0 ? "?" : "&") + `access_token=${encodeURIComponent(token)}`;
if (this.accessTokenFactory) {
const token = await this.accessTokenFactory();
if (token) {
url += (url.indexOf("?") < 0 ? "?" : "&") + `access_token=${encodeURIComponent(token)}`;
}
}
this.url = url;
@ -86,7 +94,7 @@ export class ServerSentEventsTransport implements ITransport {
if (!this.eventSource) {
return Promise.reject(new Error("Cannot send until the transport is connected"));
}
return sendMessage(this.logger, "SSE", this.httpClient, this.url, this.accessTokenFactory, data, this.logMessageContent);
return sendMessage(this.logger, "SSE", this.httpClient, this.url!, this.accessTokenFactory, data, this.logMessageContent);
}
public stop(): Promise<void> {
@ -97,14 +105,11 @@ export class ServerSentEventsTransport implements ITransport {
private close(e?: Error) {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
this.eventSource = undefined;
if (this.onclose) {
this.onclose(e);
}
}
}
public onreceive: (data: string | ArrayBuffer) => void;
public onclose: (error?: Error) => void;
}

View File

@ -22,19 +22,19 @@ export class Arg {
}
export function getDataDetail(data: any, includeContent: boolean): string {
let length: string = null;
let detail = "";
if (data instanceof ArrayBuffer) {
length = `Binary data of length ${data.byteLength}`;
detail = `Binary data of length ${data.byteLength}`;
if (includeContent) {
length += `. Content: '${formatArrayBuffer(data)}'`;
detail += `. Content: '${formatArrayBuffer(data)}'`;
}
} else if (typeof data === "string") {
length = `String data of length ${data.length}`;
detail = `String data of length ${data.length}`;
if (includeContent) {
length += `. Content: '${data}'.`;
detail += `. Content: '${data}'.`;
}
}
return length;
return detail;
}
export function formatArrayBuffer(data: ArrayBuffer): string {
@ -51,13 +51,15 @@ export function formatArrayBuffer(data: ArrayBuffer): string {
return str.substr(0, str.length - 1);
}
export async function sendMessage(logger: ILogger, transportName: string, httpClient: HttpClient, url: string, accessTokenFactory: () => string | Promise<string>, content: string | ArrayBuffer, logMessageContent: boolean): Promise<void> {
export async function sendMessage(logger: ILogger, transportName: string, httpClient: HttpClient, url: string, accessTokenFactory: (() => string | Promise<string>) | undefined, content: string | ArrayBuffer, logMessageContent: boolean): Promise<void> {
let headers;
const token = await accessTokenFactory();
if (token) {
headers = {
["Authorization"]: `Bearer ${token}`,
};
if (accessTokenFactory) {
const token = await accessTokenFactory();
if (token) {
headers = {
["Authorization"]: `Bearer ${token}`,
};
}
}
logger.log(LogLevel.Trace, `(${transportName} transport) sending data. ${getDataDetail(content, logMessageContent)}.`);

View File

@ -7,14 +7,20 @@ import { Arg, getDataDetail } from "./Utils";
export class WebSocketTransport implements ITransport {
private readonly logger: ILogger;
private readonly accessTokenFactory: () => string | Promise<string>;
private readonly accessTokenFactory: (() => string | Promise<string>) | undefined;
private readonly logMessageContent: boolean;
private webSocket: WebSocket;
private webSocket?: WebSocket;
constructor(accessTokenFactory: () => string | Promise<string>, logger: ILogger, logMessageContent: boolean) {
public onreceive: ((data: string | ArrayBuffer) => void) | null;
public onclose: ((error?: Error) => void) | null;
constructor(accessTokenFactory: (() => string | Promise<string>) | undefined, logger: ILogger, logMessageContent: boolean) {
this.logger = logger;
this.accessTokenFactory = accessTokenFactory || (() => null);
this.accessTokenFactory = accessTokenFactory;
this.logMessageContent = logMessageContent;
this.onreceive = null;
this.onclose = null;
}
public async connect(url: string, transferFormat: TransferFormat): Promise<void> {
@ -28,9 +34,11 @@ export class WebSocketTransport implements ITransport {
this.logger.log(LogLevel.Trace, "(WebSockets transport) Connecting");
const token = await this.accessTokenFactory();
if (token) {
url += (url.indexOf("?") < 0 ? "?" : "&") + `access_token=${encodeURIComponent(token)}`;
if (this.accessTokenFactory) {
const token = await this.accessTokenFactory();
if (token) {
url += (url.indexOf("?") < 0 ? "?" : "&") + `access_token=${encodeURIComponent(token)}`;
}
}
return new Promise<void>((resolve, reject) => {
@ -46,8 +54,9 @@ export class WebSocketTransport implements ITransport {
resolve();
};
webSocket.onerror = (event: ErrorEvent) => {
reject(event.error);
webSocket.onerror = (event: Event) => {
const error = (event instanceof ErrorEvent) ? event.error : null;
reject(error);
};
webSocket.onmessage = (message: MessageEvent) => {
@ -84,11 +93,8 @@ export class WebSocketTransport implements ITransport {
public stop(): Promise<void> {
if (this.webSocket) {
this.webSocket.close();
this.webSocket = null;
this.webSocket = undefined;
}
return Promise.resolve();
}
public onreceive: (data: string | ArrayBuffer) => void;
public onclose: (error?: Error) => void;
}

View File

@ -7,9 +7,10 @@ import { TestHttpClient } from "./TestHttpClient";
describe("HttpClient", () => {
describe("get", () => {
it("sets the method and URL appropriately", async () => {
let request: HttpRequest;
let request!: HttpRequest;
const testClient = new TestHttpClient().on((r) => {
request = r; return "";
request = r;
return "";
});
await testClient.get("http://localhost");
@ -18,9 +19,10 @@ describe("HttpClient", () => {
});
it("overrides method and url in options", async () => {
let request: HttpRequest;
let request!: HttpRequest;
const testClient = new TestHttpClient().on((r) => {
request = r; return "";
request = r;
return "";
});
await testClient.get("http://localhost", {
@ -32,9 +34,10 @@ describe("HttpClient", () => {
});
it("copies other options", async () => {
let request: HttpRequest;
let request!: HttpRequest;
const testClient = new TestHttpClient().on((r) => {
request = r; return "";
request = r;
return "";
});
await testClient.get("http://localhost", {
@ -46,9 +49,10 @@ describe("HttpClient", () => {
describe("post", () => {
it("sets the method and URL appropriately", async () => {
let request: HttpRequest;
let request!: HttpRequest;
const testClient = new TestHttpClient().on((r) => {
request = r; return "";
request = r;
return "";
});
await testClient.post("http://localhost");
@ -57,9 +61,10 @@ describe("HttpClient", () => {
});
it("overrides method and url in options", async () => {
let request: HttpRequest;
let request!: HttpRequest;
const testClient = new TestHttpClient().on((r) => {
request = r; return "";
request = r;
return "";
});
await testClient.post("http://localhost", {
@ -71,9 +76,10 @@ describe("HttpClient", () => {
});
it("copies other options", async () => {
let request: HttpRequest;
let request!: HttpRequest;
const testClient = new TestHttpClient().on((r) => {
request = r; return "";
request = r;
return "";
});
await testClient.post("http://localhost", {

View File

@ -13,7 +13,6 @@ import { TestHttpClient } from "./TestHttpClient";
import { PromiseSource } from "./Utils";
const commonOptions: IHttpConnectionOptions = {
logger: null,
};
const defaultConnectionId = "abc123";
@ -165,8 +164,8 @@ describe("HttpConnection", () => {
stop(): Promise<void> {
return Promise.resolve();
},
onclose: undefined,
onreceive: undefined,
onclose: null,
onreceive: null,
};
const options: IHttpConnectionOptions = {
@ -224,7 +223,7 @@ describe("HttpConnection", () => {
const negotiateResponse = { ...defaultNegotiateResponse };
// Remove the requested transport from the response
negotiateResponse.availableTransports = negotiateResponse.availableTransports
negotiateResponse.availableTransports = negotiateResponse.availableTransports!
.filter((f) => f.transport !== HttpTransportType[requestedTransport]);
const options: IHttpConnectionOptions = {
@ -483,7 +482,7 @@ describe("HttpConnection", () => {
.on("GET", (r) => {
httpClientGetCount++;
// tslint:disable-next-line:no-string-literal
const authorizationValue = r.headers["Authorization"];
const authorizationValue = r.headers!["Authorization"];
if (httpClientGetCount === 1) {
if (authorizationValue) {
fail("First long poll request should have a authorization header.");

View File

@ -13,7 +13,7 @@ import { TextMessageFormat } from "../src/TextMessageFormat";
import { delay, PromiseSource } from "./Utils";
function createHubConnection(connection: IConnection, logger?: ILogger, protocol?: IHubProtocol) {
function createHubConnection(connection: IConnection, logger?: ILogger | null, protocol?: IHubProtocol | null) {
return HubConnection.create(connection, logger || NullLogger.instance, protocol || new JsonHubProtocol());
}
@ -181,7 +181,7 @@ describe("HubConnection", () => {
});
it("can process handshake and additional messages from binary", async () => {
let receivedProcotolData: ArrayBuffer;
let receivedProcotolData: ArrayBuffer | undefined;
const mockProtocol = new TestProtocol(TransferFormat.Binary);
mockProtocol.onreceive = (d) => receivedProcotolData = d as ArrayBuffer;
@ -202,14 +202,14 @@ describe("HubConnection", () => {
connection.receiveBinary(new Uint8Array(data).buffer);
// left over data is the message pack message
expect(receivedProcotolData.byteLength).toEqual(102);
expect(receivedProcotolData!.byteLength).toEqual(102);
} finally {
hubConnection.stop();
}
});
it("can process handshake and additional messages from text", async () => {
let receivedProcotolData: string;
let receivedProcotolData: string | undefined;
const mockProtocol = new TestProtocol(TransferFormat.Text);
mockProtocol.onreceive = (d) => receivedProcotolData = d as string;
@ -281,7 +281,7 @@ describe("HubConnection", () => {
const invokePromise = hubConnection.invoke("testMethod");
// Typically this would be called by the transport
connection.onclose(new Error("Connection lost"));
connection.onclose!(new Error("Connection lost"));
expect(invokePromise).rejects.toThrow("Connection lost");
} finally {
@ -474,12 +474,12 @@ describe("HubConnection", () => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
try {
let closeError: Error = null;
let closeError: Error | undefined;
hubConnection.onclose((e) => closeError = e);
connection.receiveHandshakeResponse("Error!");
expect(closeError.message).toEqual("Server returned handshake error: Error!");
expect(closeError!.message).toEqual("Server returned handshake error: Error!");
} finally {
hubConnection.stop();
}
@ -490,7 +490,7 @@ describe("HubConnection", () => {
const hubConnection = createHubConnection(connection);
try {
let isClosed = false;
let closeError: Error = null;
let closeError: Error | undefined;
hubConnection.onclose((e) => {
isClosed = true;
closeError = e;
@ -503,7 +503,7 @@ describe("HubConnection", () => {
});
expect(isClosed).toEqual(true);
expect(closeError).toEqual(null);
expect(closeError).toBeUndefined();
} finally {
hubConnection.stop();
}
@ -514,7 +514,7 @@ describe("HubConnection", () => {
const hubConnection = createHubConnection(connection);
try {
let isClosed = false;
let closeError: Error = null;
let closeError: Error | undefined;
hubConnection.onclose((e) => {
isClosed = true;
closeError = e;
@ -528,7 +528,7 @@ describe("HubConnection", () => {
});
expect(isClosed).toEqual(true);
expect(closeError.message).toEqual("Server returned an error on close: Error!");
expect(closeError!.message).toEqual("Server returned an error on close: Error!");
} finally {
hubConnection.stop();
}
@ -622,12 +622,12 @@ describe("HubConnection", () => {
try {
connection.receiveHandshakeResponse();
hubConnection.on(null, undefined);
hubConnection.on(undefined, null);
hubConnection.on("message", null);
hubConnection.on("message", undefined);
hubConnection.on(null, () => { });
hubConnection.on(undefined, () => { });
hubConnection.on(null!, undefined!);
hubConnection.on(undefined!, null!);
hubConnection.on("message", null!);
hubConnection.on("message", undefined!);
hubConnection.on(null!, () => { });
hubConnection.on(undefined!, () => { });
// invoke a method to make sure we are not trying to use null/undefined
connection.receive({
@ -640,12 +640,12 @@ describe("HubConnection", () => {
expect(warnings).toEqual(["No client method with the name 'message' found."]);
hubConnection.off(null, undefined);
hubConnection.off(undefined, null);
hubConnection.off("message", null);
hubConnection.off("message", undefined);
hubConnection.off(null, () => { });
hubConnection.off(undefined, () => { });
hubConnection.off(null!, undefined!);
hubConnection.off(undefined!, null!);
hubConnection.off("message", null!);
hubConnection.off("message", undefined!);
hubConnection.off(null!, () => { });
hubConnection.off(undefined!, () => { });
} finally {
hubConnection.stop();
}
@ -741,7 +741,7 @@ describe("HubConnection", () => {
.subscribe(observer);
// Typically this would be called by the transport
connection.onclose(new Error("Connection lost"));
connection.onclose!(new Error("Connection lost"));
expect(observer.completed).rejects.toThrow("Error: Connection lost");
} finally {
@ -784,7 +784,7 @@ describe("HubConnection", () => {
// Typically this would be called by the transport
// triggers observer.error()
connection.onclose(new Error("Connection lost"));
connection.onclose!(new Error("Connection lost"));
} finally {
hubConnection.stop();
}
@ -845,7 +845,7 @@ describe("HubConnection", () => {
hubConnection.onclose((e) => invocations++);
hubConnection.onclose((e) => invocations++);
// Typically this would be called by the transport
connection.onclose();
connection.onclose!();
expect(invocations).toBe(2);
} finally {
hubConnection.stop();
@ -856,12 +856,12 @@ describe("HubConnection", () => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
try {
let error: Error;
let error: Error | undefined;
hubConnection.onclose((e) => error = e);
// Typically this would be called by the transport
connection.onclose(new Error("Test error."));
expect(error.message).toBe("Test error.");
connection.onclose!(new Error("Test error."));
expect(error!.message).toBe("Test error.");
} finally {
hubConnection.stop();
}
@ -871,10 +871,10 @@ describe("HubConnection", () => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
try {
hubConnection.onclose(null);
hubConnection.onclose(undefined);
hubConnection.onclose(null!);
hubConnection.onclose(undefined!);
// Typically this would be called by the transport
connection.onclose();
connection.onclose!();
// expect no errors
} finally {
hubConnection.stop();
@ -885,10 +885,10 @@ describe("HubConnection", () => {
const connection = new TestConnection();
const hubConnection = createHubConnection(connection);
try {
let state: HubConnectionState;
let state: HubConnectionState | undefined;
hubConnection.onclose((e) => state = hubConnection.state);
// Typically this would be called by the transport
connection.onclose();
connection.onclose!();
expect(state).toBe(HubConnectionState.Disconnected);
} finally {
@ -998,6 +998,18 @@ async function pingAndWait(connection: TestConnection): Promise<void> {
class TestConnection implements IConnection {
public readonly features: any = {};
public onreceive: ((data: string | ArrayBuffer) => void) | null;
public onclose: ((error?: Error) => void) | null;
public sentData: any[];
public lastInvocationId: string | null;
constructor() {
this.onreceive = null;
this.onclose = null;
this.sentData = [];
this.lastInvocationId = null;
}
public start(): Promise<void> {
return Promise.resolve();
}
@ -1029,21 +1041,22 @@ class TestConnection implements IConnection {
public receive(data: any): void {
const payload = JSON.stringify(data);
this.onreceive(TextMessageFormat.write(payload));
this.invokeOnReceive(TextMessageFormat.write(payload));
}
public receiveText(data: string) {
this.onreceive(data);
this.invokeOnReceive(data);
}
public receiveBinary(data: ArrayBuffer) {
this.onreceive(data);
this.invokeOnReceive(data);
}
public onreceive: (data: string | ArrayBuffer) => void;
public onclose: (error?: Error) => void;
public sentData: any[];
public lastInvocationId: string;
private invokeOnReceive(data: string | ArrayBuffer) {
if (this.onreceive) {
this.onreceive(data);
}
}
}
class TestProtocol implements IHubProtocol {
@ -1052,10 +1065,11 @@ class TestProtocol implements IHubProtocol {
public readonly transferFormat: TransferFormat;
public onreceive: (data: string | ArrayBuffer) => void;
public onreceive: ((data: string | ArrayBuffer) => void) | null;
constructor(transferFormat: TransferFormat) {
this.transferFormat = transferFormat;
this.onreceive = null;
}
public parseMessages(input: any): HubMessage[] {
@ -1072,17 +1086,17 @@ class TestProtocol implements IHubProtocol {
}
class TestObserver implements IStreamSubscriber<any> {
public readonly closed: boolean;
public itemsReceived: [any];
private itemsSource: PromiseSource<[any]>;
public readonly closed: boolean = false;
public itemsReceived: any[];
private itemsSource: PromiseSource<any[]>;
get completed(): Promise<[any]> {
get completed(): Promise<any[]> {
return this.itemsSource.promise;
}
constructor() {
this.itemsReceived = [] as [any];
this.itemsSource = new PromiseSource<[any]>();
this.itemsReceived = [];
this.itemsSource = new PromiseSource<any[]>();
}
public next(value: any) {

View File

@ -37,17 +37,17 @@ describe("HubConnectionBuilder", () => {
eachMissingValue((val, name) => {
it(`configureLogging throws if logger is ${name}`, () => {
const builder = new HubConnectionBuilder();
expect(() => builder.configureLogging(val)).toThrow("The 'logging' argument is required.");
expect(() => builder.configureLogging(val!)).toThrow("The 'logging' argument is required.");
});
it(`withUrl throws if url is ${name}`, () => {
const builder = new HubConnectionBuilder();
expect(() => builder.withUrl(val)).toThrow("The 'url' argument is required.");
expect(() => builder.withUrl(val!)).toThrow("The 'url' argument is required.");
});
it(`withHubProtocol throws if protocol is ${name}`, () => {
const builder = new HubConnectionBuilder();
expect(() => builder.withHubProtocol(val)).toThrow("The 'protocol' argument is required.");
expect(() => builder.withHubProtocol(val!)).toThrow("The 'protocol' argument is required.");
});
});
@ -88,7 +88,7 @@ describe("HubConnectionBuilder", () => {
const builder = createConnectionBuilder()
.withUrl("http://example.com", HttpTransportType.WebSockets)
.withHubProtocol(protocol);
expect(builder.httpConnectionOptions.transport).toBe(HttpTransportType.WebSockets);
expect(builder.httpConnectionOptions!.transport).toBe(HttpTransportType.WebSockets);
});
it("can configure hub protocol", async () => {

View File

@ -71,33 +71,29 @@ describe("JsonHubProtocol", () => {
result: null,
type: MessageType.Completion,
} as CompletionMessage],
[`{"type":3, "invocationId": "abc", "result": "OK", "error": null, "headers": {}}${TextMessageFormat.RecordSeparator}`,
[`{"type":3, "invocationId": "abc", "result": "OK", "headers": {}}${TextMessageFormat.RecordSeparator}`,
{
error: null,
headers: {},
invocationId: "abc",
result: "OK",
type: MessageType.Completion,
} as CompletionMessage],
[`{"type":3, "invocationId": "abc", "error": null, "result": null, "headers": {}}${TextMessageFormat.RecordSeparator}`,
[`{"type":3, "invocationId": "abc", "result": null, "headers": {}}${TextMessageFormat.RecordSeparator}`,
{
error: null,
headers: {},
invocationId: "abc",
result: null,
type: MessageType.Completion,
} as CompletionMessage],
[`{"type":3, "invocationId": "abc", "result": 1514805840000, "error": null, "headers": {}}${TextMessageFormat.RecordSeparator}`,
[`{"type":3, "invocationId": "abc", "result": 1514805840000, "headers": {}}${TextMessageFormat.RecordSeparator}`,
{
error: null,
headers: {},
invocationId: "abc",
result: Date.UTC(2018, 0, 1, 11, 24, 0),
type: MessageType.Completion,
} as CompletionMessage],
[`{"type":3, "invocationId": "abc", "error": null, "result": null, "headers": {}, "extraParameter":"value"}${TextMessageFormat.RecordSeparator}`,
[`{"type":3, "invocationId": "abc", "result": null, "headers": {}, "extraParameter":"value"}${TextMessageFormat.RecordSeparator}`,
{
error: null,
extraParameter: "value",
headers: {},
invocationId: "abc",
@ -165,7 +161,7 @@ describe("JsonHubProtocol", () => {
}));
it("can read multiple messages", () => {
const payload = `{"type":2, "invocationId": "abc", "headers": {}, "item": 8}${TextMessageFormat.RecordSeparator}{"type":3, "invocationId": "abc", "headers": {}, "result": "OK", "error": null}${TextMessageFormat.RecordSeparator}`;
const payload = `{"type":2, "invocationId": "abc", "headers": {}, "item": 8}${TextMessageFormat.RecordSeparator}{"type":3, "invocationId": "abc", "headers": {}, "result": "OK"}${TextMessageFormat.RecordSeparator}`;
const messages = new JsonHubProtocol().parseMessages(payload, NullLogger.instance);
expect(messages).toEqual([
{
@ -175,7 +171,6 @@ describe("JsonHubProtocol", () => {
type: MessageType.StreamItem,
} as StreamItemMessage,
{
error: null,
headers: {},
invocationId: "abc",
result: "OK",

View File

@ -20,7 +20,7 @@ describe("LongPollingTransport", () => {
return new HttpResponse(200);
} else {
// Turn 'onabort' into a promise.
const abort = new Promise((resolve, reject) => r.abortSignal.onabort = resolve);
const abort = new Promise((resolve, reject) => r.abortSignal!.onabort = resolve);
await abort;
// Signal that the poll has completed.
@ -30,7 +30,7 @@ describe("LongPollingTransport", () => {
}
})
.on("DELETE", (r) => new HttpResponse(202));
const transport = new LongPollingTransport(client, null, NullLogger.instance, false);
const transport = new LongPollingTransport(client, undefined, NullLogger.instance, false);
await transport.connect("http://example.com", TransferFormat.Text);
const stopPromise = transport.stop();
@ -53,7 +53,7 @@ describe("LongPollingTransport", () => {
return new HttpResponse(204);
}
});
const transport = new LongPollingTransport(client, null, NullLogger.instance, false);
const transport = new LongPollingTransport(client, undefined, NullLogger.instance, false);
const stopPromise = makeClosedPromise(transport);
@ -84,7 +84,7 @@ describe("LongPollingTransport", () => {
return new HttpResponse(202);
});
const transport = new LongPollingTransport(httpClient, null, NullLogger.instance, false);
const transport = new LongPollingTransport(httpClient, undefined, NullLogger.instance, false);
await transport.connect("http://tempuri.org", TransferFormat.Text);

View File

@ -49,8 +49,8 @@ export class TestHttpClient extends HttpClient {
const oldHandler = this.handler;
const newHandler = async (request: HttpRequest) => {
if (matches(method, request.method) && matches(url, request.url)) {
const promise = handler(request, oldHandler);
if (matches(method, request.method!) && matches(url, request.url!)) {
const promise = handler!(request, oldHandler);
let val: TestHttpHandlerResult;
if (promise instanceof Promise) {

View File

@ -12,8 +12,8 @@ export function delay(durationInMilliseconds: number): Promise<void> {
export class PromiseSource<T = void> implements Promise<T> {
public promise: Promise<T>;
private resolver: (value?: T | PromiseLike<T>) => void;
private rejecter: (reason?: any) => void;
private resolver!: (value?: T | PromiseLike<T>) => void;
private rejecter!: (reason?: any) => void;
constructor() {
this.promise = new Promise<T>((resolve, reject) => {

View File

@ -14,6 +14,7 @@
"suppressImplicitAnyIndexErrors": true,
"noEmitOnError": true,
"stripInternal": true,
"strict": true,
"lib": [ "es5", "es2015.promise", "es2015.iterable", "dom" ],
"baseUrl": ".",
"paths": {