Browse Source

Added websocket packages

dev
David Ludwig 4 years ago
parent
commit
8a4cc5540a
12 changed files with 784 additions and 0 deletions
  1. +11
    -0
      packages/websocket-client/package.json
  2. +286
    -0
      packages/websocket-client/src/WebSocketClient.ts
  3. +2
    -0
      packages/websocket-client/src/index.ts
  4. +18
    -0
      packages/websocket-client/src/schema.ts
  5. +9
    -0
      packages/websocket-client/tsconfig.json
  6. +122
    -0
      packages/websocket-client/yarn.lock
  7. +20
    -0
      packages/websocket-server/package.json
  8. +168
    -0
      packages/websocket-server/src/WebSocketServerService.ts
  9. +2
    -0
      packages/websocket-server/src/index.ts
  10. +17
    -0
      packages/websocket-server/src/schema.ts
  11. +7
    -0
      packages/websocket-server/tsconfig.json
  12. +122
    -0
      packages/websocket-server/yarn.lock

+ 11
- 0
packages/websocket-client/package.json View File

@ -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"
}
}

+ 286
- 0
packages/websocket-client/src/WebSocketClient.ts View File

@ -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[<any>requestId].reject(new Error("closed"));
delete this.#pendingRequests[<any>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<void>(resolve => this.onAuthStateChanged = resolve);
}
}
/**
* Connect to the server
*/
protected connect() {
return new Promise<void>((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<void>((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<void>((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(<IWebSocketRequest><any>parsed); // WAT!?!?
} else {
this.handleResponse(<IWebSocketResponse><any>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<T>(method: string, payload?: any) {
const requestId = this.#nextRequestId();
return new Promise<T>((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
});
});
}
}

+ 2
- 0
packages/websocket-client/src/index.ts View File

@ -0,0 +1,2 @@
export * from "./schema";
export * from "./WebSocketClient";

+ 18
- 0
packages/websocket-client/src/schema.ts View File

@ -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
}

+ 9
- 0
packages/websocket-client/tsconfig.json View File

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.package.json",
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"outDir": "./dist/lib",
"declarationDir": "./dist/typings"
}
}

+ 122
- 0
packages/websocket-client/yarn.lock View File

@ -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==

+ 20
- 0
packages/websocket-server/package.json View File

@ -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"
}
}

+ 168
- 0
packages/websocket-server/src/WebSocketServerService.ts View File

@ -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<M extends Microservice> extends InternalService<M>
{
/**
* 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>|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, <string>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>|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(<IWebSocketResponse>{
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, <IWebSocketRequest><any>parsed); // WAT!?!?
}
}
}

+ 2
- 0
packages/websocket-server/src/index.ts View File

@ -0,0 +1,2 @@
export * from "./schema";
export * from "./WebSocketServerService";

+ 17
- 0
packages/websocket-server/src/schema.ts View File

@ -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
}

+ 7
- 0
packages/websocket-server/tsconfig.json View File

@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.package.json",
"compilerOptions": {
"outDir": "./dist/lib",
"declarationDir": "./dist/typings"
}
}

+ 122
- 0
packages/websocket-server/yarn.lock View File

@ -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==

Loading…
Cancel
Save