@ -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" | |||
} | |||
} |
@ -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 | |||
}); | |||
}); | |||
} | |||
} |
@ -0,0 +1,2 @@ | |||
export * from "./schema"; | |||
export * from "./WebSocketClient"; |
@ -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 | |||
} |
@ -0,0 +1,9 @@ | |||
{ | |||
"extends": "../../tsconfig.package.json", | |||
"compilerOptions": { | |||
"target": "esnext", | |||
"module": "esnext", | |||
"outDir": "./dist/lib", | |||
"declarationDir": "./dist/typings" | |||
} | |||
} |
@ -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== |
@ -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" | |||
} | |||
} |
@ -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!?!? | |||
} | |||
} | |||
} |
@ -0,0 +1,2 @@ | |||
export * from "./schema"; | |||
export * from "./WebSocketServerService"; |
@ -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 | |||
} |
@ -0,0 +1,7 @@ | |||
{ | |||
"extends": "../../tsconfig.package.json", | |||
"compilerOptions": { | |||
"outDir": "./dist/lib", | |||
"declarationDir": "./dist/typings" | |||
} | |||
} |
@ -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== |