Add webworker support to SignalR JS client (#7058)
* Added Platform utils to detect platform type * Added additional build for WebWorker * Changed env param from webworker to platform to make ability to specify platform to the build script * Updated the readme file with SignalR WebWorker instructions
This commit is contained in:
parent
3d4b198990
commit
095c1c1759
|
|
@ -14,6 +14,10 @@ See the [SignalR Documentation](https://docs.microsoft.com/en-us/aspnet/core/sig
|
|||
|
||||
To use the client in a browser, copy `*.js` files from the `dist/browser` folder to your script folder include on your page using the `<script>` tag.
|
||||
|
||||
### WebWorker
|
||||
|
||||
To use the client in a webworker, copy `*.js` files from the `dist/webworker` folder to your script folder include on your webworker using the `importScripts` function. Note that webworker SignalR hub connection supports only absolute path to a SignalR hub.
|
||||
|
||||
### Node.js
|
||||
|
||||
To use the client in a NodeJS application, install the package to your `node_modules` folder and use `require('@aspnet/signalr')` to load the module. The object returned by `require('@aspnet/signalr')` has the same members as the global `signalR` object (when used in a browser).
|
||||
|
|
@ -33,6 +37,25 @@ connection.start()
|
|||
.then(() => connection.invoke("send", "Hello"));
|
||||
```
|
||||
|
||||
### Example (WebWorker)
|
||||
|
||||
|
||||
```JavaScript
|
||||
importScripts('signalr.js');
|
||||
|
||||
let connection = new signalR.HubConnectionBuilder()
|
||||
.withUrl("https://example.com/signalr/chat")
|
||||
.build();
|
||||
|
||||
connection.on("send", data => {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
connection.start()
|
||||
.then(() => connection.invoke("send", "Hello"));
|
||||
|
||||
```
|
||||
|
||||
### Example (NodeJS)
|
||||
|
||||
```JavaScript
|
||||
|
|
|
|||
|
|
@ -12,12 +12,15 @@
|
|||
},
|
||||
"scripts": {
|
||||
"clean": "node ../common/node_modules/rimraf/bin.js ./dist",
|
||||
"build": "npm run clean && npm run build:lint && npm run build:esm && npm run build:cjs && npm run build:browser && npm run build:uglify",
|
||||
"build": "npm run clean && npm run build:lint && npm run build:esm && npm run build:cjs && npm run build:browser && npm run build:webworker && npm run build:uglify",
|
||||
"build:lint": "node ../common/node_modules/tslint/bin/tslint -c ../tslint.json -p ./tsconfig.json",
|
||||
"build:esm": "node ../common/node_modules/typescript/bin/tsc --project ./tsconfig.json --module es2015 --outDir ./dist/esm -d && node ./build/process-dts.js",
|
||||
"build:cjs": "node ../common/node_modules/typescript/bin/tsc --project ./tsconfig.json --module commonjs --outDir ./dist/cjs",
|
||||
"build:browser": "node ../common/node_modules/webpack-cli/bin/cli.js",
|
||||
"build:uglify": "node ../common/node_modules/uglify-js/bin/uglifyjs --source-map \"url='signalr.min.js.map',content='./dist/browser/signalr.js.map'\" --comments -o ./dist/browser/signalr.min.js ./dist/browser/signalr.js",
|
||||
"build:webworker": "node ../common/node_modules/webpack-cli/bin/cli.js --env.platform=webworker",
|
||||
"build:uglify": "npm run build:uglify:browser && npm run build:uglify:webworker",
|
||||
"build:uglify:browser": "node ../common/node_modules/uglify-js/bin/uglifyjs --source-map \"url='signalr.min.js.map',content='./dist/browser/signalr.js.map'\" --comments -o ./dist/browser/signalr.min.js ./dist/browser/signalr.js",
|
||||
"build:uglify:webworker": "node ../common/node_modules/uglify-js/bin/uglifyjs --source-map \"url='signalr.min.js.map',content='./dist/webworker/signalr.js.map'\" --comments -o ./dist/webworker/signalr.min.js ./dist/webworker/signalr.js",
|
||||
"prepack": "node ../build/embed-version.js",
|
||||
"test": "echo \"Run 'npm test' in the 'clients\\ts' folder to test this package\" && exit 1"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { ILogger, LogLevel } from "./ILogger";
|
|||
import { HttpTransportType, ITransport, TransferFormat } from "./ITransport";
|
||||
import { LongPollingTransport } from "./LongPollingTransport";
|
||||
import { ServerSentEventsTransport } from "./ServerSentEventsTransport";
|
||||
import { Arg, createLogger } from "./Utils";
|
||||
import { Arg, createLogger, Platform } from "./Utils";
|
||||
import { WebSocketTransport } from "./WebSocketTransport";
|
||||
|
||||
/** @private */
|
||||
|
|
@ -38,7 +38,7 @@ const MAX_REDIRECTS = 100;
|
|||
|
||||
let WebSocketModule: any = null;
|
||||
let EventSourceModule: any = null;
|
||||
if (typeof window === "undefined" && typeof require !== "undefined") {
|
||||
if (Platform.isNode && typeof require !== "undefined") {
|
||||
// In order to ignore the dynamic require in webpack builds we need to do this magic
|
||||
// @ts-ignore: TS doesn't know about these names
|
||||
const requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
|
||||
|
|
@ -71,18 +71,17 @@ export class HttpConnection implements IConnection {
|
|||
options = options || {};
|
||||
options.logMessageContent = options.logMessageContent || false;
|
||||
|
||||
const isNode = typeof window === "undefined";
|
||||
if (!isNode && typeof WebSocket !== "undefined" && !options.WebSocket) {
|
||||
if (!Platform.isNode && typeof WebSocket !== "undefined" && !options.WebSocket) {
|
||||
options.WebSocket = WebSocket;
|
||||
} else if (isNode && !options.WebSocket) {
|
||||
} else if (Platform.isNode && !options.WebSocket) {
|
||||
if (WebSocketModule) {
|
||||
options.WebSocket = WebSocketModule;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNode && typeof EventSource !== "undefined" && !options.EventSource) {
|
||||
if (!Platform.isNode && typeof EventSource !== "undefined" && !options.EventSource) {
|
||||
options.EventSource = EventSource;
|
||||
} else if (isNode && !options.EventSource) {
|
||||
} else if (Platform.isNode && !options.EventSource) {
|
||||
if (typeof EventSourceModule !== "undefined") {
|
||||
options.EventSource = EventSourceModule;
|
||||
}
|
||||
|
|
@ -383,7 +382,7 @@ export class HttpConnection implements IConnection {
|
|||
return url;
|
||||
}
|
||||
|
||||
if (typeof window === "undefined" || !window || !window.document) {
|
||||
if (!Platform.isBrowser || !window.document) {
|
||||
throw new Error(`Cannot resolve '${url}'.`);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { HttpClient } from "./HttpClient";
|
|||
import { ILogger, LogLevel } from "./ILogger";
|
||||
import { ITransport, TransferFormat } from "./ITransport";
|
||||
import { EventSourceConstructor } from "./Polyfills";
|
||||
import { Arg, getDataDetail, sendMessage } from "./Utils";
|
||||
import { Arg, getDataDetail, Platform, sendMessage } from "./Utils";
|
||||
|
||||
/** @private */
|
||||
export class ServerSentEventsTransport implements ITransport {
|
||||
|
|
@ -57,7 +57,7 @@ export class ServerSentEventsTransport implements ITransport {
|
|||
}
|
||||
|
||||
let eventSource: EventSource;
|
||||
if (typeof window !== "undefined") {
|
||||
if (Platform.isBrowser || Platform.isWebWorker) {
|
||||
eventSource = new this.eventSourceConstructor(url, { withCredentials: true });
|
||||
} else {
|
||||
// Non-browser passes cookies via the dictionary
|
||||
|
|
|
|||
|
|
@ -23,6 +23,22 @@ export class Arg {
|
|||
}
|
||||
}
|
||||
|
||||
/** @private */
|
||||
export class Platform {
|
||||
|
||||
public static get isBrowser(): boolean {
|
||||
return typeof window === "object";
|
||||
}
|
||||
|
||||
public static get isWebWorker(): boolean {
|
||||
return typeof self === "object" && "importScripts" in self;
|
||||
}
|
||||
|
||||
public static get isNode(): boolean {
|
||||
return !this.isBrowser && !this.isWebWorker;
|
||||
}
|
||||
}
|
||||
|
||||
/** @private */
|
||||
export function getDataDetail(data: any, includeContent: boolean): string {
|
||||
let detail = "";
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { HttpClient } from "./HttpClient";
|
|||
import { ILogger, LogLevel } from "./ILogger";
|
||||
import { ITransport, TransferFormat } from "./ITransport";
|
||||
import { WebSocketConstructor } from "./Polyfills";
|
||||
import { Arg, getDataDetail } from "./Utils";
|
||||
import { Arg, getDataDetail, Platform } from "./Utils";
|
||||
|
||||
/** @private */
|
||||
export class WebSocketTransport implements ITransport {
|
||||
|
|
@ -50,7 +50,7 @@ export class WebSocketTransport implements ITransport {
|
|||
let webSocket: WebSocket | undefined;
|
||||
const cookies = this.httpClient.getCookieString(url);
|
||||
|
||||
if (typeof window === "undefined" && cookies) {
|
||||
if (Platform.isNode && cookies) {
|
||||
// Only pass cookies when in non-browser environments
|
||||
webSocket = new this.webSocketConstructor(url, undefined, {
|
||||
headers: {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// 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.
|
||||
|
||||
const baseConfig = require("../webpack.config.base");
|
||||
module.exports = baseConfig(__dirname, "signalr", {
|
||||
module.exports = env => baseConfig(__dirname, "signalr", {
|
||||
// These are only used in Node environments
|
||||
// so we tell webpack not to pull them in for the browser
|
||||
target: env && env.platform ? env.platform : undefined,
|
||||
platformDist: env && env.platform ? env.platform : undefined,
|
||||
externals: [
|
||||
"websocket",
|
||||
"eventsource",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ module.exports = function (modulePath, browserBaseName, options) {
|
|||
process: false,
|
||||
Buffer: false,
|
||||
},
|
||||
target: options.target,
|
||||
resolveLoader: {
|
||||
// Special resolution rules for loaders (which are in the 'common' directory)
|
||||
modules: [ path.resolve(__dirname, "common", "node_modules") ],
|
||||
|
|
@ -43,7 +44,7 @@ module.exports = function (modulePath, browserBaseName, options) {
|
|||
},
|
||||
output: {
|
||||
filename: `${browserBaseName}.js`,
|
||||
path: path.resolve(modulePath, "dist", "browser"),
|
||||
path: path.resolve(modulePath, "dist", options.platformDist || "browser"),
|
||||
library: {
|
||||
root: pkg.umd_name.split("."),
|
||||
amd: pkg.umd_name,
|
||||
|
|
|
|||
Loading…
Reference in New Issue