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.
// 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 {
// 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";
// TypeDoc's @inheritDoc and @link don't work across modules :(
/** Implements the MessagePack Hub Protocol */
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";
/** The version of the protocol. */
public readonly version: number = 1;
/** The TransferFormat of the protocol. */
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[] {
// 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) {
logger = NullLogger.instance;
}
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 {
if (input.length === 0) {
throw new Error("Invalid payload.");
@ -154,20 +187,6 @@ export class MessagePackHubProtocol implements IHubProtocol {
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 {
const msgpack = msgpack5();
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 version: number = 1;
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.");
}
public writeMessage(message: HubMessage) {
public writeMessage(message: HubMessage): string | ArrayBuffer {
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
return new HubConnectionBuilder()
.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
// it's a very new API right now.
// Not exported from index.
export class AbortController implements AbortSignal {
private isAborted: boolean = false;
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 {
/** 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;
}

View File

@ -1,10 +1,19 @@
// 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.
/** Error thrown when an HTTP request fails. */
export class HttpError extends Error {
// tslint:disable-next-line:variable-name
private __proto__: Error;
/** The HTTP status code represented by this error. */
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) {
const trueProto = new.target.prototype;
super(errorMessage);
@ -16,9 +25,15 @@ export class HttpError extends Error {
}
}
/** Error thrown when a timeout elapses. */
export class TimeoutError extends Error {
// tslint:disable-next-line:variable-name
private __proto__: Error;
/** Constructs a new instance of {@link TimeoutError}.
*
* @param {string} errorMessage A descriptive error message.
*/
constructor(errorMessage: string = "A timeout occurred.") {
const trueProto = new.target.prototype;
super(errorMessage);

View File

@ -5,20 +5,59 @@ import { AbortSignal } from "./AbortController";
import { HttpError, TimeoutError } from "./Errors";
import { ILogger, LogLevel } from "./ILogger";
/** Represents an HTTP request. */
export interface HttpRequest {
/** The HTTP method to use for the request. */
method?: string;
/** The URL for the request. */
url?: string;
/** The body content for the request. May be a string or an ArrayBuffer (for binary data). */
content?: string | ArrayBuffer;
/** An object describing headers to apply to the request. */
headers?: { [key: string]: string };
/** The XMLHttpRequestResponseType to apply to the request. */
responseType?: XMLHttpRequestResponseType;
/** An AbortSignal that can be monitored for cancellation. */
abortSignal?: AbortSignal;
/** The time to wait for the request to complete before throwing a TimeoutError. Measured in milliseconds. */
timeout?: number;
}
/** Represents an HTTP response. */
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);
/** 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);
/** 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);
/** 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(
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 {
/** 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>;
/** 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> {
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>;
/** 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> {
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>;
/** 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> {
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>;
}
/** Default implementation of {@link HttpClient}. */
export class DefaultHttpClient extends HttpClient {
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();
this.logger = logger;
}
/** @inheritDoc */
public send(request: HttpRequest): Promise<HttpResponse> {
return new Promise<HttpResponse>((resolve, reject) => {
const xhr = new XMLHttpRequest();

View File

@ -10,6 +10,7 @@ import { Arg, Subject } from "./Utils";
const DEFAULT_TIMEOUT_IN_MS: number = 30 * 1000;
/** Represents a connection to a SignalR Hub. */
export class HubConnection {
private readonly connection: IConnection;
private readonly logger: ILogger;
@ -22,6 +23,11 @@ export class HubConnection {
private timeoutHandle: NodeJS.Timer;
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;
/** @internal */
@ -54,6 +60,10 @@ export class HubConnection {
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> {
const handshakeRequest: HandshakeRequestMessage = {
protocol: this.protocol.name,
@ -77,6 +87,10 @@ export class HubConnection {
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> {
this.logger.log(LogLevel.Debug, "Stopping HubConnection.");
@ -84,6 +98,13 @@ export class HubConnection {
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> {
const invocationDescriptor = this.createStreamInvocation(methodName, args);
@ -124,6 +145,15 @@ export class HubConnection {
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> {
const invocationDescriptor = this.createInvocation(methodName, args, true);
@ -132,6 +162,17 @@ export class HubConnection {
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> {
const invocationDescriptor = this.createInvocation(methodName, args, false);
@ -165,6 +206,11 @@ export class HubConnection {
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) {
if (!methodName || !newMethod) {
return;
@ -183,7 +229,22 @@ export class HubConnection {
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) {
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) {
if (callback) {
this.closedCallbacks.push(callback);

View File

@ -11,6 +11,7 @@ import { JsonHubProtocol } from "./JsonHubProtocol";
import { NullLogger } from "./Loggers";
import { Arg, ConsoleLogger } from "./Utils";
/** A builder for configuring {@link HubConnection} instances. */
export class HubConnectionBuilder {
/** @internal */
public protocol: IHubProtocol;
@ -21,6 +22,19 @@ export class HubConnectionBuilder {
/** @internal */
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 {
Arg.isRequired(logging, "logging");
@ -33,9 +47,30 @@ export class HubConnectionBuilder {
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, 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;
/** 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 {
Arg.isRequired(url, "url");
@ -54,6 +89,10 @@ export class HubConnectionBuilder {
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 {
Arg.isRequired(protocol, "protocol");
@ -61,6 +100,10 @@ export class HubConnectionBuilder {
return this;
}
/** Creates a {@link HubConnection} from the configuration options specified in this builder.
*
* @returns {HubConnection} The configured {@link HubConnection}.
*/
public build(): HubConnection {
// If httpConnectionOptions has a logger, use it. Otherwise, override it with the one
// provided to configureLogger

View File

@ -5,11 +5,37 @@ import { HttpClient } from "./HttpClient";
import { ILogger, LogLevel } from "./ILogger";
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 {
/** An {@link HttpClient} that will be used to make HTTP requests. */
httpClient?: HttpClient;
/** An {@link HttpTransportType} value specifying the transport to use for the connection. */
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;
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;
/** 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;
}

View File

@ -4,69 +4,162 @@
import { ILogger } from "./ILogger";
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,
/** Indicates the message is a StreamItem message and implements the {@link StreamItemMessage} interface. */
StreamItem = 2,
/** Indicates the message is a Completion message and implements the {@link CompletionMessage} interface. */
Completion = 3,
/** Indicates the message is a Stream Invocation message and implements the {@link StreamInvocationMessage} interface. */
StreamInvocation = 4,
/** Indicates the message is a Cancel Invocation message and implements the {@link CancelInvocationMessage} interface. */
CancelInvocation = 5,
/** Indicates the message is a Ping message and implements the {@link PingMessage} interface. */
Ping = 6,
/** Indicates the message is a Close message and implements the {@link CloseMessage} interface. */
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 {
/** A {@link MessageType} value indicating the type of this message. */
readonly type: MessageType;
}
/** Defines properties common to all Hub messages relating to a specific invocation. */
export interface HubInvocationMessage extends HubMessageBase {
/** A {@link MessageHeaders} dictionary containing headers attached to the message. */
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;
}
/** A hub message representing a non-streaming invocation. */
export interface InvocationMessage extends HubInvocationMessage {
readonly type: MessageType.Invocation;
/** The target method name. */
readonly target: string;
/** The target method arguments. */
readonly arguments: any[];
}
/** A hub message representing a streaming invocation. */
export interface StreamInvocationMessage extends HubInvocationMessage {
/** @inheritDoc */
readonly type: MessageType.StreamInvocation;
/** The invocation ID. */
readonly invocationId: string;
/** The target method name. */
readonly target: string;
/** The target method arguments. */
readonly arguments: any[];
}
/** A hub message representing a single item produced as part of a result stream. */
export interface StreamItemMessage extends HubInvocationMessage {
/** @inheritDoc */
readonly type: MessageType.StreamItem;
/** The invocation ID. */
readonly invocationId: string;
/** The item produced by the server. */
readonly item?: any;
}
/** A hub message representing the result of an invocation. */
export interface CompletionMessage extends HubInvocationMessage {
/** @inheritDoc */
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;
/** The result produced by the invocation, if any.
*
* Either {@link error} or {@link result} must be defined, but not both.
*/
readonly result?: any;
}
/** A hub message indicating that the sender is still active. */
export interface PingMessage extends HubMessageBase {
/** @inheritDoc */
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 {
/** @inheritDoc */
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;
}
/** A hub message sent to request that a streaming invocation be canceled. */
export interface CancelInvocationMessage extends HubInvocationMessage {
/** @inheritDoc */
readonly type: MessageType.CancelInvocation;
/** The invocation ID. */
readonly invocationId: string;
}
/** A protocol abstraction for communicating with SignalR Hubs. */
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;
/** The version of the protocol. */
readonly version: number;
/** The {@link TransferFormat} of the protocol. */
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.
// 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 {
/** Log level for very low severity diagnostic messages. */
Trace = 0,
/** Log level for low severity diagnostic messages. */
Debug = 1,
/** Log level for informational diagnostic messages. */
Information = 2,
/** Log level for diagnostic messages that indicate a non-fatal problem. */
Warning = 3,
/** Log level for diagnostic messages that indicate a failure in the current operation. */
Error = 4,
/** Log level for diagnostic messages that indicate a failure that will terminate the entire application. */
Critical = 5,
/** The highest possible log level. Used when configuring logging to indicate that no log messages should be emitted. */
None = 6,
}
/** An abstraction that provides a sink for diagnostic messages. */
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;
}

View File

@ -1,17 +1,25 @@
// 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.
/** Specifies a specific HTTP transport type. */
export enum HttpTransportType {
/** Specifies the WebSockets transport. */
WebSockets,
/** Specifies the Server-Sent Events transport. */
ServerSentEvents,
/** Specifies the Long Polling transport. */
LongPolling,
}
/** Specifies the transfer format for a connection. */
export enum TransferFormat {
/** Specifies that only text data will be transmitted over the connection. */
Text = 1,
/** Specifies that binary data will be transmitted over the connection. */
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 {
connect(url: string, transferFormat: TransferFormat): Promise<void>;
send(data: any): Promise<void>;

View File

@ -7,16 +7,30 @@ import { TransferFormat } from "./ITransport";
import { NullLogger } from "./Loggers";
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 {
/** @inheritDoc */
public readonly name: string = JSON_HUB_PROTOCOL_NAME;
/** @inheritDoc */
public readonly version: number = 1;
/** @inheritDoc */
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[] {
// 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) {
return [];
}
@ -61,6 +75,11 @@ export class JsonHubProtocol implements IHubProtocol {
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 {
return TextMessageFormat.write(JSON.stringify(message));
}

View File

@ -3,11 +3,14 @@
import { ILogger, LogLevel } from "./ILogger";
/** A logger that does nothing when log messages are sent to it. */
export class NullLogger implements ILogger {
/** The singleton instance of the {@link NullLogger}. */
public static instance: ILogger = new NullLogger();
private constructor() {}
/** @inheritDoc */
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
// 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> {
/** A boolean that will be set by the {@link IStreamResult} when the stream is closed. */
closed?: boolean;
/** Called by the framework when a new item is available. */
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;
/** 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;
}
/** Defines the result of a streaming hub method.
*
* @typeparam T The type of the items being sent by the server.
*/
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> {
/** Disconnects the {@link IStreamSubscriber} associated with this subscription from the stream. */
dispose(): void;
}

View File

@ -1,6 +1,7 @@
// 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.
// Not exported from index
export class TextMessageFormat {
public static RecordSeparatorCode = 0x1e;
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.
// Everything that users need to access must be exported here. Including interfaces.
export * from "./Errors";
export * from "./HttpClient";
export * from "./IHttpConnectionOptions";
export * from "./HubConnection";
export * from "./HubConnectionBuilder";
export * from "./IHubProtocol";
export * from "./ILogger";
export * from "./ITransport";
export * from "./Stream";
export * from "./Loggers";
export * from "./JsonHubProtocol";
export { AbortSignal } from "./AbortController";
export { HttpError, TimeoutError } from "./Errors";
export { DefaultHttpClient, HttpClient, HttpRequest, HttpResponse } from "./HttpClient";
export { IHttpConnectionOptions } from "./IHttpConnectionOptions";
export { HubConnection } from "./HubConnection";
export { HubConnectionBuilder } from "./HubConnectionBuilder";
export { MessageType, MessageHeaders, HubMessage, HubMessageBase, HubInvocationMessage, InvocationMessage, StreamInvocationMessage, StreamItemMessage, CompletionMessage, PingMessage, CloseMessage, CancelInvocationMessage, IHubProtocol } from "./IHubProtocol";
export { ILogger, LogLevel } from "./ILogger";
export { HttpTransportType, TransferFormat, ITransport } from "./ITransport";
export { IStreamSubscriber, IStreamResult, ISubscription } from "./Stream";
export { NullLogger } from "./Loggers";
export { JsonHubProtocol } from "./JsonHubProtocol";