From 8a4cc5540a68719fadfce4e739b3891fc6024220 Mon Sep 17 00:00:00 2001 From: David Ludwig Date: Sun, 20 Jun 2021 21:07:41 -0500 Subject: [PATCH] Added websocket packages --- packages/websocket-client/package.json | 11 + .../websocket-client/src/WebSocketClient.ts | 286 ++++++++++++++++++ packages/websocket-client/src/index.ts | 2 + packages/websocket-client/src/schema.ts | 18 ++ packages/websocket-client/tsconfig.json | 9 + packages/websocket-client/yarn.lock | 122 ++++++++ packages/websocket-server/package.json | 20 ++ .../src/WebSocketServerService.ts | 168 ++++++++++ packages/websocket-server/src/index.ts | 2 + packages/websocket-server/src/schema.ts | 17 ++ packages/websocket-server/tsconfig.json | 7 + packages/websocket-server/yarn.lock | 122 ++++++++ 12 files changed, 784 insertions(+) create mode 100644 packages/websocket-client/package.json create mode 100644 packages/websocket-client/src/WebSocketClient.ts create mode 100644 packages/websocket-client/src/index.ts create mode 100644 packages/websocket-client/src/schema.ts create mode 100644 packages/websocket-client/tsconfig.json create mode 100644 packages/websocket-client/yarn.lock create mode 100644 packages/websocket-server/package.json create mode 100644 packages/websocket-server/src/WebSocketServerService.ts create mode 100644 packages/websocket-server/src/index.ts create mode 100644 packages/websocket-server/src/schema.ts create mode 100644 packages/websocket-server/tsconfig.json create mode 100644 packages/websocket-server/yarn.lock diff --git a/packages/websocket-client/package.json b/packages/websocket-client/package.json new file mode 100644 index 0000000..85db17e --- /dev/null +++ b/packages/websocket-client/package.json @@ -0,0 +1,11 @@ +{ + "name": "@autoplex/websocket-client", + "version": "0.0.0", + "main": "dist/lib/index.js", + "types": "dist/typings/index.d.ts", + "license": "MIT", + "scripts": { + "build": "yarn run clean && tsc", + "clean": "rimraf ./dist" + } +} diff --git a/packages/websocket-client/src/WebSocketClient.ts b/packages/websocket-client/src/WebSocketClient.ts new file mode 100644 index 0000000..73a303e --- /dev/null +++ b/packages/websocket-client/src/WebSocketClient.ts @@ -0,0 +1,286 @@ +import { IPendingRequest, IWebSocketRequest, IWebSocketResponse } from "./schema"; + +export abstract class WebSocketClient +{ + /** + * The URL to connect to + */ + protected readonly WEBSOCKET_URL: string; + + /** + * Indicate if the websocket is ready + */ + protected isReady: boolean = false; + + /** + * Indicate if the websocket client is currently running + */ + protected running: boolean = false; + + /** + * The websocket instance + */ + protected socket: WebSocket|null = null; + + /** + * Store the timestamp of the most recent connection attempt + */ + #lastConnectAttempt: number = 0; + + /** + * Maintain a mapping of pending requests + */ + #pendingRequests: { [requestId: number]: IPendingRequest } = {}; + + /** + * Store the next request ID + */ + #requestId: number = 0; + + /** + * The auth state change event handler + */ + protected onAuthStateChanged: (() => void)|null = null; + + public constructor(url: string) { + this.WEBSOCKET_URL = url; + this.setup(); + this.run(); + } + + // Overridable --------------------------------------------------------------------------------- + + /** + * Setup the socket + */ + protected abstract setup(): void; + + /** + * Get the app's current auth JWT + */ + protected abstract jwt(): string|null; + + /** + * Forget the JWT and log out of the app + */ + protected abstract forgetJwt(): void; + + // Websocket Service Interface ----------------------------------------------------------------- + + /** + * Connect to the server + */ + public async run() { + if (this.running) { + return; + } + this.running = true; + while (this.running) { + this.close(); + await this.waitForAuthReady(); + try { + await this.connect(); + await this.authenticate(); + await this.exec(); + } catch(e) { + continue; + } + } + this.close(); + } + + /** + * Close the connection if it exists + */ + public async close() { + this.isReady = false; + for (let requestId of Object.keys(this.#pendingRequests)) { + this.#pendingRequests[requestId].reject(new Error("closed")); + delete this.#pendingRequests[requestId]; + } + if (this.socket === null) { + return; + } + this.socket.onclose = null; + this.socket.onerror = null; + this.socket.onmessage = null; + this.socket.close(); + } + + // Connection Handling ------------------------------------------------------------------------- + + /** + * If authentication info isn't in the store, wait for it + */ + protected async waitForAuthReady() { + while (!this.jwt()) { + console.log("Waiting for auth state change..."); + await new Promise(resolve => this.onAuthStateChanged = resolve); + } + } + + /** + * Connect to the server + */ + protected connect() { + return new Promise((resolve, reject) => { + let timeout = setTimeout(() => { + console.log("Connecting..."); + this.#lastConnectAttempt = Date.now(); + this.socket = new WebSocket(this.WEBSOCKET_URL); + this.socket.onopen = () => resolve(); + this.socket.onerror = reject; + this.socket.onclose = reject; + }, this.#lastConnectAttempt - Date.now() + 5000); + this.onAuthStateChanged = () => { + clearTimeout(timeout); + reject(); + }; + }); + } + + /** + * Authenticate the connection + */ + protected authenticate() { + return new Promise((resolve, reject) => { + console.log("Authenticating..."); + + let timeout = setTimeout(reject, 5000); + this.onAuthStateChanged = this.socket!.onerror = this.socket!.onclose = () => { + clearTimeout(timeout); + reject(); + } + this.socket!.onmessage = (event) => { + clearTimeout(timeout); + if (event.data !== "true") { + this.forgetJwt(); + reject(); + return; + } + resolve(); + }; + this.socket!.send(this.jwt() ?? ""); + }); + } + + /** + * Connection established. Wait for close + */ + protected exec() { + return new Promise((resolve, reject) => { + console.log("Connected"); + this.isReady = true; + this.onAuthStateChanged = () => reject(); + this.socket!.onclose = () => resolve(); + this.socket!.onerror = () => reject(); + this.socket!.onmessage = event => this.onMessage(event.data); + }); + } + + /** + * Get the next request Id + */ + #nextRequestId() { + this.#requestId = (this.#requestId + 1) % Number.MAX_SAFE_INTEGER; + return this.#requestId; + } + + /** + * Send a payload to the websocket server + */ + #send(payload: any) { + this.socket!.send(JSON.stringify(payload)); + } + + // Event Handling ------------------------------------------------------------------------------ + + /** + * Invoked when a message is received by the socket + */ + protected onMessage(data: string) { + let parsed: IWebSocketRequest | IWebSocketResponse; + try { + parsed = JSON.parse(data); + } catch(e) { + console.warn("Failed to parse JSON response"); + return; + } + if (parsed.type === "request") { + this.handleRequest(parsed); // WAT!?!? + } else { + this.handleResponse(parsed); // WHY?!?! + } + } + + /** + * Handle an incoming request from the server + */ + protected handleRequest(request: IWebSocketRequest) { + + } + + /** + * Handle an incoming response from the server + */ + protected handleResponse(response: IWebSocketResponse) { + if (this.#pendingRequests[response.requestId] === undefined) { + return; + } + let pendingRequest = this.#pendingRequests[response.requestId]; + pendingRequest.resolve(response.payload); + } + + // Public Interface ---------------------------------------------------------------------------- + + /** + * Notify the socket that the authentication state of the app has changed + */ + public authStateChanged() { + if (this.onAuthStateChanged === null) { + return; + } + this.onAuthStateChanged(); + } + + /** + * Send a payload to the server + */ + public send(method: string, payload?: any) { + this.#send({ + requestId: this.#nextRequestId(), + method, + payload + }); + } + + /** + * Request info from the server + */ + public request(method: string, payload?: any) { + const requestId = this.#nextRequestId(); + return new Promise((resolve, reject) => { + let timeout = setTimeout(() => { + reject(new Error("timeout")); + delete this.#pendingRequests[requestId]; + }, 5000); + this.#pendingRequests[requestId] = { + resolve: (data: T) => { + clearTimeout(timeout); + delete this.#pendingRequests[requestId]; + resolve(data); + }, + reject: (error: any) => { + clearTimeout(timeout); + delete this.#pendingRequests[requestId]; + reject(error); + } + }; + this.#send({ + requestId, + method, + payload + }); + }); + } +} diff --git a/packages/websocket-client/src/index.ts b/packages/websocket-client/src/index.ts new file mode 100644 index 0000000..bd40aed --- /dev/null +++ b/packages/websocket-client/src/index.ts @@ -0,0 +1,2 @@ +export * from "./schema"; +export * from "./WebSocketClient"; diff --git a/packages/websocket-client/src/schema.ts b/packages/websocket-client/src/schema.ts new file mode 100644 index 0000000..38ac9e5 --- /dev/null +++ b/packages/websocket-client/src/schema.ts @@ -0,0 +1,18 @@ +export interface IWebSocketRequest { + type: "request", + requestId: number, + method: string, + payload: any +} + +export interface IWebSocketResponse { + type : "response", + requestId: number, + payload : any +} + + +export interface IPendingRequest { + resolve(payload?: any): void, + reject(error: Error|string): void +} diff --git a/packages/websocket-client/tsconfig.json b/packages/websocket-client/tsconfig.json new file mode 100644 index 0000000..a0dcfe6 --- /dev/null +++ b/packages/websocket-client/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.package.json", + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "outDir": "./dist/lib", + "declarationDir": "./dist/typings" + } +} diff --git a/packages/websocket-client/yarn.lock b/packages/websocket-client/yarn.lock new file mode 100644 index 0000000..bd5de83 --- /dev/null +++ b/packages/websocket-client/yarn.lock @@ -0,0 +1,122 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/jsonwebtoken@^8.5.1": + version "8.5.1" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#56958cb2d80f6d74352bd2e501a018e2506a8a84" + integrity sha512-rNAPdomlIUX0i0cg2+I+Q1wOUr531zHBQ+cV/28PJ39bSPKjahatZZ2LMuhiguETkCgLVzfruw/ZvNMNkKoSzw== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "15.12.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.2.tgz#1f2b42c4be7156ff4a6f914b2fb03d05fa84e38d" + integrity sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww== + +"@types/ws@^7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.5.tgz#8ff0f7efcd8fea19f51f9dd66cb8b498d172a752" + integrity sha512-8mbDgtc8xpxDDem5Gwj76stBDJX35KQ3YBoayxlqUQcL5BZUthiqP/VQ4PQnLHqM4PmlbyO74t98eJpURO+gPA== + dependencies: + "@types/node" "*" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +ws@^7.5.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.0.tgz#0033bafea031fb9df041b2026fc72a571ca44691" + integrity sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw== diff --git a/packages/websocket-server/package.json b/packages/websocket-server/package.json new file mode 100644 index 0000000..128bbde --- /dev/null +++ b/packages/websocket-server/package.json @@ -0,0 +1,20 @@ +{ + "name": "@autoplex/websocket-server", + "version": "0.0.0", + "main": "dist/lib/index.js", + "types": "dist/typings/index.d.ts", + "license": "MIT", + "scripts": { + "build": "yarn run clean && tsc", + "clean": "rimraf ./dist" + }, + "devDependencies": { + "@autoplex/microservice": "^0.0.0", + "@types/jsonwebtoken": "^8.5.1", + "@types/ws": "^7.4.5" + }, + "dependencies": { + "jsonwebtoken": "^8.5.1", + "ws": "^7.5.0" + } +} diff --git a/packages/websocket-server/src/WebSocketServerService.ts b/packages/websocket-server/src/WebSocketServerService.ts new file mode 100644 index 0000000..f7598cc --- /dev/null +++ b/packages/websocket-server/src/WebSocketServerService.ts @@ -0,0 +1,168 @@ +import { InternalService, Microservice } from "@autoplex/microservice"; +import { IncomingMessage } from "http"; +import WebSocket, { Server } from "ws"; +import jsonwebtoken from "jsonwebtoken"; +import { IWebSocketRequest, IWebSocketResponse } from "./schema"; + +export abstract class WebSocketServerService extends InternalService +{ + /** + * The list of active client connections + */ + protected connections: WebSocket[] = []; + + /** + * The websocket server instance + */ + protected server!: Server; + + /** + * The list of registered methods + */ + #methods: { [method: string]: (payload?: any) => Promise|any } = {}; + + // Overridable --------------------------------------------------------------------------------- + + /** + * The application key for the application + */ + protected abstract get appKey(): string; + + /** + * Install methods into the websocket service + */ + protected installMethods() { + // no-op + } + + // Service Methods ----------------------------------------------------------------------------- + + /** + * Boot the service + */ + public override async boot() { + this.installMethods(); + this.server = new Server({ port: 3250 }); + this.server.on("connection", this.acceptConnection.bind(this)); + } + + /** + * Shutdown the service + */ + public override async shutdown() { + this.server.close(); + for (let socket of this.connections) { + socket.close(); + } + } + + /** + * Accept the pending websocket connection + */ + protected acceptConnection(socket: WebSocket, request: IncomingMessage) { + let timeout = setTimeout(() => socket.close.bind(socket), 5000); + socket.once("message", async (data) => { + clearTimeout(timeout); + let cookies = this.parseCookies(request); + let token = data + '.' + (cookies["jwt_signature"] ?? ""); + try { + await this.authenticate(token); + socket.send("true"); + socket.on("close", () => this.onClose(socket)); + socket.on("error", (error) => this.onError(socket, error)); + socket.on("message", (data) => this.onMessage(socket, data)); + this.connections.push(socket); + } catch(e) { + socket.send("false"); + socket.close(); + } + }); + } + + /** + * Verify the provided JWT + */ + protected async authenticate(token: string) { + await new Promise((resolve, reject) => { + jsonwebtoken.verify(token, this.appKey, (err, decoded) => { + if (err) { + reject(err); + return; + } + resolve(decoded); + }); + }); + } + + /** + * Parse the received cookies from the request + * https://stackoverflow.com/a/3409200/16243951 + */ + protected parseCookies(request: IncomingMessage) { + let cookies: { [cookie: string]: string } = {}; + for (let cookie of request.headers.cookie?.split(';') ?? []) { + let parts = cookie.split('='); + cookies[parts.shift()!.trim()] = decodeURI(parts.join('=')); + } + return cookies; + } + + /** + * Install a method into the server service + */ + protected installMethod(name: string, method: (payload?: any) => Promise|any) { + if (this.#methods[name] !== undefined) { + throw new Error("Attempted to install method with duplicate name in websocket service"); + } + this.#methods[name] = method; + } + + // Event Handling ------------------------------------------------------------------------------ + + /** + * Handle an incoming request + */ + protected async handleRequest(socket: WebSocket, request: IWebSocketRequest) { + if (this.#methods[request.method] === undefined) { + console.warn(`Requested unknown method: '${request.method}' with payload:`, request.payload); + return; + } + let result = await this.#methods[request.method](request.payload); + socket.send(JSON.stringify({ + type: "response", + requestId: request.requestId, + payload: JSON.stringify(result) + })); + } + + /** + * Invoked when a connection closes + */ + protected onClose(socket: WebSocket) { + let index = this.connections.indexOf(socket); + this.connections.splice(index, 1); + } + + /** + * Invoked when a connection encounters an error + */ + protected onError(socket: WebSocket, error: Error) { + this.onClose(socket); + } + + /** + * Invoked when a message is received from a connection + */ + protected onMessage(socket: WebSocket, data: string) { + let parsed: IWebSocketRequest | IWebSocketResponse; + try { + parsed = JSON.parse(data); + } catch(e) { + console.warn("Failed to parse JSON response"); + return; + } + if (parsed.type !== "request") { + this.handleRequest(socket, parsed); // WAT!?!? + } + } +} diff --git a/packages/websocket-server/src/index.ts b/packages/websocket-server/src/index.ts new file mode 100644 index 0000000..979bb24 --- /dev/null +++ b/packages/websocket-server/src/index.ts @@ -0,0 +1,2 @@ +export * from "./schema"; +export * from "./WebSocketServerService"; diff --git a/packages/websocket-server/src/schema.ts b/packages/websocket-server/src/schema.ts new file mode 100644 index 0000000..df68eca --- /dev/null +++ b/packages/websocket-server/src/schema.ts @@ -0,0 +1,17 @@ +export interface IWebSocketRequest { + type: "request", + requestId: number, + method: string, + payload: any +} + +export interface IWebSocketResponse { + type : "response", + requestId: number, + payload : any +} + +export interface IPendingRequest { + resolve(payload?: any): void, + reject(error: Error|string): void +} diff --git a/packages/websocket-server/tsconfig.json b/packages/websocket-server/tsconfig.json new file mode 100644 index 0000000..595f8fd --- /dev/null +++ b/packages/websocket-server/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.package.json", + "compilerOptions": { + "outDir": "./dist/lib", + "declarationDir": "./dist/typings" + } +} diff --git a/packages/websocket-server/yarn.lock b/packages/websocket-server/yarn.lock new file mode 100644 index 0000000..bd5de83 --- /dev/null +++ b/packages/websocket-server/yarn.lock @@ -0,0 +1,122 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/jsonwebtoken@^8.5.1": + version "8.5.1" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#56958cb2d80f6d74352bd2e501a018e2506a8a84" + integrity sha512-rNAPdomlIUX0i0cg2+I+Q1wOUr531zHBQ+cV/28PJ39bSPKjahatZZ2LMuhiguETkCgLVzfruw/ZvNMNkKoSzw== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "15.12.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.2.tgz#1f2b42c4be7156ff4a6f914b2fb03d05fa84e38d" + integrity sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww== + +"@types/ws@^7.4.5": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.5.tgz#8ff0f7efcd8fea19f51f9dd66cb8b498d172a752" + integrity sha512-8mbDgtc8xpxDDem5Gwj76stBDJX35KQ3YBoayxlqUQcL5BZUthiqP/VQ4PQnLHqM4PmlbyO74t98eJpURO+gPA== + dependencies: + "@types/node" "*" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +ws@^7.5.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.0.tgz#0033bafea031fb9df041b2026fc72a571ca44691" + integrity sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==