From 7a46e9d8a33e30a78c364bd0e4965dbf7764ff0c Mon Sep 17 00:00:00 2001 From: Alex Mikhalev Date: Sat, 16 Jun 2018 23:54:03 -0600 Subject: [PATCH] Improved connection status display --- .gitignore | 1 + app/components/DeviceView.tsx | 33 ++-- app/{state => sprinklers}/websocket.ts | 44 ++++-- app/state/web.ts | 4 +- common/sprinklers/ConnectionState.ts | 34 +++++ common/sprinklers/ISprinklersApi.ts | 4 + common/sprinklers/SprinklersDevice.ts | 10 +- common/sprinklers/index.ts | 1 + common/sprinklers/mqtt/index.ts | 43 ++++-- common/sprinklers/schema/index.ts | 11 +- common/sprinklers/websocketData.ts | 7 +- package.json | 8 +- server/logging/prettyPrint.ts | 5 +- server/state.ts | 6 +- server/websocket/index.ts | 26 +++- yarn.lock | 203 ++++++++----------------- 16 files changed, 242 insertions(+), 198 deletions(-) rename app/{state => sprinklers}/websocket.ts (71%) create mode 100644 common/sprinklers/ConnectionState.ts diff --git a/.gitignore b/.gitignore index 226d09c..cc0c948 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ npm-debug* /public .idea .vscode +.env* \ No newline at end of file diff --git a/app/components/DeviceView.tsx b/app/components/DeviceView.tsx index 138dd7c..9d0f1d2 100644 --- a/app/components/DeviceView.tsx +++ b/app/components/DeviceView.tsx @@ -4,20 +4,31 @@ import * as React from "react"; import { Grid, Header, Icon, Item } from "semantic-ui-react"; import { injectState, StateBase } from "@app/state"; -import { SprinklersDevice } from "@common/sprinklers"; +import { ConnectionState as ConState, SprinklersDevice } from "@common/sprinklers"; import { ProgramTable, RunSectionForm, SectionRunnerView, SectionTable } from "."; import "./DeviceView.scss"; -function ConnectionState({ connected, className }: { connected: boolean, className?: string }) { +function ConnectionState({ connectionState, className }: { connectionState: ConState, className?: string }) { + const connected = connectionState.isConnected; const classes = classNames({ connectionState: true, connected: connected, /* tslint:disable-line:object-literal-shorthand */ disconnected: !connected, }, className); + let connectionText: string; + if (connected) { + connectionText = "Connected"; + } else if (connectionState.serverToBroker) { + connectionText = "Device Disconnected"; + } else if (connectionState.clientToServer) { + connectionText = "Broker Disconnected"; + } else { + connectionText = "Server Disconnected"; + } return (
-   - {connected ? "Connected" : "Disconnected"} +   + {connectionText}
); } @@ -39,28 +50,28 @@ class DeviceView extends React.Component { } render() { - const { id, connected, sections, programs, sectionRunner } = this.device; + const { id, connectionState, sections, programs, sectionRunner } = this.device; return ( - +
Device {id}
- +
Raspberry Pi Grinklers Device - + - + - - + +
); diff --git a/app/state/websocket.ts b/app/sprinklers/websocket.ts similarity index 71% rename from app/state/websocket.ts rename to app/sprinklers/websocket.ts index eb60718..9ee48cb 100644 --- a/app/state/websocket.ts +++ b/app/sprinklers/websocket.ts @@ -1,20 +1,28 @@ import { update } from "serializr"; import logger from "@common/logger"; -import * as s from "@common/sprinklers"; +import * as s from "@common/sprinklers/index"; import * as requests from "@common/sprinklers/requests"; -import * as schema from "@common/sprinklers/schema"; +import * as schema from "@common/sprinklers/schema/index"; import { seralizeRequest } from "@common/sprinklers/schema/requests"; import * as ws from "@common/sprinklers/websocketData"; +import { autorun, observable } from "mobx"; const log = logger.child({ source: "websocket" }); -export class WebSprinklersDevice extends s.SprinklersDevice { - readonly api: WebApiClient; +export class WSSprinklersDevice extends s.SprinklersDevice { + readonly api: WebSocketApiClient; - constructor(api: WebApiClient) { + constructor(api: WebSocketApiClient) { super(); this.api = api; + autorun(() => { + this.connectionState.serverToBroker = api.connectionState.serverToBroker; + this.connectionState.clientToServer = api.connectionState.clientToServer; + if (!api.connectionState.isConnected) { + this.connectionState.brokerToDevice = null; + } + }); } get id() { @@ -26,17 +34,25 @@ export class WebSprinklersDevice extends s.SprinklersDevice { } } -export class WebApiClient implements s.ISprinklersApi { +export class WebSocketApiClient implements s.ISprinklersApi { readonly webSocketUrl: string; socket!: WebSocket; - device: WebSprinklersDevice; + device: WSSprinklersDevice; nextDeviceRequestId = Math.round(Math.random() * 1000000); deviceResponseCallbacks: { [id: number]: (res: ws.IDeviceCallResponse) => void | undefined; } = {}; + @observable connectionState: s.ConnectionState = new s.ConnectionState(); + + get connected(): boolean { + return this.connectionState.isConnected; + } + constructor(webSocketUrl: string) { this.webSocketUrl = webSocketUrl; - this.device = new WebSprinklersDevice(this); + this.device = new WSSprinklersDevice(this); + this.connectionState.clientToServer = false; + this.connectionState.serverToBroker = false; } start() { @@ -83,25 +99,28 @@ export class WebApiClient implements s.ISprinklersApi { private onOpen() { log.info("established websocket connection"); + this.connectionState.clientToServer = true; } private onClose(event: CloseEvent) { log.info({ reason: event.reason, wasClean: event.wasClean }, "disconnected from websocket"); + this.connectionState.clientToServer = false; } private onError(event: Event) { log.error(event, "websocket error"); + this.connectionState.clientToServer = false; } private onMessage(event: MessageEvent) { - log.trace({ event }, "websocket message"); let data: ws.IServerMessage; try { data = JSON.parse(event.data); } catch (err) { return log.error({ event, err }, "received invalid websocket message"); } + log.trace({ data }, "websocket message"); switch (data.type) { case "deviceUpdate": this.onDeviceUpdate(data); @@ -109,6 +128,9 @@ export class WebApiClient implements s.ISprinklersApi { case "deviceCallResponse": this.onDeviceCallResponse(data); break; + case "brokerConnectionUpdate": + this.onBrokerConnectionUpdate(data); + break; default: log.warn({ data }, "unsupported event type received"); } @@ -127,4 +149,8 @@ export class WebApiClient implements s.ISprinklersApi { cb(data); } } + + private onBrokerConnectionUpdate(data: ws.IBrokerConnectionUpdate) { + this.connectionState.serverToBroker = data.brokerConnected; + } } diff --git a/app/state/web.ts b/app/state/web.ts index 7d8d234..df30e6e 100644 --- a/app/state/web.ts +++ b/app/state/web.ts @@ -1,6 +1,6 @@ import { MqttApiClient } from "@common/sprinklers/mqtt"; +import { WebSocketApiClient } from "../sprinklers/websocket"; import StateBase from "./StateBase"; -import { WebApiClient } from "./websocket"; const isDev = process.env.NODE_ENV === "development"; const websocketPort = isDev ? 8080 : location.port; @@ -10,5 +10,5 @@ export class MqttApiState extends StateBase { } export class WebApiState extends StateBase { - sprinklersApi = new WebApiClient(`ws://${location.hostname}:${websocketPort}`); + sprinklersApi = new WebSocketApiClient(`ws://${location.hostname}:${websocketPort}`); } diff --git a/common/sprinklers/ConnectionState.ts b/common/sprinklers/ConnectionState.ts new file mode 100644 index 0000000..38bf6f4 --- /dev/null +++ b/common/sprinklers/ConnectionState.ts @@ -0,0 +1,34 @@ +import { computed, observable } from "mobx"; + +export class ConnectionState { + /** + * Represents if a client is connected to the sprinklers3 server (eg. via websocket) + * Can be null if there is no client involved + */ + @observable clientToServer: boolean | null = null; + + /** + * Represents if the sprinklers3 server is connected to the broker (eg. via mqtt) + * Can be null if there is no broker involved + */ + @observable serverToBroker: boolean | null = null; + + /** + * Represents if the device is connected to the broker and we can communicate with it (eg. via mqtt) + * Can be null if there is no device involved + */ + @observable brokerToDevice: boolean | null = null; + + @computed get isConnected(): boolean { + if (this.brokerToDevice != null) { + return this.brokerToDevice; + } + if (this.serverToBroker != null) { + return this.serverToBroker; + } + if (this.clientToServer != null) { + return this.clientToServer; + } + return false; + } +} diff --git a/common/sprinklers/ISprinklersApi.ts b/common/sprinklers/ISprinklersApi.ts index 07a95e2..79f44d6 100644 --- a/common/sprinklers/ISprinklersApi.ts +++ b/common/sprinklers/ISprinklersApi.ts @@ -1,6 +1,10 @@ +import { ConnectionState } from "./ConnectionState"; import { SprinklersDevice } from "./SprinklersDevice"; export interface ISprinklersApi { + readonly connectionState: ConnectionState; + readonly connected: boolean; + start(): void; getDevice(id: string): SprinklersDevice; diff --git a/common/sprinklers/SprinklersDevice.ts b/common/sprinklers/SprinklersDevice.ts index 47cf139..cfeb150 100644 --- a/common/sprinklers/SprinklersDevice.ts +++ b/common/sprinklers/SprinklersDevice.ts @@ -1,15 +1,20 @@ -import { observable } from "mobx"; +import { computed, observable } from "mobx"; +import { ConnectionState } from "./ConnectionState"; import { Program } from "./Program"; import * as requests from "./requests"; import { Section } from "./Section"; import { SectionRunner } from "./SectionRunner"; export abstract class SprinklersDevice { - @observable connected: boolean = false; + @observable connectionState: ConnectionState = new ConnectionState(); @observable sections: Section[] = []; @observable programs: Program[] = []; @observable sectionRunner: SectionRunner; + @computed get connected(): boolean { + return this.connectionState.isConnected; + } + sectionConstructor: typeof Section = Section; sectionRunnerConstructor: typeof SectionRunner = SectionRunner; programConstructor: typeof Program = Program; @@ -19,6 +24,7 @@ export abstract class SprinklersDevice { } abstract get id(): string; + abstract makeRequest(request: requests.Request): Promise; runProgram(opts: requests.WithProgram) { diff --git a/common/sprinklers/index.ts b/common/sprinklers/index.ts index b6d7175..1834ce8 100644 --- a/common/sprinklers/index.ts +++ b/common/sprinklers/index.ts @@ -5,3 +5,4 @@ export * from "./schedule"; export * from "./Section"; export * from "./SectionRunner"; export * from "./SprinklersDevice"; +export * from "./ConnectionState"; diff --git a/common/sprinklers/mqtt/index.ts b/common/sprinklers/mqtt/index.ts index 22eaae6..3054ab0 100644 --- a/common/sprinklers/mqtt/index.ts +++ b/common/sprinklers/mqtt/index.ts @@ -6,6 +6,7 @@ import * as s from "@common/sprinklers"; import * as requests from "@common/sprinklers/requests"; import * as schema from "@common/sprinklers/schema"; import { seralizeRequest } from "@common/sprinklers/schema/requests"; +import { autorun, observable } from "mobx"; const log = logger.child({ source: "mqtt" }); @@ -16,11 +17,16 @@ interface WithRid { export class MqttApiClient implements s.ISprinklersApi { readonly mqttUri: string; client!: mqtt.Client; - connected: boolean = false; + @observable connectionState: s.ConnectionState = new s.ConnectionState(); devices: Map = new Map(); + get connected(): boolean { + return this.connectionState.isConnected; + } + constructor(mqttUri: string) { this.mqttUri = mqttUri; + this.connectionState.serverToBroker = false; } private static newClientId() { @@ -29,24 +35,21 @@ export class MqttApiClient implements s.ISprinklersApi { start() { const clientId = MqttApiClient.newClientId(); - log.info({ clientId }, "connecting to mqtt with client id"); + log.info({ mqttUri: this.mqttUri, clientId }, "connecting to mqtt broker with client id"); this.client = mqtt.connect(this.mqttUri, { - clientId, + clientId, connectTimeout: 5000, reconnectPeriod: 5000, }); this.client.on("message", this.onMessageArrived.bind(this)); - this.client.on("offline", () => { - this.connected = false; + this.client.on("close", () => { + logger.warn("mqtt disconnected"); + this.connectionState.serverToBroker = false; }); this.client.on("error", (err) => { log.error({ err }, "mqtt error"); }); this.client.on("connect", () => { log.info("mqtt connected"); - this.connected = true; - const values = this.devices.values(); - for (const device of values) { - device.doSubscribe(); - } + this.connectionState.serverToBroker = true; }); } @@ -116,9 +119,10 @@ interface IHandlerEntry { const handler = (test: RegExp) => (target: MqttSprinklersDevice, propertyKey: string, descriptor: TypedPropertyDescriptor) => { if (typeof descriptor.value === "function") { - (target.handlers || (target.handlers = [])).push({ + const entry = { test, handler: descriptor.value, - }); + }; + (target.handlers || (target.handlers = [])).push(entry); } }; @@ -126,7 +130,7 @@ class MqttSprinklersDevice extends s.SprinklersDevice { readonly apiClient: MqttApiClient; readonly prefix: string; - handlers: IHandlerEntry[] = []; + handlers!: IHandlerEntry[]; private nextRequestId: number = Math.floor(Math.random() * 1000000000); private responseCallbacks: Map = new Map(); @@ -138,6 +142,16 @@ class MqttSprinklersDevice extends s.SprinklersDevice { this.apiClient = apiClient; this.prefix = prefix; this.sectionRunner = new MqttSectionRunner(this); + + autorun(() => { + const brokerConnected = apiClient.connected; + this.connectionState.serverToBroker = brokerConnected; + if (brokerConnected) { + this.doSubscribe(); + } else { + this.connectionState.brokerToDevice = false; + } + }); } get id(): string { @@ -192,7 +206,7 @@ class MqttSprinklersDevice extends s.SprinklersDevice { /* tslint:disable:no-unused-variable */ @handler(/^connected$/) private handleConnected(payload: string) { - this.connected = (payload === "true"); + this.connectionState.brokerToDevice = (payload === "true"); log.trace(`MqttSprinklersDevice with prefix ${this.prefix}: ${this.connected}`); return; } @@ -242,6 +256,7 @@ class MqttSprinklersDevice extends s.SprinklersDevice { cb(data); } } + /* tslint:enable:no-unused-variable */ } diff --git a/common/sprinklers/schema/index.ts b/common/sprinklers/schema/index.ts index c228126..6d8b6d3 100644 --- a/common/sprinklers/schema/index.ts +++ b/common/sprinklers/schema/index.ts @@ -10,6 +10,15 @@ export { requests }; import * as common from "./common"; export * from "./common"; +export const connectionState: ModelSchema = { + factory: (c) => new s.ConnectionState(), + props: { + clientToServer: primitive(), + serverToBroker: primitive(), + brokerToDevice: primitive(), + }, +}; + export const section: ModelSchema = { factory: (c) => new (c.parentContext.target as s.SprinklersDevice).sectionConstructor( c.parentContext.target, c.json.id), @@ -73,7 +82,7 @@ export const program: ModelSchema = { }; export const sprinklersDevice = createSimpleSchema({ - connected: primitive(), + connectionState: object(connectionState), sections: list(object(section)), sectionRunner: object(sectionRunner), programs: list(object(program)), diff --git a/common/sprinklers/websocketData.ts b/common/sprinklers/websocketData.ts index 797f763..43f39f0 100644 --- a/common/sprinklers/websocketData.ts +++ b/common/sprinklers/websocketData.ts @@ -12,7 +12,12 @@ export interface IDeviceCallResponse { data: ResponseData; } -export type IServerMessage = IDeviceUpdate | IDeviceCallResponse; +export interface IBrokerConnectionUpdate { + type: "brokerConnectionUpdate"; + brokerConnected: boolean; +} + +export type IServerMessage = IDeviceUpdate | IDeviceCallResponse | IBrokerConnectionUpdate; export interface IDeviceCallRequest { type: "deviceCallRequest"; diff --git a/package.json b/package.json index 123983d..01ffd2d 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "start:dev-server": "NODE_ENV=development webpack-dev-server --config ./app/webpack.config.js --env dev", "start": "NODE_ENV=development node dist/server/index.js", "start:pretty": "yarn start | node dist/server/logging/prettyPrint.js", - "start:nodemon": "NODE_ENV=development nodemon --exec \"node dist/server/index.js | node dist/server/logging/prettyPrint.js\"", + "start:nodemon": "nodemon --delay 0.5 --exec \"env NODE_ENV=development node dist/server/index.js | node dist/server/logging/prettyPrint.js\"", "start:watch": "run-p watch:server start:nodemon", "start:dev": "run-p start:dev-server start:watch", "lint:app": "tslint --project app --force --format verbose", @@ -45,7 +45,8 @@ "pino": "^4.16.1", "postcss-cssnext": "^3.1.0", "serializr": "^1.1.14", - "uglify-es": "3.3.9" + "uglify-es": "3.3.9", + "ws": "^5.1.1" }, "devDependencies": { "@types/async": "^2.0.49", @@ -101,8 +102,7 @@ "url-loader": "^1.0.1", "webpack": "^4.5.0", "webpack-cli": "^2.0.14", - "webpack-dev-server": "^3.1.3", - "ws": "^5.1.1" + "webpack-dev-server": "^3.1.3" }, "resolutions": { "**/@types/react": "16.3.9", diff --git a/server/logging/prettyPrint.ts b/server/logging/prettyPrint.ts index 0373260..a28b6fd 100644 --- a/server/logging/prettyPrint.ts +++ b/server/logging/prettyPrint.ts @@ -46,9 +46,8 @@ function formatter(value: any) { if (value.msg) { line += chalk.cyan(value.msg); } - line += "\n"; if (value.err) { - line += " " + withSpaces(value.err.stack) + "\n"; + line += "\n " + withSpaces(value.err.stack) + "\n"; } else { line += filter(value); } @@ -78,7 +77,7 @@ function filter(value: any) { for (const key of keys) { if (filteredKeys.indexOf(key) < 0) { - result += " " + key + ": " + withSpaces(JSON.stringify(value[key], null, 2)) + "\n"; + result += "\n " + key + ": " + withSpaces(JSON.stringify(value[key], null, 2)); } } diff --git a/server/state.ts b/server/state.ts index 191e930..7a160e1 100644 --- a/server/state.ts +++ b/server/state.ts @@ -6,7 +6,11 @@ export class State { device!: SprinklersDevice; start() { - this.mqttClient = new mqtt.MqttApiClient("mqtt://localhost:1883"); + const mqttUrl = process.env.MQTT_URL; + if (!mqttUrl) { + throw new Error("Must specify a MQTT_URL to connect to"); + } + this.mqttClient = new mqtt.MqttApiClient(mqttUrl); this.device = this.mqttClient.getDevice("grinklers"); this.mqttClient.start(); diff --git a/server/websocket/index.ts b/server/websocket/index.ts index 3edf4e9..70807d6 100644 --- a/server/websocket/index.ts +++ b/server/websocket/index.ts @@ -7,7 +7,7 @@ import * as requests from "@common/sprinklers/requests"; import * as schema from "@common/sprinklers/schema"; import * as ws from "@common/sprinklers/websocketData"; -import {state} from "../state"; +import { state } from "../state"; async function doDeviceCallRequest(requestData: ws.IDeviceCallRequest) { const { deviceName, data } = requestData; @@ -37,12 +37,24 @@ async function deviceCallRequest(socket: WebSocket, data: ws.IDeviceCallRequest) } export function handler(socket: WebSocket) { - const stop = autorun(() => { - const json = serialize(schema.sprinklersDevice, state.device); - log.trace({ device: json }); - const data = { type: "deviceUpdate", name: "grinklers", data: json }; - socket.send(JSON.stringify(data)); - }, { delay: 100 }); + const disposers = [ + autorun(() => { + const json = serialize(schema.sprinklersDevice, state.device); + log.trace({ device: json }); + const data: ws.IDeviceUpdate = { type: "deviceUpdate", name: "grinklers", data: json }; + socket.send(JSON.stringify(data)); + }, { delay: 100 }), + autorun(() => { + const data: ws.IBrokerConnectionUpdate = { + type: "brokerConnectionUpdate", + brokerConnected: state.mqttClient.connected, + }; + socket.send(JSON.stringify(data)); + }), + ]; + const stop = () => { + disposers.forEach((disposer) => disposer()); + }; socket.on("message", (socketData: WebSocket.Data) => { if (typeof socketData !== "string") { return log.error({ type: typeof socketData }, "received invalid socket data type from client"); diff --git a/yarn.lock b/yarn.lock index c33de13..c9da8a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -144,15 +144,6 @@ ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^5.1.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - ajv@^6.1.0: version "6.4.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.4.0.tgz#d3aff78e9277549771daf0164cff48482b754fc6" @@ -226,8 +217,8 @@ aproba@^1.0.3, aproba@^1.1.1: resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" are-we-there-yet@~1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" dependencies: delegates "^1.0.0" readable-stream "^2.0.6" @@ -434,11 +425,7 @@ aws-sign2@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - -aws4@^1.2.1, aws4@^1.6.0: +aws4@^1.2.1: version "1.7.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" @@ -1137,18 +1124,6 @@ boom@2.x.x: dependencies: hoek "2.x.x" -boom@4.x.x: - version "4.3.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" - dependencies: - hoek "4.x.x" - -boom@5.x.x: - version "5.2.0" - resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" - dependencies: - hoek "4.x.x" - boxen@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" @@ -1693,7 +1668,7 @@ colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" -combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5: +combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" dependencies: @@ -1906,12 +1881,6 @@ cryptiles@2.x.x: dependencies: boom "2.x.x" -cryptiles@3.x.x: - version "3.1.2" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" - dependencies: - boom "5.x.x" - crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -2589,7 +2558,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: +extend@^3.0.0, extend@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" @@ -2821,14 +2790,6 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -form-data@~2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" - dependencies: - asynckit "^0.4.0" - combined-stream "1.0.6" - mime-types "^2.1.12" - forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -2909,8 +2870,8 @@ gauge@~2.7.3: wide-align "^1.1.0" gaze@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.2.tgz#847224677adb8870d679257ed3388fdb61e40105" + version "1.1.3" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" dependencies: globule "^1.0.0" @@ -3068,11 +3029,11 @@ globby@^6.1.0: pinkie-promise "^2.0.0" globule@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.0.tgz#1dc49c6822dd9e8a2fa00ba2a295006e8664bd09" + version "1.2.1" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d" dependencies: glob "~7.1.1" - lodash "~4.17.4" + lodash "~4.17.10" minimatch "~3.0.2" got@^6.7.1: @@ -3156,10 +3117,6 @@ har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - har-validator@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" @@ -3176,13 +3133,6 @@ har-validator@~4.2.1: ajv "^4.9.1" har-schema "^1.0.5" -har-validator@~5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" - dependencies: - ajv "^5.1.0" - har-schema "^2.0.0" - has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -3277,15 +3227,6 @@ hawk@3.1.3, hawk@~3.1.3: hoek "2.x.x" sntp "1.x.x" -hawk@~6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" - dependencies: - boom "4.x.x" - cryptiles "3.x.x" - hoek "4.x.x" - sntp "2.x.x" - he@1.1.x: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" @@ -3311,10 +3252,6 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" -hoek@4.x.x: - version "4.2.1" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" - hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40" @@ -3440,14 +3377,6 @@ http-signature@~1.1.0: jsprim "^1.2.2" sshpk "^1.7.0" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -3996,7 +3925,11 @@ jquery@x.*: version "3.3.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" -js-base64@^2.1.8, js-base64@^2.1.9: +js-base64@^2.1.8: + version "2.4.5" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.5.tgz#e293cd3c7c82f070d700fc7a1ca0a2e69f101f92" + +js-base64@^2.1.9: version "2.4.3" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.3.tgz#2e545ec2b0f2957f41356510205214e98fad6582" @@ -4307,7 +4240,11 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@~4.17.4: +lodash@^4.0.0, lodash@~4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + +lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" @@ -4363,7 +4300,14 @@ lowercase-keys@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" -lru-cache@^4.0.1, lru-cache@^4.1.1: +lru-cache@^4.0.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.2.tgz#45234b2e6e2f2b33da125624c4664929a0224c3f" dependencies: @@ -4761,18 +4705,17 @@ node-forge@0.7.1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.1.tgz#9da611ea08982f4b94206b3beb4cc9665f20c300" node-gyp@^3.3.1: - version "3.6.2" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60" + version "3.7.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.7.0.tgz#789478e8f6c45e277aa014f3e28f958f286f9203" dependencies: fstream "^1.0.0" glob "^7.0.3" graceful-fs "^4.1.2" - minimatch "^3.0.2" mkdirp "^0.5.0" nopt "2 || 3" npmlog "0 || 1 || 2 || 3 || 4" osenv "0" - request "2" + request ">=2.9.0 <2.82.0" rimraf "2" semver "~5.3.0" tar "^2.0.0" @@ -4823,8 +4766,8 @@ node-pre-gyp@^0.6.39: tar-pack "^3.4.0" node-sass@^4.8.3: - version "4.8.3" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.8.3.tgz#d077cc20a08ac06f661ca44fb6f19cd2ed41debb" + version "4.9.0" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.0.tgz#d1b8aa855d98ed684d6848db929a20771cc2ae52" dependencies: async-foreach "^0.1.3" chalk "^1.1.1" @@ -4966,7 +4909,7 @@ number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" -oauth-sign@~0.8.1, oauth-sign@~0.8.2: +oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" @@ -5317,10 +5260,6 @@ performance-now@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -6046,7 +5985,7 @@ q@^1.1.2: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" -qs@6.5.1, qs@~6.5.1: +qs@6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" @@ -6430,34 +6369,7 @@ replace-ext@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" -request@2: - version "2.85.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.6.0" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" - forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" - hawk "~6.0.2" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.17" - oauth-sign "~0.8.2" - performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - stringstream "~0.0.5" - tough-cookie "~2.3.3" - tunnel-agent "^0.6.0" - uuid "^3.1.0" - -request@2.81.0: +request@2.81.0, "request@>=2.9.0 <2.82.0": version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" dependencies: @@ -6629,17 +6541,21 @@ rxjs@^5.4.2, rxjs@^5.5.2: dependencies: symbol-observable "1.0.1" -safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.1, safe-buffer@^5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" dependencies: ret "~0.1.10" -safer-buffer@^2.1.0: +safer-buffer@^2.0.2, safer-buffer@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -6910,12 +6826,6 @@ sntp@1.x.x: dependencies: hoek "2.x.x" -sntp@2.x.x: - version "2.1.0" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" - dependencies: - hoek "4.x.x" - sockjs-client@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.4.tgz#5babe386b775e4cf14e7520911452654016c8b12" @@ -7066,13 +6976,14 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" sshpk@^1.7.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" + version "1.14.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" dashdash "^1.12.0" getpass "^0.1.1" + safer-buffer "^2.0.2" optionalDependencies: bcrypt-pbkdf "^1.0.0" ecc-jsbn "~0.1.1" @@ -7162,7 +7073,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: @@ -7187,9 +7098,9 @@ string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" -stringstream@~0.0.4, stringstream@~0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" +stringstream@~0.0.4: + version "0.0.6" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.6.tgz#7880225b0d4ad10e30927d167a1d6f2fd3b33a72" strip-ansi@3.0.1, strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" @@ -7412,7 +7323,7 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@~2.3.0, tough-cookie@~2.3.3: +tough-cookie@~2.3.0: version "2.3.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" dependencies: @@ -8007,17 +7918,23 @@ which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" -which@1, which@^1.2.14, which@^1.2.9: +which@1, which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + dependencies: + isexe "^2.0.0" + +which@^1.2.14: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" dependencies: isexe "^2.0.0" wide-align@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" dependencies: - string-width "^1.0.2" + string-width "^1.0.2 || 2" widest-line@^2.0.0: version "2.0.0"