Merge pull request #2181 from aspnet/release/2.1

JSDocs (#2168)
This commit is contained in:
Andrew Stanton-Nurse 2018-05-01 16:06:54 -07:00 committed by GitHub
commit 01d47ec78f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 471 additions and 41 deletions

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Not exported from index.
export class BinaryMessageFormat { export class BinaryMessageFormat {
// The length prefix of binary messages is encoded as VarInt. Read the comment in // The length prefix of binary messages is encoded as VarInt. Read the comment in

View File

@ -8,20 +8,53 @@ import { CompletionMessage, HubMessage, IHubProtocol, ILogger, InvocationMessage
import { BinaryMessageFormat } from "./BinaryMessageFormat"; import { BinaryMessageFormat } from "./BinaryMessageFormat";
// TypeDoc's @inheritDoc and @link don't work across modules :(
/** Implements the MessagePack Hub Protocol */
export class MessagePackHubProtocol implements IHubProtocol { export class MessagePackHubProtocol implements IHubProtocol {
/** The name of the protocol. This is used by SignalR to resolve the protocol between the client and server. */
public readonly name: string = "messagepack"; public readonly name: string = "messagepack";
/** The version of the protocol. */
public readonly version: number = 1; public readonly version: number = 1;
/** The TransferFormat of the protocol. */
public readonly transferFormat: TransferFormat = TransferFormat.Binary; public readonly transferFormat: TransferFormat = TransferFormat.Binary;
/** Creates an array of HubMessage objects from the specified serialized representation.
*
* @param {ArrayBuffer} input An ArrayBuffer containing the serialized representation.
* @param {ILogger} logger A logger that will be used to log messages that occur during parsing.
*/
public parseMessages(input: ArrayBuffer, logger: ILogger): HubMessage[] { public parseMessages(input: ArrayBuffer, logger: ILogger): HubMessage[] {
// The interface does allow "string" to be passed in, but this implementation does not. So let's throw a useful error.
if (!(input instanceof ArrayBuffer)) {
throw new Error("Invalid input for MessagePack hub protocol. Expected an ArrayBuffer.");
}
if (logger === null) { if (logger === null) {
logger = NullLogger.instance; logger = NullLogger.instance;
} }
return BinaryMessageFormat.parse(input).map((m) => this.parseMessage(m, logger)); return BinaryMessageFormat.parse(input).map((m) => this.parseMessage(m, logger));
} }
/** Writes the specified HubMessage to an ArrayBuffer and returns it.
*
* @param {HubMessage} message The message to write.
* @returns {ArrayBuffer} An ArrayBuffer containing the serialized representation of the message.
*/
public writeMessage(message: HubMessage): ArrayBuffer {
switch (message.type) {
case MessageType.Invocation:
return this.writeInvocation(message as InvocationMessage);
case MessageType.StreamInvocation:
return this.writeStreamInvocation(message as StreamInvocationMessage);
case MessageType.StreamItem:
case MessageType.Completion:
throw new Error(`Writing messages of type '${message.type}' is not supported.`);
default:
throw new Error("Invalid message type.");
}
}
private parseMessage(input: Uint8Array, logger: ILogger): HubMessage { private parseMessage(input: Uint8Array, logger: ILogger): HubMessage {
if (input.length === 0) { if (input.length === 0) {
throw new Error("Invalid payload."); throw new Error("Invalid payload.");
@ -154,20 +187,6 @@ export class MessagePackHubProtocol implements IHubProtocol {
return completionMessage as CompletionMessage; return completionMessage as CompletionMessage;
} }
public writeMessage(message: HubMessage): ArrayBuffer {
switch (message.type) {
case MessageType.Invocation:
return this.writeInvocation(message as InvocationMessage);
case MessageType.StreamInvocation:
return this.writeStreamInvocation(message as StreamInvocationMessage);
case MessageType.StreamItem:
case MessageType.Completion:
throw new Error(`Writing messages of type '${message.type}' is not supported.`);
default:
throw new Error("Invalid message type.");
}
}
private writeInvocation(invocationMessage: InvocationMessage): ArrayBuffer { private writeInvocation(invocationMessage: InvocationMessage): ArrayBuffer {
const msgpack = msgpack5(); const msgpack = msgpack5();
const payload = msgpack.encode([MessageType.Invocation, invocationMessage.headers || {}, invocationMessage.invocationId || null, const payload = msgpack.encode([MessageType.Invocation, invocationMessage.headers || {}, invocationMessage.invocationId || null,

View File

@ -197,15 +197,15 @@ class TestProtocol implements IHubProtocol {
public name: string = "test"; public name: string = "test";
public version: number = 1; public version: number = 1;
public transferFormat: TransferFormat = TransferFormat.Text; public transferFormat: TransferFormat = TransferFormat.Text;
public parseMessages(input: any, logger: ILogger): HubMessage[] { public parseMessages(input: string | ArrayBuffer, logger: ILogger): HubMessage[] {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
public writeMessage(message: HubMessage) { public writeMessage(message: HubMessage): string | ArrayBuffer {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
} }
function createConnectionBuilder(logger?: ILogger | LogLevel): HubConnectionBuilder { function createConnectionBuilder(logger?: ILogger): HubConnectionBuilder {
// We don't want to spam test output with logs. This can be changed as needed // We don't want to spam test output with logs. This can be changed as needed
return new HubConnectionBuilder() return new HubConnectionBuilder()
.configureLogging(logger || NullLogger.instance); .configureLogging(logger || NullLogger.instance);

View File

@ -5,6 +5,7 @@
// We don't actually ever use the API being polyfilled, we always use the polyfill because // We don't actually ever use the API being polyfilled, we always use the polyfill because
// it's a very new API right now. // it's a very new API right now.
// Not exported from index.
export class AbortController implements AbortSignal { export class AbortController implements AbortSignal {
private isAborted: boolean = false; private isAborted: boolean = false;
public onabort: () => void; public onabort: () => void;
@ -27,7 +28,10 @@ export class AbortController implements AbortSignal {
} }
} }
/** Represents a signal that can be monitored to determine if a request has been aborted. */
export interface AbortSignal { export interface AbortSignal {
/** Indicates if the request has been aborted. */
aborted: boolean; aborted: boolean;
/** Set this to a handler that will be invoked when the request is aborted. */
onabort: () => void; onabort: () => void;
} }

View File

@ -1,10 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
/** Error thrown when an HTTP request fails. */
export class HttpError extends Error { export class HttpError extends Error {
// tslint:disable-next-line:variable-name // tslint:disable-next-line:variable-name
private __proto__: Error; private __proto__: Error;
/** The HTTP status code represented by this error. */
public statusCode: number; public statusCode: number;
/** Constructs a new instance of {@link HttpError}.
*
* @param {string} errorMessage A descriptive error message.
* @param {number} statusCode The HTTP status code represented by this error.
*/
constructor(errorMessage: string, statusCode: number) { constructor(errorMessage: string, statusCode: number) {
const trueProto = new.target.prototype; const trueProto = new.target.prototype;
super(errorMessage); super(errorMessage);
@ -16,9 +25,15 @@ export class HttpError extends Error {
} }
} }
/** Error thrown when a timeout elapses. */
export class TimeoutError extends Error { export class TimeoutError extends Error {
// tslint:disable-next-line:variable-name // tslint:disable-next-line:variable-name
private __proto__: Error; private __proto__: Error;
/** Constructs a new instance of {@link TimeoutError}.
*
* @param {string} errorMessage A descriptive error message.
*/
constructor(errorMessage: string = "A timeout occurred.") { constructor(errorMessage: string = "A timeout occurred.") {
const trueProto = new.target.prototype; const trueProto = new.target.prototype;
super(errorMessage); super(errorMessage);

View File

@ -5,20 +5,59 @@ import { AbortSignal } from "./AbortController";
import { HttpError, TimeoutError } from "./Errors"; import { HttpError, TimeoutError } from "./Errors";
import { ILogger, LogLevel } from "./ILogger"; import { ILogger, LogLevel } from "./ILogger";
/** Represents an HTTP request. */
export interface HttpRequest { export interface HttpRequest {
/** The HTTP method to use for the request. */
method?: string; method?: string;
/** The URL for the request. */
url?: string; url?: string;
/** The body content for the request. May be a string or an ArrayBuffer (for binary data). */
content?: string | ArrayBuffer; content?: string | ArrayBuffer;
/** An object describing headers to apply to the request. */
headers?: { [key: string]: string }; headers?: { [key: string]: string };
/** The XMLHttpRequestResponseType to apply to the request. */
responseType?: XMLHttpRequestResponseType; responseType?: XMLHttpRequestResponseType;
/** An AbortSignal that can be monitored for cancellation. */
abortSignal?: AbortSignal; abortSignal?: AbortSignal;
/** The time to wait for the request to complete before throwing a TimeoutError. Measured in milliseconds. */
timeout?: number; timeout?: number;
} }
/** Represents an HTTP response. */
export class HttpResponse { export class HttpResponse {
/** Constructs a new instance of {@link HttpResponse} with the specified status code.
*
* @param {number} statusCode The status code of the response.
*/
constructor(statusCode: number); constructor(statusCode: number);
/** Constructs a new instance of {@link HttpResponse} with the specified status code and message.
*
* @param {number} statusCode The status code of the response.
* @param {string} statusText The status message of the response.
*/
constructor(statusCode: number, statusText: string); constructor(statusCode: number, statusText: string);
/** Constructs a new instance of {@link HttpResponse} with the specified status code, message and string content.
*
* @param {number} statusCode The status code of the response.
* @param {string} statusText The status message of the response.
* @param {string} content The content of the response.
*/
constructor(statusCode: number, statusText: string, content: string); constructor(statusCode: number, statusText: string, content: string);
/** Constructs a new instance of {@link HttpResponse} with the specified status code, message and binary content.
*
* @param {number} statusCode The status code of the response.
* @param {string} statusText The status message of the response.
* @param {ArrayBuffer} content The content of the response.
*/
constructor(statusCode: number, statusText: string, content: ArrayBuffer); constructor(statusCode: number, statusText: string, content: ArrayBuffer);
constructor( constructor(
public readonly statusCode: number, public readonly statusCode: number,
@ -27,8 +66,24 @@ export class HttpResponse {
} }
} }
/** Abstraction over an HTTP client.
*
* This class provides an abstraction over an HTTP client so that a different implementation can be provided on different platforms.
*/
export abstract class HttpClient { export abstract class HttpClient {
/** Issues an HTTP GET request to the specified URL, returning a Promise that resolves with an {@link HttpResponse} representing the result.
*
* @param {string} url The URL for the request.
* @returns {Promise<HttpResponse>} A Promise that resolves with an {@link HttpResponse} describing the response, or rejects with an Error indicating a failure.
*/
public get(url: string): Promise<HttpResponse>; public get(url: string): Promise<HttpResponse>;
/** Issues an HTTP GET request to the specified URL, returning a Promise that resolves with an {@link HttpResponse} representing the result.
*
* @param {string} url The URL for the request.
* @param {HttpRequest} options Additional options to configure the request. The 'url' field in this object will be overridden by the url parameter.
* @returns {Promise<HttpResponse>} A Promise that resolves with an {@link HttpResponse} describing the response, or rejects with an Error indicating a failure.
*/
public get(url: string, options: HttpRequest): Promise<HttpResponse>; public get(url: string, options: HttpRequest): Promise<HttpResponse>;
public get(url: string, options?: HttpRequest): Promise<HttpResponse> { public get(url: string, options?: HttpRequest): Promise<HttpResponse> {
return this.send({ return this.send({
@ -38,7 +93,19 @@ export abstract class HttpClient {
}); });
} }
/** Issues an HTTP POST request to the specified URL, returning a Promise that resolves with an {@link HttpResponse} representing the result.
*
* @param {string} url The URL for the request.
* @returns {Promise<HttpResponse>} A Promise that resolves with an {@link HttpResponse} describing the response, or rejects with an Error indicating a failure.
*/
public post(url: string): Promise<HttpResponse>; public post(url: string): Promise<HttpResponse>;
/** Issues an HTTP POST request to the specified URL, returning a Promise that resolves with an {@link HttpResponse} representing the result.
*
* @param {string} url The URL for the request.
* @param {HttpRequest} options Additional options to configure the request. The 'url' field in this object will be overridden by the url parameter.
* @returns {Promise<HttpResponse>} A Promise that resolves with an {@link HttpResponse} describing the response, or rejects with an Error indicating a failure.
*/
public post(url: string, options: HttpRequest): Promise<HttpResponse>; public post(url: string, options: HttpRequest): Promise<HttpResponse>;
public post(url: string, options?: HttpRequest): Promise<HttpResponse> { public post(url: string, options?: HttpRequest): Promise<HttpResponse> {
return this.send({ return this.send({
@ -48,7 +115,19 @@ export abstract class HttpClient {
}); });
} }
/** Issues an HTTP DELETE request to the specified URL, returning a Promise that resolves with an {@link HttpResponse} representing the result.
*
* @param {string} url The URL for the request.
* @returns {Promise<HttpResponse>} A Promise that resolves with an {@link HttpResponse} describing the response, or rejects with an Error indicating a failure.
*/
public delete(url: string): Promise<HttpResponse>; public delete(url: string): Promise<HttpResponse>;
/** Issues an HTTP DELETE request to the specified URL, returning a Promise that resolves with an {@link HttpResponse} representing the result.
*
* @param {string} url The URL for the request.
* @param {HttpRequest} options Additional options to configure the request. The 'url' field in this object will be overridden by the url parameter.
* @returns {Promise<HttpResponse>} A Promise that resolves with an {@link HttpResponse} describing the response, or rejects with an Error indicating a failure.
*/
public delete(url: string, options: HttpRequest): Promise<HttpResponse>; public delete(url: string, options: HttpRequest): Promise<HttpResponse>;
public delete(url: string, options?: HttpRequest): Promise<HttpResponse> { public delete(url: string, options?: HttpRequest): Promise<HttpResponse> {
return this.send({ return this.send({
@ -58,17 +137,25 @@ export abstract class HttpClient {
}); });
} }
/** Issues an HTTP request to the specified URL, returning a {@link Promise} that resolves with an {@link HttpResponse} representing the result.
*
* @param {HttpRequest} request An {@link HttpRequest} describing the request to send.
* @returns {Promise<HttpResponse>} A Promise that resolves with an HttpResponse describing the response, or rejects with an Error indicating a failure.
*/
public abstract send(request: HttpRequest): Promise<HttpResponse>; public abstract send(request: HttpRequest): Promise<HttpResponse>;
} }
/** Default implementation of {@link HttpClient}. */
export class DefaultHttpClient extends HttpClient { export class DefaultHttpClient extends HttpClient {
private readonly logger: ILogger; private readonly logger: ILogger;
constructor(logger: ILogger) { /** Creates a new instance of the {@link DefaultHttpClient}, using the provided {@link ILogger} to log messages. */
public constructor(logger: ILogger) {
super(); super();
this.logger = logger; this.logger = logger;
} }
/** @inheritDoc */
public send(request: HttpRequest): Promise<HttpResponse> { public send(request: HttpRequest): Promise<HttpResponse> {
return new Promise<HttpResponse>((resolve, reject) => { return new Promise<HttpResponse>((resolve, reject) => {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();

View File

@ -10,6 +10,7 @@ import { Arg, Subject } from "./Utils";
const DEFAULT_TIMEOUT_IN_MS: number = 30 * 1000; const DEFAULT_TIMEOUT_IN_MS: number = 30 * 1000;
/** Represents a connection to a SignalR Hub. */
export class HubConnection { export class HubConnection {
private readonly connection: IConnection; private readonly connection: IConnection;
private readonly logger: ILogger; private readonly logger: ILogger;
@ -22,6 +23,11 @@ export class HubConnection {
private timeoutHandle: NodeJS.Timer; private timeoutHandle: NodeJS.Timer;
private receivedHandshakeResponse: boolean; private receivedHandshakeResponse: boolean;
/** The server timeout in milliseconds.
*
* If this timeout elapses without receiving any messages from the server, the connection will be terminated with an error.
* The default timeout value is 30,000 milliseconds (30 seconds).
*/
public serverTimeoutInMilliseconds: number; public serverTimeoutInMilliseconds: number;
/** @internal */ /** @internal */
@ -54,6 +60,10 @@ export class HubConnection {
this.id = 0; this.id = 0;
} }
/** Starts the connection.
*
* @returns {Promise<void>} A Promise that resolves when the connection has been successfully established, or rejects with an error.
*/
public async start(): Promise<void> { public async start(): Promise<void> {
const handshakeRequest: HandshakeRequestMessage = { const handshakeRequest: HandshakeRequestMessage = {
protocol: this.protocol.name, protocol: this.protocol.name,
@ -77,6 +87,10 @@ export class HubConnection {
this.configureTimeout(); this.configureTimeout();
} }
/** Stops the connection.
*
* @returns {Promise<void>} A Promise that resolves when the connection has been successfully terminated, or rejects with an error.
*/
public stop(): Promise<void> { public stop(): Promise<void> {
this.logger.log(LogLevel.Debug, "Stopping HubConnection."); this.logger.log(LogLevel.Debug, "Stopping HubConnection.");
@ -84,6 +98,13 @@ export class HubConnection {
return this.connection.stop(); return this.connection.stop();
} }
/** Invokes a streaming hub method on the server using the specified name and arguments.
*
* @typeparam T The type of the items returned by the server.
* @param {string} methodName The name of the server method to invoke.
* @param {any[]} args The arguments used to invoke the server method.
* @returns {IStreamResult<T>} An object that yields results from the server as they are received.
*/
public stream<T = any>(methodName: string, ...args: any[]): IStreamResult<T> { public stream<T = any>(methodName: string, ...args: any[]): IStreamResult<T> {
const invocationDescriptor = this.createStreamInvocation(methodName, args); const invocationDescriptor = this.createStreamInvocation(methodName, args);
@ -124,6 +145,15 @@ export class HubConnection {
return subject; return subject;
} }
/** Invokes a hub method on the server using the specified name and arguments. Does not wait for a response from the receiver.
*
* The Promise returned by this method resolves when the client has sent the invocation to the server. The server may still
* be processing the invocation.
*
* @param {string} methodName The name of the server method to invoke.
* @param {any[]} args The arguments used to invoke the server method.
* @returns {Promise<void>} A Promise that resolves when the invocation has been successfully sent, or rejects with an error.
*/
public send(methodName: string, ...args: any[]): Promise<void> { public send(methodName: string, ...args: any[]): Promise<void> {
const invocationDescriptor = this.createInvocation(methodName, args, true); const invocationDescriptor = this.createInvocation(methodName, args, true);
@ -132,6 +162,17 @@ export class HubConnection {
return this.connection.send(message); return this.connection.send(message);
} }
/** Invokes a hub method on the server using the specified name and arguments.
*
* The Promise returned by this method resolves when the server indicates it has finished invoking the method. When the promise
* resolves, the server has finished invoking the method. If the server method returns a result, it is produced as the result of
* resolving the Promise.
*
* @typeparam T The expected return type.
* @param {string} methodName The name of the server method to invoke.
* @param {any[]} args The arguments used to invoke the server method.
* @returns {Promise<T>} A Promise that resolves with the result of the server method (if any), or rejects with an error.
*/
public invoke<T = any>(methodName: string, ...args: any[]): Promise<T> { public invoke<T = any>(methodName: string, ...args: any[]): Promise<T> {
const invocationDescriptor = this.createInvocation(methodName, args, false); const invocationDescriptor = this.createInvocation(methodName, args, false);
@ -165,6 +206,11 @@ export class HubConnection {
return p; return p;
} }
/** Registers a handler that will be invoked when the hub method with the specified method name is invoked.
*
* @param {string} methodName The name of the hub method to define.
* @param {Function} newMethod The handler that will be raised when the hub method is invoked.
*/
public on(methodName: string, newMethod: (...args: any[]) => void) { public on(methodName: string, newMethod: (...args: any[]) => void) {
if (!methodName || !newMethod) { if (!methodName || !newMethod) {
return; return;
@ -183,7 +229,22 @@ export class HubConnection {
this.methods[methodName].push(newMethod); this.methods[methodName].push(newMethod);
} }
public off(methodName: string, method?: (...args: any[]) => void) { /** Removes all handlers for the specified hub method.
*
* @param {string} methodName The name of the method to remove handlers for.
*/
public off(methodName: string): void;
/** Removes the specified handler for the specified hub method.
*
* You must pass the exact same Function instance as was previously passed to {@link on}. Passing a different instance (even if the function
* body is the same) will not remove the handler.
*
* @param {string} methodName The name of the method to remove handlers for.
* @param {Function} method The handler to remove. This must be the same Function instance as the one passed to {@link on}.
*/
public off(methodName: string, method: (...args: any[]) => void): void;
public off(methodName: string, method?: (...args: any[]) => void): void {
if (!methodName) { if (!methodName) {
return; return;
} }
@ -207,6 +268,10 @@ export class HubConnection {
} }
/** Registers a handler that will be invoked when the connection is closed.
*
* @param {Function} callback The handler that will be invoked when the connection is closed. Optionally receives a single argument containing the error that caused the connection to close (if any).
*/
public onclose(callback: (error?: Error) => void) { public onclose(callback: (error?: Error) => void) {
if (callback) { if (callback) {
this.closedCallbacks.push(callback); this.closedCallbacks.push(callback);

View File

@ -11,6 +11,7 @@ import { JsonHubProtocol } from "./JsonHubProtocol";
import { NullLogger } from "./Loggers"; import { NullLogger } from "./Loggers";
import { Arg, ConsoleLogger } from "./Utils"; import { Arg, ConsoleLogger } from "./Utils";
/** A builder for configuring {@link HubConnection} instances. */
export class HubConnectionBuilder { export class HubConnectionBuilder {
/** @internal */ /** @internal */
public protocol: IHubProtocol; public protocol: IHubProtocol;
@ -21,6 +22,19 @@ export class HubConnectionBuilder {
/** @internal */ /** @internal */
public logger: ILogger; public logger: ILogger;
/** Configures console logging for the {@link HubConnection}.
*
* @param {LogLevel} logLevel The minimum level of messages to log. Anything at this level, or a more severe level, will be logged.
* @returns The {@link HubConnectionBuilder} instance, for chaining.
*/
public configureLogging(logLevel: LogLevel): HubConnectionBuilder;
/** Configures custom logging for the {@link HubConnection}.
*
* @param {ILogger} logger An object implementing the {@link ILogger} interface, which will be used to write all log messages.
* @returns The {@link HubConnectionBuilder} instance, for chaining.
*/
public configureLogging(logger: ILogger): HubConnectionBuilder;
public configureLogging(logging: LogLevel | ILogger): HubConnectionBuilder { public configureLogging(logging: LogLevel | ILogger): HubConnectionBuilder {
Arg.isRequired(logging, "logging"); Arg.isRequired(logging, "logging");
@ -33,9 +47,30 @@ export class HubConnectionBuilder {
return this; return this;
} }
/** Configures the {@link HubConnection} to use HTTP-based transports to connect to the specified URL.
*
* The transport will be selected automatically based on what the server and client support.
*
* @param {string} url The URL the connection will use.
* @returns The {@link HubConnectionBuilder} instance, for chaining.
*/
public withUrl(url: string): HubConnectionBuilder; public withUrl(url: string): HubConnectionBuilder;
public withUrl(url: string, options: IHttpConnectionOptions): HubConnectionBuilder;
/** Configures the {@link HubConnection} to use the specified HTTP-based transport to connect to the specified URL.
*
* @param {string} url The URL the connection will use.
* @param {HttpTransportType} transportType The specific transport to use.
* @returns The {@link HubConnectionBuilder} instance, for chaining.
*/
public withUrl(url: string, transportType: HttpTransportType): HubConnectionBuilder; public withUrl(url: string, transportType: HttpTransportType): HubConnectionBuilder;
/** Configures the {@link HubConnection} to use HTTP-based transports to connect to the specified URL.
*
* @param {string} url The URL the connection will use.
* @param {IHttpConnectionOptions} options An options object used to configure the connection.
* @returns The {@link HubConnectionBuilder} instance, for chaining.
*/
public withUrl(url: string, options: IHttpConnectionOptions): HubConnectionBuilder;
public withUrl(url: string, transportTypeOrOptions?: IHttpConnectionOptions | HttpTransportType): HubConnectionBuilder { public withUrl(url: string, transportTypeOrOptions?: IHttpConnectionOptions | HttpTransportType): HubConnectionBuilder {
Arg.isRequired(url, "url"); Arg.isRequired(url, "url");
@ -54,6 +89,10 @@ export class HubConnectionBuilder {
return this; return this;
} }
/** Configures the {@link HubConnection} to use the specified Hub Protocol.
*
* @param {IHubProtocol} protocol The {@link IHubProtocol} implementation to use.
*/
public withHubProtocol(protocol: IHubProtocol): HubConnectionBuilder { public withHubProtocol(protocol: IHubProtocol): HubConnectionBuilder {
Arg.isRequired(protocol, "protocol"); Arg.isRequired(protocol, "protocol");
@ -61,6 +100,10 @@ export class HubConnectionBuilder {
return this; return this;
} }
/** Creates a {@link HubConnection} from the configuration options specified in this builder.
*
* @returns {HubConnection} The configured {@link HubConnection}.
*/
public build(): HubConnection { public build(): HubConnection {
// If httpConnectionOptions has a logger, use it. Otherwise, override it with the one // If httpConnectionOptions has a logger, use it. Otherwise, override it with the one
// provided to configureLogger // provided to configureLogger

View File

@ -5,11 +5,37 @@ import { HttpClient } from "./HttpClient";
import { ILogger, LogLevel } from "./ILogger"; import { ILogger, LogLevel } from "./ILogger";
import { HttpTransportType, ITransport } from "./ITransport"; import { HttpTransportType, ITransport } from "./ITransport";
/** Options provided to the 'withUrl' method on {@link HubConnectionBuilder} to configure options for the HTTP-based transports. */
export interface IHttpConnectionOptions { export interface IHttpConnectionOptions {
/** An {@link HttpClient} that will be used to make HTTP requests. */
httpClient?: HttpClient; httpClient?: HttpClient;
/** An {@link HttpTransportType} value specifying the transport to use for the connection. */
transport?: HttpTransportType | ITransport; transport?: HttpTransportType | ITransport;
/** Configures the logger used for logging.
*
* Provide an {@link ILogger} instance, and log messages will be logged via that instance. Alternatively, provide a value from
* the {@link LogLevel} enumeration and a default logger which logs to the Console will be configured to log messages of the specified
* level (or higher).
*/
logger?: ILogger | LogLevel; logger?: ILogger | LogLevel;
accessTokenFactory?: () => string | Promise<string>;
/** A function that provides an access token required for HTTP Bearer authentication.
*
* @returns {string | Promise<string>} A string containing the access token, or a Promise that resolves to a string containing the access token.
*/
accessTokenFactory?(): string | Promise<string>;
/** A boolean indicating if message content should be logged.
*
* Message content can contain sensitive user data, so this is disabled by default.
*/
logMessageContent?: boolean; logMessageContent?: boolean;
/** A boolean indicating if negotiation should be skipped.
*
* Negotiation can only be skipped when the {@link transport} property is set to 'HttpTransportType.WebSockets'.
*/
skipNegotiation?: boolean; skipNegotiation?: boolean;
} }

View File

@ -4,69 +4,162 @@
import { ILogger } from "./ILogger"; import { ILogger } from "./ILogger";
import { TransferFormat } from "./ITransport"; import { TransferFormat } from "./ITransport";
export const enum MessageType { /** Defines the type of a Hub Message. */
export enum MessageType {
/** Indicates the message is an Invocation message and implements the {@link InvocationMessage} interface. */
Invocation = 1, Invocation = 1,
/** Indicates the message is a StreamItem message and implements the {@link StreamItemMessage} interface. */
StreamItem = 2, StreamItem = 2,
/** Indicates the message is a Completion message and implements the {@link CompletionMessage} interface. */
Completion = 3, Completion = 3,
/** Indicates the message is a Stream Invocation message and implements the {@link StreamInvocationMessage} interface. */
StreamInvocation = 4, StreamInvocation = 4,
/** Indicates the message is a Cancel Invocation message and implements the {@link CancelInvocationMessage} interface. */
CancelInvocation = 5, CancelInvocation = 5,
/** Indicates the message is a Ping message and implements the {@link PingMessage} interface. */
Ping = 6, Ping = 6,
/** Indicates the message is a Close message and implements the {@link CloseMessage} interface. */
Close = 7, Close = 7,
} }
export interface MessageHeaders { [key: string]: string; } /** Defines a dictionary of string keys and string values representing headers attached to a Hub message. */
export interface MessageHeaders {
/** Gets or sets the header with the specified key. */
[key: string]: string;
}
export type HubMessage = InvocationMessage | StreamInvocationMessage | StreamItemMessage | CompletionMessage | CancelInvocationMessage | PingMessage | CloseMessage; /** Union type of all known Hub messages. */
export type HubMessage =
InvocationMessage |
StreamInvocationMessage |
StreamItemMessage |
CompletionMessage |
CancelInvocationMessage |
PingMessage |
CloseMessage;
/** Defines properties common to all Hub messages. */
export interface HubMessageBase { export interface HubMessageBase {
/** A {@link MessageType} value indicating the type of this message. */
readonly type: MessageType; readonly type: MessageType;
} }
/** Defines properties common to all Hub messages relating to a specific invocation. */
export interface HubInvocationMessage extends HubMessageBase { export interface HubInvocationMessage extends HubMessageBase {
/** A {@link MessageHeaders} dictionary containing headers attached to the message. */
readonly headers?: MessageHeaders; readonly headers?: MessageHeaders;
/** The ID of the invocation relating to this message.
*
* This is expected to be present for {@link StreamInvocationMessage} and {@link CompletionMessage}. It may
* be 'undefined' for an {@link InvocationMessage} if the sender does not expect a response.
*/
readonly invocationId?: string; readonly invocationId?: string;
} }
/** A hub message representing a non-streaming invocation. */
export interface InvocationMessage extends HubInvocationMessage { export interface InvocationMessage extends HubInvocationMessage {
readonly type: MessageType.Invocation; readonly type: MessageType.Invocation;
/** The target method name. */
readonly target: string; readonly target: string;
/** The target method arguments. */
readonly arguments: any[]; readonly arguments: any[];
} }
/** A hub message representing a streaming invocation. */
export interface StreamInvocationMessage extends HubInvocationMessage { export interface StreamInvocationMessage extends HubInvocationMessage {
/** @inheritDoc */
readonly type: MessageType.StreamInvocation; readonly type: MessageType.StreamInvocation;
/** The invocation ID. */
readonly invocationId: string;
/** The target method name. */
readonly target: string; readonly target: string;
/** The target method arguments. */
readonly arguments: any[]; readonly arguments: any[];
} }
/** A hub message representing a single item produced as part of a result stream. */
export interface StreamItemMessage extends HubInvocationMessage { export interface StreamItemMessage extends HubInvocationMessage {
/** @inheritDoc */
readonly type: MessageType.StreamItem; readonly type: MessageType.StreamItem;
/** The invocation ID. */
readonly invocationId: string;
/** The item produced by the server. */
readonly item?: any; readonly item?: any;
} }
/** A hub message representing the result of an invocation. */
export interface CompletionMessage extends HubInvocationMessage { export interface CompletionMessage extends HubInvocationMessage {
/** @inheritDoc */
readonly type: MessageType.Completion; readonly type: MessageType.Completion;
/** The invocation ID. */
readonly invocationId: string;
/** The error produced by the invocation, if any.
*
* Either {@link error} or {@link result} must be defined, but not both.
*/
readonly error?: string; readonly error?: string;
/** The result produced by the invocation, if any.
*
* Either {@link error} or {@link result} must be defined, but not both.
*/
readonly result?: any; readonly result?: any;
} }
/** A hub message indicating that the sender is still active. */
export interface PingMessage extends HubMessageBase { export interface PingMessage extends HubMessageBase {
/** @inheritDoc */
readonly type: MessageType.Ping; readonly type: MessageType.Ping;
} }
/** A hub message indicating that the sender is closing the connection.
*
* If {@link error} is defined, the sender is closing the connection due to an error.
*/
export interface CloseMessage extends HubMessageBase { export interface CloseMessage extends HubMessageBase {
/** @inheritDoc */
readonly type: MessageType.Close; readonly type: MessageType.Close;
/** The error that triggered the close, if any.
*
* If this property is undefined, the connection was closed normally and without error.
*/
readonly error?: string; readonly error?: string;
} }
/** A hub message sent to request that a streaming invocation be canceled. */
export interface CancelInvocationMessage extends HubInvocationMessage { export interface CancelInvocationMessage extends HubInvocationMessage {
/** @inheritDoc */
readonly type: MessageType.CancelInvocation; readonly type: MessageType.CancelInvocation;
/** The invocation ID. */
readonly invocationId: string;
} }
/** A protocol abstraction for communicating with SignalR Hubs. */
export interface IHubProtocol { export interface IHubProtocol {
/** The name of the protocol. This is used by SignalR to resolve the protocol between the client and server. */
readonly name: string; readonly name: string;
/** The version of the protocol. */
readonly version: number; readonly version: number;
/** The {@link TransferFormat} of the protocol. */
readonly transferFormat: TransferFormat; readonly transferFormat: TransferFormat;
parseMessages(input: any, logger: ILogger): HubMessage[];
writeMessage(message: HubMessage): any; /** Creates an array of {@link HubMessage} objects from the specified serialized representation.
*
* If {@link transferFormat} is 'Text', the {@link input} parameter must be a string, otherwise it must be an ArrayBuffer.
*
* @param {string | ArrayBuffer} input A string, or ArrayBuffer containing the serialized representation.
* @param {ILogger} logger A logger that will be used to log messages that occur during parsing.
*/
parseMessages(input: string | ArrayBuffer, logger: ILogger): HubMessage[];
/** Writes the specified {@link HubMessage} to a string or ArrayBuffer and returns it.
*
* If {@link transferFormat} is 'Text', the result of this method will be a string, otherwise it will be an ArrayBuffer.
*
* @param {HubMessage} message The message to write.
* @returns {string | ArrayBuffer} A string or ArrayBuffer containing the serialized representation of the message.
*/
writeMessage(message: HubMessage): string | ArrayBuffer;
} }

View File

@ -2,16 +2,33 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// These values are designed to match the ASP.NET Log Levels since that's the pattern we're emulating here. // These values are designed to match the ASP.NET Log Levels since that's the pattern we're emulating here.
/** Indicates the severity of a log message.
*
* Log Levels are ordered in increasing severity. So `Debug` is more severe than `Trace`, etc.
*/
export enum LogLevel { export enum LogLevel {
/** Log level for very low severity diagnostic messages. */
Trace = 0, Trace = 0,
/** Log level for low severity diagnostic messages. */
Debug = 1, Debug = 1,
/** Log level for informational diagnostic messages. */
Information = 2, Information = 2,
/** Log level for diagnostic messages that indicate a non-fatal problem. */
Warning = 3, Warning = 3,
/** Log level for diagnostic messages that indicate a failure in the current operation. */
Error = 4, Error = 4,
/** Log level for diagnostic messages that indicate a failure that will terminate the entire application. */
Critical = 5, Critical = 5,
/** The highest possible log level. Used when configuring logging to indicate that no log messages should be emitted. */
None = 6, None = 6,
} }
/** An abstraction that provides a sink for diagnostic messages. */
export interface ILogger { export interface ILogger {
/** Called by the framework to emit a diagnostic message.
*
* @param {LogLevel} logLevel The severity level of the message.
* @param {string} message The message.
*/
log(logLevel: LogLevel, message: string): void; log(logLevel: LogLevel, message: string): void;
} }

View File

@ -1,17 +1,25 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
/** Specifies a specific HTTP transport type. */
export enum HttpTransportType { export enum HttpTransportType {
/** Specifies the WebSockets transport. */
WebSockets, WebSockets,
/** Specifies the Server-Sent Events transport. */
ServerSentEvents, ServerSentEvents,
/** Specifies the Long Polling transport. */
LongPolling, LongPolling,
} }
/** Specifies the transfer format for a connection. */
export enum TransferFormat { export enum TransferFormat {
/** Specifies that only text data will be transmitted over the connection. */
Text = 1, Text = 1,
/** Specifies that binary data will be transmitted over the connection. */
Binary, Binary,
} }
/** An abstraction over the behavior of transports. This is designed to support the framework and not intended for use by applications. */
export interface ITransport { export interface ITransport {
connect(url: string, transferFormat: TransferFormat): Promise<void>; connect(url: string, transferFormat: TransferFormat): Promise<void>;
send(data: any): Promise<void>; send(data: any): Promise<void>;

View File

@ -7,16 +7,30 @@ import { TransferFormat } from "./ITransport";
import { NullLogger } from "./Loggers"; import { NullLogger } from "./Loggers";
import { TextMessageFormat } from "./TextMessageFormat"; import { TextMessageFormat } from "./TextMessageFormat";
export const JSON_HUB_PROTOCOL_NAME: string = "json"; const JSON_HUB_PROTOCOL_NAME: string = "json";
/** Implements the JSON Hub Protocol. */
export class JsonHubProtocol implements IHubProtocol { export class JsonHubProtocol implements IHubProtocol {
/** @inheritDoc */
public readonly name: string = JSON_HUB_PROTOCOL_NAME; public readonly name: string = JSON_HUB_PROTOCOL_NAME;
/** @inheritDoc */
public readonly version: number = 1; public readonly version: number = 1;
/** @inheritDoc */
public readonly transferFormat: TransferFormat = TransferFormat.Text; public readonly transferFormat: TransferFormat = TransferFormat.Text;
/** Creates an array of {@link HubMessage} objects from the specified serialized representation.
*
* @param {string} input A string containing the serialized representation.
* @param {ILogger} logger A logger that will be used to log messages that occur during parsing.
*/
public parseMessages(input: string, logger: ILogger): HubMessage[] { public parseMessages(input: string, logger: ILogger): HubMessage[] {
// The interface does allow "ArrayBuffer" to be passed in, but this implementation does not. So let's throw a useful error.
if (typeof input !== "string") {
throw new Error("Invalid input for JSON hub protocol. Expected a string.");
}
if (!input) { if (!input) {
return []; return [];
} }
@ -61,6 +75,11 @@ export class JsonHubProtocol implements IHubProtocol {
return hubMessages; return hubMessages;
} }
/** Writes the specified {@link HubMessage} to a string and returns it.
*
* @param {HubMessage} message The message to write.
* @returns {string} A string containing the serialized representation of the message.
*/
public writeMessage(message: HubMessage): string { public writeMessage(message: HubMessage): string {
return TextMessageFormat.write(JSON.stringify(message)); return TextMessageFormat.write(JSON.stringify(message));
} }

View File

@ -3,11 +3,14 @@
import { ILogger, LogLevel } from "./ILogger"; import { ILogger, LogLevel } from "./ILogger";
/** A logger that does nothing when log messages are sent to it. */
export class NullLogger implements ILogger { export class NullLogger implements ILogger {
/** The singleton instance of the {@link NullLogger}. */
public static instance: ILogger = new NullLogger(); public static instance: ILogger = new NullLogger();
private constructor() {} private constructor() {}
/** @inheritDoc */
public log(logLevel: LogLevel, message: string): void { public log(logLevel: LogLevel, message: string): void {
} }
} }

View File

@ -7,17 +7,45 @@
// depend on RxJS in the core library, so instead we duplicate the minimum logic needed and then users can easily adapt these into // depend on RxJS in the core library, so instead we duplicate the minimum logic needed and then users can easily adapt these into
// proper RxJS observables if they want. // proper RxJS observables if they want.
/** Defines the expected type for a receiver of results streamed by the server.
*
* @typeparam T The type of the items being sent by the server.
*/
export interface IStreamSubscriber<T> { export interface IStreamSubscriber<T> {
/** A boolean that will be set by the {@link IStreamResult} when the stream is closed. */
closed?: boolean; closed?: boolean;
/** Called by the framework when a new item is available. */
next(value: T): void; next(value: T): void;
/** Called by the framework when an error has occurred.
*
* After this method is called, no additional methods on the {@link IStreamSubscriber} will be called.
*/
error(err: any): void; error(err: any): void;
/** Called by the framework when the end of the stream is reached.
*
* After this method is called, no additional methods on the {@link IStreamSubscriber} will be called.
*/
complete(): void; complete(): void;
} }
/** Defines the result of a streaming hub method.
*
* @typeparam T The type of the items being sent by the server.
*/
export interface IStreamResult<T> { export interface IStreamResult<T> {
subscribe(observer: IStreamSubscriber<T>): ISubscription<T>; /** Attaches a {@link IStreamSubscriber}, which will be invoked when new items are available from the stream.
*
* @param {IStreamSubscriber<T>} observer The subscriber to attach.
* @returns {ISubscription<T>} A subscription that can be disposed to terminate the stream and stop calling methods on the {@link IStreamSubscriber}.
*/
subscribe(subscriber: IStreamSubscriber<T>): ISubscription<T>;
} }
/** An interface that allows an {@link IStreamSubscriber} to be disconnected from a stream.
*
* @typeparam T The type of the items being sent by the server.
*/
export interface ISubscription<T> { export interface ISubscription<T> {
/** Disconnects the {@link IStreamSubscriber} associated with this subscription from the stream. */
dispose(): void; dispose(): void;
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Not exported from index
export class TextMessageFormat { export class TextMessageFormat {
public static RecordSeparatorCode = 0x1e; public static RecordSeparatorCode = 0x1e;
public static RecordSeparator = String.fromCharCode(TextMessageFormat.RecordSeparatorCode); public static RecordSeparator = String.fromCharCode(TextMessageFormat.RecordSeparatorCode);

View File

@ -2,14 +2,15 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Everything that users need to access must be exported here. Including interfaces. // Everything that users need to access must be exported here. Including interfaces.
export * from "./Errors"; export { AbortSignal } from "./AbortController";
export * from "./HttpClient"; export { HttpError, TimeoutError } from "./Errors";
export * from "./IHttpConnectionOptions"; export { DefaultHttpClient, HttpClient, HttpRequest, HttpResponse } from "./HttpClient";
export * from "./HubConnection"; export { IHttpConnectionOptions } from "./IHttpConnectionOptions";
export * from "./HubConnectionBuilder"; export { HubConnection } from "./HubConnection";
export * from "./IHubProtocol"; export { HubConnectionBuilder } from "./HubConnectionBuilder";
export * from "./ILogger"; export { MessageType, MessageHeaders, HubMessage, HubMessageBase, HubInvocationMessage, InvocationMessage, StreamInvocationMessage, StreamItemMessage, CompletionMessage, PingMessage, CloseMessage, CancelInvocationMessage, IHubProtocol } from "./IHubProtocol";
export * from "./ITransport"; export { ILogger, LogLevel } from "./ILogger";
export * from "./Stream"; export { HttpTransportType, TransferFormat, ITransport } from "./ITransport";
export * from "./Loggers"; export { IStreamSubscriber, IStreamResult, ISubscription } from "./Stream";
export * from "./JsonHubProtocol"; export { NullLogger } from "./Loggers";
export { JsonHubProtocol } from "./JsonHubProtocol";