From 050a1ecf27aad91c5d60000ccd7d83051cc571af Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Thu, 19 Apr 2018 10:12:09 -0700 Subject: [PATCH 1/2] fix package.json versions (#2098) --- clients/ts/signalr-protocol-msgpack/package.json | 2 +- clients/ts/signalr/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/ts/signalr-protocol-msgpack/package.json b/clients/ts/signalr-protocol-msgpack/package.json index 0dd9d8f47d..44e6b6a381 100644 --- a/clients/ts/signalr-protocol-msgpack/package.json +++ b/clients/ts/signalr-protocol-msgpack/package.json @@ -1,6 +1,6 @@ { "name": "@aspnet/signalr-protocol-msgpack", - "version": "1.0.0-preview3-t000", + "version": "1.0.0-rc1-t000", "description": "MsgPack Protocol support for ASP.NET Core SignalR", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", diff --git a/clients/ts/signalr/package.json b/clients/ts/signalr/package.json index 4674918d1a..eb594352d1 100644 --- a/clients/ts/signalr/package.json +++ b/clients/ts/signalr/package.json @@ -1,6 +1,6 @@ { "name": "@aspnet/signalr", - "version": "1.0.0-preview3-t000", + "version": "1.0.0-rc1-t000", "description": "ASP.NET Core SignalR Client", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.js", From 65adcdd73ac0bb93b9f192a4916eb52b08cdf467 Mon Sep 17 00:00:00 2001 From: Andrew Stanton-Nurse Date: Thu, 19 Apr 2018 12:09:47 -0700 Subject: [PATCH 2/2] HubConnectionBuilder.ts (#2063) --- clients/ts/FunctionalTests/package.json | 4 +- .../{tsconfig.json => tsconfig-selenium.json} | 0 .../FunctionalTests/ts/HubConnectionTests.ts | 203 +++++++-------- clients/ts/signalr/spec/HubConnection.spec.ts | 93 +++---- .../signalr/spec/HubConnectionBuilder.spec.ts | 245 ++++++++++++++++++ clients/ts/signalr/spec/TestHttpClient.ts | 2 +- clients/ts/signalr/spec/Utils.ts | 2 +- clients/ts/signalr/src/HttpClient.ts | 6 +- clients/ts/signalr/src/HubConnection.ts | 34 +-- .../ts/signalr/src/HubConnectionBuilder.ts | 88 +++++++ clients/ts/signalr/src/Utils.ts | 5 +- clients/ts/signalr/src/index.ts | 1 + clients/ts/tsconfig-base.json | 1 + samples/ChatSample/Views/Home/Index.cshtml | 5 +- samples/JwtSample/wwwroot/index.html | 5 +- samples/SignalRSamples/wwwroot/hubs.html | 9 +- samples/SignalRSamples/wwwroot/streaming.html | 5 +- 17 files changed, 512 insertions(+), 196 deletions(-) rename clients/ts/FunctionalTests/selenium/{tsconfig.json => tsconfig-selenium.json} (100%) create mode 100644 clients/ts/signalr/spec/HubConnectionBuilder.spec.ts create mode 100644 clients/ts/signalr/src/HubConnectionBuilder.ts diff --git a/clients/ts/FunctionalTests/package.json b/clients/ts/FunctionalTests/package.json index 37b8ffcb9c..0222f12fec 100644 --- a/clients/ts/FunctionalTests/package.json +++ b/clients/ts/FunctionalTests/package.json @@ -22,8 +22,8 @@ "build:rollup": "node ../node_modules/rollup/bin/rollup -c", "pretest": "npm run build", "test": "dotnet build && npm run test-only", - "test-only": "ts-node --project ./selenium/tsconfig.json ./selenium/run-tests.ts", - "ci-test": "ts-node --project ./selenium/tsconfig.json ./selenium/run-ci-tests.ts" + "test-only": "ts-node --project ./selenium/tsconfig-selenium.json ./selenium/run-tests.ts", + "ci-test": "ts-node --project ./selenium/tsconfig-selenium.json ./selenium/run-ci-tests.ts" }, "author": "", "license": "Apache-2.0" diff --git a/clients/ts/FunctionalTests/selenium/tsconfig.json b/clients/ts/FunctionalTests/selenium/tsconfig-selenium.json similarity index 100% rename from clients/ts/FunctionalTests/selenium/tsconfig.json rename to clients/ts/FunctionalTests/selenium/tsconfig-selenium.json diff --git a/clients/ts/FunctionalTests/ts/HubConnectionTests.ts b/clients/ts/FunctionalTests/ts/HubConnectionTests.ts index 91b2ee3ffb..bd539851d6 100644 --- a/clients/ts/FunctionalTests/ts/HubConnectionTests.ts +++ b/clients/ts/FunctionalTests/ts/HubConnectionTests.ts @@ -1,7 +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. -import { DefaultHttpClient, HttpClient, HttpRequest, HttpResponse, HttpTransportType, HubConnection, IHubConnectionOptions, IStreamSubscriber, JsonHubProtocol, LogLevel } from "@aspnet/signalr"; +import { DefaultHttpClient, HttpClient, HttpRequest, HttpResponse, HttpTransportType, HubConnection, HubConnectionBuilder, IHttpConnectionOptions, IStreamSubscriber, JsonHubProtocol, LogLevel } from "@aspnet/signalr"; import { MessagePackHubProtocol } from "@aspnet/signalr-protocol-msgpack"; import { eachTransport, eachTransportAndProtocol } from "./Common"; @@ -10,25 +10,35 @@ import { TestLogger } from "./TestLogger"; const TESTHUBENDPOINT_URL = "/testhub"; const TESTHUB_NOWEBSOCKETS_ENDPOINT_URL = "/testhub-nowebsockets"; -const commonOptions: IHubConnectionOptions = { - logMessageContent: true, - logger: TestLogger.instance, -}; - // On slower CI machines, these tests sometimes take longer than 5s jasmine.DEFAULT_TIMEOUT_INTERVAL = 10 * 1000; +const commonOptions: IHttpConnectionOptions = { + logMessageContent: true, +}; + +function getConnectionBuilder(transportType?: HttpTransportType, url?: string, options?: IHttpConnectionOptions): HubConnectionBuilder { + let actualOptions: IHttpConnectionOptions = options || {}; + if (transportType) { + actualOptions.transport = transportType; + } + actualOptions = { ...actualOptions, ...commonOptions }; + + return new HubConnectionBuilder() + .configureLogging(TestLogger.instance) + .withUrl(url || TESTHUBENDPOINT_URL, actualOptions); +} + describe("hubConnection", () => { eachTransportAndProtocol((transportType, protocol) => { describe("using " + protocol.name + " over " + HttpTransportType[transportType] + " transport", async () => { it("can invoke server method and receive result", (done) => { const message = "你好,世界!"; - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); + hubConnection.onclose((error) => { expect(error).toBeUndefined(); done(); @@ -51,11 +61,10 @@ describe("hubConnection", () => { it("can invoke server method non-blocking and not receive result", (done) => { const message = "你好,世界!"; - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); + hubConnection.onclose((error) => { expect(error).toBe(undefined); done(); @@ -74,11 +83,9 @@ describe("hubConnection", () => { }); it("can invoke server method structural object and receive structural result", (done) => { - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); hubConnection.on("CustomObject", (customObject) => { expect(customObject.Name).toBe("test"); @@ -100,11 +107,9 @@ describe("hubConnection", () => { }); it("can stream server method and receive result", (done) => { - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); hubConnection.onclose((error) => { expect(error).toBe(undefined); @@ -134,11 +139,9 @@ describe("hubConnection", () => { it("rethrows an exception from the server when invoking", (done) => { const errorMessage = "An unexpected error occurred invoking 'ThrowException' on the server. InvalidOperationException: An error occurred."; - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); hubConnection.start().then(() => { hubConnection.invoke("ThrowException", "An error occurred.").then(() => { @@ -158,11 +161,9 @@ describe("hubConnection", () => { }); it("throws an exception when invoking streaming method with invoke", (done) => { - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); hubConnection.start().then(() => { hubConnection.invoke("EmptyStream").then(() => { @@ -182,11 +183,9 @@ describe("hubConnection", () => { }); it("throws an exception when receiving a streaming result for method called with invoke", (done) => { - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); hubConnection.start().then(() => { hubConnection.invoke("Stream").then(() => { @@ -207,11 +206,9 @@ describe("hubConnection", () => { it("rethrows an exception from the server when streaming", (done) => { const errorMessage = "An unexpected error occurred invoking 'StreamThrowException' on the server. InvalidOperationException: An error occurred."; - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); hubConnection.start().then(() => { hubConnection.stream("StreamThrowException", "An error occurred.").subscribe({ @@ -236,11 +233,9 @@ describe("hubConnection", () => { }); it("throws an exception when invoking hub method with stream", (done) => { - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); hubConnection.start().then(() => { hubConnection.stream("Echo", "42").subscribe({ @@ -265,11 +260,9 @@ describe("hubConnection", () => { }); it("can receive server calls", (done) => { - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); const message = "你好 SignalR!"; @@ -297,11 +290,9 @@ describe("hubConnection", () => { }); it("can receive server calls without rebinding handler when restarted", (done) => { - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); const message = "你好 SignalR!"; @@ -354,11 +345,9 @@ describe("hubConnection", () => { }); it("closed with error if hub cannot be created", (done) => { - const hubConnection = new HubConnection("http://" + document.location.host + "/uncreatable", { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType, "http://" + document.location.host + "/uncreatable") + .withHubProtocol(protocol) + .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'."); @@ -368,11 +357,10 @@ describe("hubConnection", () => { }); it("can handle different types", (done) => { - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); + hubConnection.onclose((error) => { expect(error).toBe(undefined); done(); @@ -412,11 +400,10 @@ describe("hubConnection", () => { }); it("can receive different types", (done) => { - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); + hubConnection.onclose((error) => { expect(error).toBe(undefined); done(); @@ -458,11 +445,9 @@ describe("hubConnection", () => { it("can be restarted", (done) => { const message = "你好,世界!"; - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType) + .withHubProtocol(protocol) + .build(); let closeCount = 0; hubConnection.onclose((error) => { @@ -512,11 +497,11 @@ describe("hubConnection", () => { try { const jwtToken = await getJwtToken("http://" + document.location.host + "/generateJwtToken"); - const hubConnection = new HubConnection("/authorizedhub", { + + const hubConnection = getConnectionBuilder(transportType, "/authorizedhub", { accessTokenFactory: () => jwtToken, - ...commonOptions, - transport: transportType, - }); + }).build(); + hubConnection.onclose((error) => { expect(error).toBe(undefined); done(); @@ -539,11 +524,10 @@ describe("hubConnection", () => { const message = "你好,世界!"; try { - const hubConnection = new HubConnection("/authorizedhub", { + const hubConnection = getConnectionBuilder(transportType, "/authorizedhub", { accessTokenFactory: () => getJwtToken("http://" + document.location.host + "/generateJwtToken"), - ...commonOptions, - transport: transportType, - }); + }).build(); + hubConnection.onclose((error) => { expect(error).toBe(undefined); done(); @@ -566,11 +550,10 @@ describe("hubConnection", () => { const message = "你好,世界!"; try { - const hubConnection = new HubConnection("/authorizedhub", { + const hubConnection = getConnectionBuilder(transportType, "/authorizedhub", { accessTokenFactory: () => getJwtToken("http://" + document.location.host + "/generateJwtToken"), - ...commonOptions, - transport: transportType, - }); + }).build(); + hubConnection.onclose((error) => { expect(error).toBe(undefined); done(); @@ -591,11 +574,8 @@ describe("hubConnection", () => { if (transportType !== HttpTransportType.LongPolling) { it("terminates if no messages received within timeout interval", (done) => { - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - timeoutInMilliseconds: 100, - transport: transportType, - }); + const hubConnection = getConnectionBuilder(transportType).build(); + hubConnection.serverTimeoutInMilliseconds = 100; const timeout = setTimeout(200, () => { fail("Server timeout did not fire within expected interval"); @@ -615,10 +595,9 @@ describe("hubConnection", () => { if (typeof EventSource !== "undefined") { it("allows Server-Sent Events when negotiating for JSON protocol", async (done) => { - const hubConnection = new HubConnection(TESTHUB_NOWEBSOCKETS_ENDPOINT_URL, { - ...commonOptions, - protocol: new JsonHubProtocol(), - }); + const hubConnection = getConnectionBuilder(undefined, TESTHUB_NOWEBSOCKETS_ENDPOINT_URL) + .withHubProtocol(new JsonHubProtocol()) + .build(); try { await hubConnection.start(); @@ -633,10 +612,9 @@ describe("hubConnection", () => { } it("skips Server-Sent Events when negotiating for MessagePack protocol", async (done) => { - const hubConnection = new HubConnection(TESTHUB_NOWEBSOCKETS_ENDPOINT_URL, { - ...commonOptions, - protocol: new MessagePackHubProtocol(), - }); + const hubConnection = getConnectionBuilder(undefined, TESTHUB_NOWEBSOCKETS_ENDPOINT_URL) + .withHubProtocol(new MessagePackHubProtocol()) + .build(); try { await hubConnection.start(); @@ -657,10 +635,9 @@ describe("hubConnection", () => { throw new Error("Kick rocks"); }; - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, - protocol: new JsonHubProtocol(), - }); + const hubConnection = getConnectionBuilder() + .withHubProtocol(new JsonHubProtocol()) + .build(); try { await hubConnection.start(); @@ -693,10 +670,10 @@ describe("hubConnection", () => { } const testClient = new TestClient(); - const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, { - ...commonOptions, + const hubConnection = getConnectionBuilder(HttpTransportType.LongPolling, TESTHUBENDPOINT_URL, { httpClient: testClient, - }); + }).build(); + try { await hubConnection.start(); diff --git a/clients/ts/signalr/spec/HubConnection.spec.ts b/clients/ts/signalr/spec/HubConnection.spec.ts index dfbcce5ec2..a438bb8721 100644 --- a/clients/ts/signalr/spec/HubConnection.spec.ts +++ b/clients/ts/signalr/spec/HubConnection.spec.ts @@ -1,27 +1,27 @@ // 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. -import { HubConnection } from "../src/HubConnection"; -import { IHubConnectionOptions } from "../src/HubConnection"; +import { HubConnection, JsonHubProtocol } from "../src/HubConnection"; import { IConnection } from "../src/IConnection"; import { HubMessage, IHubProtocol, MessageType } from "../src/IHubProtocol"; import { ILogger, LogLevel } from "../src/ILogger"; import { HttpTransportType, ITransport, TransferFormat } from "../src/ITransport"; +import { NullLogger } from "../src/Loggers"; import { IStreamSubscriber } from "../src/Stream"; import { TextMessageFormat } from "../src/TextMessageFormat"; import { asyncit as it, captureException, delay, PromiseSource } from "./Utils"; -const commonOptions: IHubConnectionOptions = { - logger: null, -}; +function createHubConnection(connection: IConnection, logger?: ILogger, protocol?: IHubProtocol) { + return new HubConnection(connection, logger || NullLogger.instance, protocol || new JsonHubProtocol()); +} describe("HubConnection", () => { describe("start", () => { it("sends negotiation message", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); await hubConnection.start(); expect(connection.sentData.length).toBe(1); expect(JSON.parse(connection.sentData[0])).toEqual({ @@ -36,7 +36,7 @@ describe("HubConnection", () => { it("sends a non blocking invocation", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); const invokePromise = hubConnection.send("testMethod", "arg", 42) .catch((_) => { }); // Suppress exception and unhandled promise rejection warning. @@ -60,7 +60,7 @@ describe("HubConnection", () => { it("sends an invocation", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); const invokePromise = hubConnection.invoke("testMethod", "arg", 42) .catch((_) => { }); // Suppress exception and unhandled promise rejection warning. @@ -89,7 +89,7 @@ describe("HubConnection", () => { }; const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, { logger: null, protocol: mockProtocol }); + const hubConnection = createHubConnection(connection, null, mockProtocol); const data = "{}" + TextMessageFormat.RecordSeparator; @@ -108,7 +108,7 @@ describe("HubConnection", () => { }; const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, { logger: null, protocol: mockProtocol }); + const hubConnection = createHubConnection(connection, null, mockProtocol); // handshake response + message separator const data = [0x7b, 0x7d, 0x1e]; @@ -126,7 +126,7 @@ describe("HubConnection", () => { mockProtocol.onreceive = (d) => receivedProcotolData = d as ArrayBuffer; const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, { logger: null, protocol: mockProtocol }); + const hubConnection = createHubConnection(connection, null, mockProtocol); // handshake response + message separator + message pack message const data = [ @@ -151,7 +151,7 @@ describe("HubConnection", () => { mockProtocol.onreceive = (d) => receivedProcotolData = d as string; const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, { logger: null, protocol: mockProtocol }); + const hubConnection = createHubConnection(connection, null, mockProtocol); const data = "{}" + TextMessageFormat.RecordSeparator + "{\"type\":6}" + TextMessageFormat.RecordSeparator; @@ -162,7 +162,7 @@ describe("HubConnection", () => { it("rejects the promise when an error is received", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); const invokePromise = hubConnection.invoke("testMethod", "arg", 42); @@ -175,7 +175,7 @@ describe("HubConnection", () => { it("resolves the promise when a result is received", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); const invokePromise = hubConnection.invoke("testMethod", "arg", 42); @@ -188,7 +188,7 @@ describe("HubConnection", () => { it("completes pending invocations when stopped", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); @@ -202,7 +202,7 @@ describe("HubConnection", () => { it("completes pending invocations when connection is lost", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); @@ -226,7 +226,7 @@ describe("HubConnection", () => { }, } as ILogger; const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, { logger }); + const hubConnection = createHubConnection(connection, logger); connection.receiveHandshakeResponse(); @@ -250,7 +250,7 @@ describe("HubConnection", () => { }, } as ILogger; const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, { logger }); + const hubConnection = createHubConnection(connection, logger); connection.receiveHandshakeResponse(); @@ -271,7 +271,7 @@ describe("HubConnection", () => { it("all handlers can be unregistered with just the method name", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); @@ -304,7 +304,7 @@ describe("HubConnection", () => { it("a single handler can be unregistered with the method name and handler", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); @@ -337,7 +337,7 @@ describe("HubConnection", () => { it("can't register the same handler multiple times", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); @@ -359,7 +359,7 @@ describe("HubConnection", () => { it("callback invoked when servers invokes a method on the client", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); @@ -379,7 +379,7 @@ describe("HubConnection", () => { it("stop on handshake error", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); let closeError: Error = null; hubConnection.onclose((e) => closeError = e); @@ -391,7 +391,7 @@ describe("HubConnection", () => { it("stop on close message", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); let isClosed = false; let closeError: Error = null; @@ -412,7 +412,7 @@ describe("HubConnection", () => { it("stop on error close message", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); let isClosed = false; let closeError: Error = null; @@ -434,7 +434,7 @@ describe("HubConnection", () => { it("can have multiple callbacks", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); @@ -457,7 +457,7 @@ describe("HubConnection", () => { it("can unsubscribe from on", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); @@ -488,7 +488,7 @@ describe("HubConnection", () => { it("unsubscribing from non-existing callbacks no-ops", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); hubConnection.off("_", () => { }); hubConnection.on("message", (t) => { }); @@ -507,7 +507,7 @@ describe("HubConnection", () => { } as ILogger; const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, { logger }); + const hubConnection = createHubConnection(connection, logger); connection.receiveHandshakeResponse(); @@ -542,7 +542,7 @@ describe("HubConnection", () => { it("sends an invocation", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); const invokePromise = hubConnection.stream("testStream", "arg", 42); // Verify the message is sent @@ -563,7 +563,7 @@ describe("HubConnection", () => { it("completes with an error when an error is yielded", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); @@ -579,7 +579,7 @@ describe("HubConnection", () => { it("completes the observer when a completion is received", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); @@ -595,7 +595,7 @@ describe("HubConnection", () => { it("completes pending streams when stopped", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); const observer = new TestObserver(); hubConnection.stream("testMethod") .subscribe(observer); @@ -608,7 +608,7 @@ describe("HubConnection", () => { it("completes pending streams when connection is lost", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); const observer = new TestObserver(); hubConnection.stream("testMethod") .subscribe(observer); @@ -622,7 +622,7 @@ describe("HubConnection", () => { it("yields items as they arrive", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); @@ -646,7 +646,7 @@ describe("HubConnection", () => { it("does not require error function registered", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); const observer = hubConnection.stream("testMethod").subscribe(NullSubscriber.instance); // Typically this would be called by the transport @@ -657,7 +657,7 @@ describe("HubConnection", () => { it("does not require complete function registered", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); const observer = hubConnection.stream("testMethod").subscribe(NullSubscriber.instance); // Send completion to trigger observer.complete() @@ -667,7 +667,7 @@ describe("HubConnection", () => { it("can be canceled", () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); connection.receiveHandshakeResponse(); @@ -696,7 +696,7 @@ describe("HubConnection", () => { describe("onClose", () => { it("can have multiple callbacks", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); let invocations = 0; hubConnection.onclose((e) => invocations++); hubConnection.onclose((e) => invocations++); @@ -707,7 +707,7 @@ describe("HubConnection", () => { it("callbacks receive error", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); let error: Error; hubConnection.onclose((e) => error = e); @@ -718,7 +718,7 @@ describe("HubConnection", () => { it("ignores null callbacks", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); hubConnection.onclose(null); hubConnection.onclose(undefined); // Typically this would be called by the transport @@ -732,7 +732,7 @@ describe("HubConnection", () => { // Receive the ping mid-invocation so we can see that the rest of the flow works fine const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, commonOptions); + const hubConnection = createHubConnection(connection); const invokePromise = hubConnection.invoke("testMethod", "arg", 42); connection.receive({ type: MessageType.Ping }); @@ -743,7 +743,8 @@ describe("HubConnection", () => { it("does not terminate if messages are received", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, { ...commonOptions, timeoutInMilliseconds: 100 }); + const hubConnection = createHubConnection(connection); + hubConnection.serverTimeoutInMilliseconds = 100; const p = new PromiseSource(); hubConnection.onclose((e) => p.resolve(e)); @@ -768,7 +769,8 @@ describe("HubConnection", () => { it("does not timeout if message was received before HubConnection.start", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, { ...commonOptions, timeoutInMilliseconds: 100 }); + const hubConnection = createHubConnection(connection); + hubConnection.serverTimeoutInMilliseconds = 100; const p = new PromiseSource(); hubConnection.onclose((e) => p.resolve(e)); @@ -795,7 +797,8 @@ describe("HubConnection", () => { it("terminates if no messages received within timeout interval", async () => { const connection = new TestConnection(); - const hubConnection = new HubConnection(connection, { ...commonOptions, timeoutInMilliseconds: 100 }); + const hubConnection = createHubConnection(connection); + hubConnection.serverTimeoutInMilliseconds = 100; const p = new PromiseSource(); hubConnection.onclose((e) => p.resolve(e)); diff --git a/clients/ts/signalr/spec/HubConnectionBuilder.spec.ts b/clients/ts/signalr/spec/HubConnectionBuilder.spec.ts new file mode 100644 index 0000000000..45035ca414 --- /dev/null +++ b/clients/ts/signalr/spec/HubConnectionBuilder.spec.ts @@ -0,0 +1,245 @@ +// 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. + +import { HubConnectionBuilder } from "../src/HubConnectionBuilder"; + +import { HubConnection } from "../src"; +import { HttpRequest, HttpResponse } from "../src/HttpClient"; +import { IHttpConnectionOptions } from "../src/HttpConnection"; +import { HubMessage, IHubProtocol } from "../src/IHubProtocol"; +import { ILogger, LogLevel } from "../src/ILogger"; +import { TransferFormat, HttpTransportType } from "../src/ITransport"; +import { NullLogger } from "../src/Loggers"; +import { TestHttpClient } from "./TestHttpClient"; +import { asyncit as it, PromiseSource } from "./Utils"; + +const allTransportsNegotiateResponse = { + availableTransports: [ + { transport: "WebSockets", transferFormats: ["Text", "Binary"] }, + { transport: "ServerSentEvents", transferFormats: ["Text"] }, + { transport: "LongPolling", transferFormats: ["Text", "Binary"] }, + ], + connectionId: "abc123", +}; + +const longPollingNegotiateResponse = { + availableTransports: [ + { transport: "LongPolling", transferFormats: ["Text", "Binary"] }, + ], + connectionId: "abc123", +}; + +const commonHttpOptions: IHttpConnectionOptions = { + logMessageContent: true, +}; + +describe("HubConnectionBuilder", () => { + eachMissingValue((val, name) => { + it(`configureLogging throws if logger is ${name}`, () => { + const builder = new HubConnectionBuilder(); + expect(() => builder.configureLogging(val)).toThrow(new Error("The 'logging' argument is required.")); + }); + + it(`withUrl throws if url is ${name}`, () => { + const builder = new HubConnectionBuilder(); + expect(() => builder.withUrl(val)).toThrow(new Error("The 'url' argument is required.")); + }); + + it(`withHubProtocol throws if protocol is ${name}`, () => { + const builder = new HubConnectionBuilder(); + expect(() => builder.withHubProtocol(val)).toThrow(new Error("The 'protocol' argument is required.")); + }); + }); + + it("builds HubConnection with HttpConnection using provided URL", async () => { + const pollSent = new PromiseSource(); + const pollCompleted = new PromiseSource(); + const testClient = createTestClient(pollSent, pollCompleted.promise) + .on("POST", "http://example.com?id=abc123", (req) => { + // Respond from the poll with the handshake response + pollCompleted.resolve(new HttpResponse(204, "No Content", "{}")); + return new HttpResponse(202); + }); + const connection = createConnectionBuilder() + .withUrl("http://example.com", { + ...commonHttpOptions, + httpClient: testClient, + }) + .build(); + + // Start the connection + const closed = makeClosedPromise(connection); + await connection.start(); + + const pollRequest = await pollSent.promise; + expect(pollRequest.url).toMatch(/http:\/\/example.com\?id=abc123.*/); + + await closed; + }); + + it("can configure transport type", async () => { + const protocol = new TestProtocol(); + + const pollSent = new PromiseSource(); + const pollCompleted = new PromiseSource(); + const negotiateReceived = new PromiseSource(); + const testClient = createTestClient(pollSent, pollCompleted.promise, allTransportsNegotiateResponse); + + const builder = createConnectionBuilder() + .withUrl("http://example.com", HttpTransportType.WebSockets) + .withHubProtocol(protocol); + expect(builder.httpConnectionOptions.transport).toBe(HttpTransportType.WebSockets); + }); + + it("can configure hub protocol", async () => { + const protocol = new TestProtocol(); + + const pollSent = new PromiseSource(); + const pollCompleted = new PromiseSource(); + const negotiateReceived = new PromiseSource(); + const testClient = createTestClient(pollSent, pollCompleted.promise) + .on("POST", "http://example.com?id=abc123", (req) => { + // Respond from the poll with the handshake response + negotiateReceived.resolve(req); + pollCompleted.resolve(new HttpResponse(204, "No Content", "{}")); + return new HttpResponse(202); + }); + + const connection = createConnectionBuilder() + .withUrl("http://example.com", { + ...commonHttpOptions, + httpClient: testClient, + }) + .withHubProtocol(protocol) + .build(); + + // Start the connection + const closed = makeClosedPromise(connection); + await connection.start(); + + const negotiateRequest = await negotiateReceived.promise; + expect(negotiateRequest.content).toBe(`{"protocol":"${protocol.name}","version":1}\x1E`); + + await closed; + }); + + + it("allows logger to be replaced", async () => { + let loggedMessages = 0; + const logger = { + log() { + loggedMessages += 1; + } + } + const connection = createConnectionBuilder(logger) + .withUrl("http://example.com") + .build(); + + try { + await connection.start(); + } catch { + // Ignore failures + } + + expect(loggedMessages).toBeGreaterThan(0); + }); + + it("uses logger for both HttpConnection and HubConnection", async () => { + const logger = new CaptureLogger(); + const connection = createConnectionBuilder(logger) + .withUrl("http://example.com") + .build(); + + try { + await connection.start(); + } catch { + // Ignore failures + } + + // A HubConnection message + expect(logger.messages).toContain("Starting HubConnection."); + + // An HttpConnection message + expect(logger.messages).toContain("Starting connection with transfer format 'Text'."); + }); + + it("does not replace HttpConnectionOptions logger if provided", async () => { + const hubConnectionLogger = new CaptureLogger(); + const httpConnectionLogger = new CaptureLogger(); + const connection = createConnectionBuilder(hubConnectionLogger) + .withUrl("http://example.com", { logger: httpConnectionLogger }) + .build(); + + try { + await connection.start(); + } catch { + // Ignore failures + } + + // A HubConnection message + expect(hubConnectionLogger.messages).toContain("Starting HubConnection."); + expect(httpConnectionLogger.messages).not.toContain("Starting HubConnection."); + + // An HttpConnection message + expect(httpConnectionLogger.messages).toContain("Starting connection with transfer format 'Text'."); + expect(hubConnectionLogger.messages).not.toContain("Starting connection with transfer format 'Text'."); + }); +}); + +class CaptureLogger implements ILogger { + public readonly messages: string[] = []; + + public log(logLevel: LogLevel, message: string): void { + this.messages.push(message); + } +} + +class TestProtocol implements IHubProtocol { + public name: string = "test"; + public version: number = 1; + public transferFormat: TransferFormat = TransferFormat.Text; + public parseMessages(input: any, logger: ILogger): HubMessage[] { + throw new Error("Method not implemented."); + } + public writeMessage(message: HubMessage) { + throw new Error("Method not implemented."); + } +} + +function createConnectionBuilder(logger?: ILogger | LogLevel): HubConnectionBuilder { + // We don't want to spam test output with logs. This can be changed as needed + return new HubConnectionBuilder() + .configureLogging(logger || NullLogger.instance); +} + +function createTestClient(pollSent: PromiseSource, pollCompleted: Promise, negotiateResponse?: any): TestHttpClient { + let firstRequest = true; + return new TestHttpClient() + .on("POST", "http://example.com/negotiate", () => negotiateResponse || longPollingNegotiateResponse) + .on("GET", /http:\/\/example.com\?id=abc123&_=.*/, (req) => { + if (firstRequest) { + firstRequest = false; + return new HttpResponse(200); + } else { + pollSent.resolve(req); + return pollCompleted; + } + }); +} + +function makeClosedPromise(connection: HubConnection): Promise { + const closed = new PromiseSource(); + connection.onclose((error) => { + if (error) { + closed.reject(error); + } else { + closed.resolve(); + } + }); + return closed.promise; +} + +function eachMissingValue(callback: (val: undefined | null, name: string) => void) { + callback(null, "null"); + callback(undefined, "undefined"); +} diff --git a/clients/ts/signalr/spec/TestHttpClient.ts b/clients/ts/signalr/spec/TestHttpClient.ts index b591c1bebd..b369231865 100644 --- a/clients/ts/signalr/spec/TestHttpClient.ts +++ b/clients/ts/signalr/spec/TestHttpClient.ts @@ -3,7 +3,7 @@ import { HttpClient, HttpRequest, HttpResponse } from "../src/HttpClient"; -type TestHttpHandlerResult = any; +type TestHttpHandlerResult = string | HttpResponse | any; export type TestHttpHandler = (request: HttpRequest, next?: (request: HttpRequest) => Promise) => Promise | TestHttpHandlerResult; export class TestHttpClient extends HttpClient { diff --git a/clients/ts/signalr/spec/Utils.ts b/clients/ts/signalr/spec/Utils.ts index 46c397d12a..15b630d930 100644 --- a/clients/ts/signalr/spec/Utils.ts +++ b/clients/ts/signalr/spec/Utils.ts @@ -38,7 +38,7 @@ export function delay(durationInMilliseconds: number): Promise { return source.promise; } -export class PromiseSource { +export class PromiseSource { public promise: Promise; private resolver: (value?: T | PromiseLike) => void; diff --git a/clients/ts/signalr/src/HttpClient.ts b/clients/ts/signalr/src/HttpClient.ts index d56dfba12c..42e353eee2 100644 --- a/clients/ts/signalr/src/HttpClient.ts +++ b/clients/ts/signalr/src/HttpClient.ts @@ -16,12 +16,14 @@ export interface HttpRequest { } export class HttpResponse { + constructor(statusCode: number); + constructor(statusCode: number, statusText: string); constructor(statusCode: number, statusText: string, content: string); constructor(statusCode: number, statusText: string, content: ArrayBuffer); constructor( public readonly statusCode: number, - public readonly statusText: string, - public readonly content: string | ArrayBuffer) { + public readonly statusText?: string, + public readonly content?: string | ArrayBuffer) { } } diff --git a/clients/ts/signalr/src/HubConnection.ts b/clients/ts/signalr/src/HubConnection.ts index 5a0f13b41d..1ed9ab125e 100644 --- a/clients/ts/signalr/src/HubConnection.ts +++ b/clients/ts/signalr/src/HubConnection.ts @@ -10,15 +10,10 @@ import { JsonHubProtocol } from "./JsonHubProtocol"; import { NullLogger } from "./Loggers"; import { IStreamResult } from "./Stream"; import { TextMessageFormat } from "./TextMessageFormat"; -import { createLogger, Subject } from "./Utils"; +import { Arg, createLogger, Subject } from "./Utils"; export { JsonHubProtocol }; -export interface IHubConnectionOptions extends IHttpConnectionOptions { - protocol?: IHubProtocol; - timeoutInMilliseconds?: number; -} - const DEFAULT_TIMEOUT_IN_MS: number = 30 * 1000; export class HubConnection { @@ -31,27 +26,22 @@ export class HubConnection { private id: number; private closedCallbacks: Array<(error?: Error) => void>; private timeoutHandle: NodeJS.Timer; - private timeoutInMilliseconds: number; private receivedHandshakeResponse: boolean; - constructor(url: string, options?: IHubConnectionOptions); - constructor(connection: IConnection, options?: IHubConnectionOptions); - constructor(urlOrConnection: string | IConnection, options: IHubConnectionOptions = {}) { - options = options || {}; + public serverTimeoutInMilliseconds: number; - this.timeoutInMilliseconds = options.timeoutInMilliseconds || DEFAULT_TIMEOUT_IN_MS; + constructor(connection: IConnection, logger: ILogger, protocol: IHubProtocol) { + Arg.isRequired(connection, "connection"); + Arg.isRequired(logger, "logger"); + Arg.isRequired(protocol, "protocol"); - this.protocol = options.protocol || new JsonHubProtocol(); + this.serverTimeoutInMilliseconds = DEFAULT_TIMEOUT_IN_MS; + + this.logger = logger; + this.protocol = protocol; + this.connection = connection; this.handshakeProtocol = new HandshakeProtocol(); - if (typeof urlOrConnection === "string") { - this.connection = new HttpConnection(urlOrConnection, options); - } else { - this.connection = urlOrConnection; - } - - this.logger = createLogger(options.logger); - this.connection.onreceive = (data: any) => this.processIncomingData(data); this.connection.onclose = (error?: Error) => this.connectionClosed(error); @@ -293,7 +283,7 @@ export class HubConnection { private configureTimeout() { if (!this.connection.features || !this.connection.features.inherentKeepAlive) { // Set the timeout timer - this.timeoutHandle = setTimeout(() => this.serverTimeout(), this.timeoutInMilliseconds); + this.timeoutHandle = setTimeout(() => this.serverTimeout(), this.serverTimeoutInMilliseconds); } } diff --git a/clients/ts/signalr/src/HubConnectionBuilder.ts b/clients/ts/signalr/src/HubConnectionBuilder.ts new file mode 100644 index 0000000000..9dca525333 --- /dev/null +++ b/clients/ts/signalr/src/HubConnectionBuilder.ts @@ -0,0 +1,88 @@ +// 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. + +import { HttpConnection, IHttpConnectionOptions } from "./HttpConnection"; +import { HubConnection, JsonHubProtocol } from "./HubConnection"; +import { IHubProtocol } from "./IHubProtocol"; +import { ILogger, LogLevel } from "./ILogger"; +import { HttpTransportType } from "./ITransport"; +import { NullLogger } from "./Loggers"; +import { Arg, ConsoleLogger } from "./Utils"; + +export class HubConnectionBuilder { + /** @internal */ + public protocol: IHubProtocol; + /** @internal */ + public httpConnectionOptions: IHttpConnectionOptions; + /** @internal */ + public url: string; + /** @internal */ + public logger: ILogger; + + public configureLogging(logging: LogLevel | ILogger): HubConnectionBuilder { + Arg.isRequired(logging, "logging"); + + if (isLogger(logging)) { + this.logger = logging; + } else { + this.logger = new ConsoleLogger(logging); + } + + return this; + } + + public withUrl(url: string): HubConnectionBuilder; + public withUrl(url: string, options: IHttpConnectionOptions): HubConnectionBuilder; + public withUrl(url: string, transportType: HttpTransportType): HubConnectionBuilder; + public withUrl(url: string, transportTypeOrOptions?: IHttpConnectionOptions | HttpTransportType): HubConnectionBuilder { + Arg.isRequired(url, "url"); + + this.url = url; + + // Flow-typing knows where it's at. Since HttpTransportType is a number and IHttpConnectionOptions is guaranteed + // to be an object, we know (as does TypeScript) this comparison is all we need to figure out which overload was called. + if (typeof transportTypeOrOptions === "object") { + this.httpConnectionOptions = transportTypeOrOptions; + } else { + this.httpConnectionOptions = { + transport: transportTypeOrOptions, + }; + } + + return this; + } + + public withHubProtocol(protocol: IHubProtocol): HubConnectionBuilder { + Arg.isRequired(protocol, "protocol"); + + this.protocol = protocol; + return this; + } + + public build(): HubConnection { + // If httpConnectionOptions has a logger, use it. Otherwise, override it with the one + // provided to configureLogger + const httpConnectionOptions = this.httpConnectionOptions || {}; + + // If it's 'null', the user **explicitly** asked for null, don't mess with it. + if (httpConnectionOptions.logger === undefined) { + // If our logger is undefined or null, that's OK, the HttpConnection constructor will handle it. + httpConnectionOptions.logger = this.logger; + } + + // Now create the connection + if (!this.url) { + throw new Error("The 'HubConnectionBuilder.withUrl' method must be called before building the connection."); + } + const connection = new HttpConnection(this.url, httpConnectionOptions); + + return new HubConnection( + connection, + this.logger || NullLogger.instance, + this.protocol || new JsonHubProtocol()); + } +} + +function isLogger(logger: any): logger is ILogger { + return logger.log !== undefined; +} diff --git a/clients/ts/signalr/src/Utils.ts b/clients/ts/signalr/src/Utils.ts index b7b5491e84..f259857d19 100644 --- a/clients/ts/signalr/src/Utils.ts +++ b/clients/ts/signalr/src/Utils.ts @@ -164,11 +164,8 @@ export class ConsoleLogger implements ILogger { case LogLevel.Information: console.info(`${LogLevel[logLevel]}: ${message}`); break; - case LogLevel.Trace: - case LogLevel.Debug: - console.debug(`${LogLevel[logLevel]}: ${message}`); - break; default: + // console.debug only goes to attached debuggers in Node, so we use console.log for Trace and Debug console.log(`${LogLevel[logLevel]}: ${message}`); break; } diff --git a/clients/ts/signalr/src/index.ts b/clients/ts/signalr/src/index.ts index d10aa1b9f1..792b68f8ae 100644 --- a/clients/ts/signalr/src/index.ts +++ b/clients/ts/signalr/src/index.ts @@ -6,6 +6,7 @@ export * from "./Errors"; export * from "./HttpClient"; export * from "./HttpConnection"; export * from "./HubConnection"; +export * from "./HubConnectionBuilder"; export * from "./IConnection"; export * from "./IHubProtocol"; export * from "./ILogger"; diff --git a/clients/ts/tsconfig-base.json b/clients/ts/tsconfig-base.json index 78b45efe77..be9bde633b 100644 --- a/clients/ts/tsconfig-base.json +++ b/clients/ts/tsconfig-base.json @@ -13,6 +13,7 @@ "noImplicitAny": true, "suppressImplicitAnyIndexErrors": true, "noEmitOnError": true, + "stripInternal": true, "lib": [ "es5", "es2015.promise", "es2015.iterable", "dom" ] } } diff --git a/samples/ChatSample/Views/Home/Index.cshtml b/samples/ChatSample/Views/Home/Index.cshtml index efedad91a1..0b52a57605 100644 --- a/samples/ChatSample/Views/Home/Index.cshtml +++ b/samples/ChatSample/Views/Home/Index.cshtml @@ -17,7 +17,10 @@