"version": "0.1.0",
"private": true,
"dependencies": {
- "@material-ui/core": "1.0.0",
+ "@material-ui/core": "1.2.0",
+ "@material-ui/icons": "^1.1.0",
"lodash": "4.17.10",
- "react": "16.3.2",
- "react-dom": "16.3.2",
+ "axios": "0.18.0",
+ "react": "16.4.0",
+ "react-dom": "16.4.0",
"react-redux": "5.0.7",
"react-router": "4.2.0",
"react-router-dom": "4.2.2",
"react-router-redux": "5.0.0-alpha.9",
"react-scripts-ts": "2.16.0",
"redux": "4.0.0",
- "redux-devtools": "3.4.1",
- "typesafe-actions": "2.0.3"
+ "redux-thunk": "2.3.0",
+ "unionize": "2.1.2"
},
"scripts": {
"start": "react-scripts-ts start",
"devDependencies": {
"@types/enzyme": "^3.1.10",
"@types/enzyme-adapter-react-16": "^1.0.2",
- "@types/jest": "22.2.3",
- "@types/lodash": "4.14.109",
- "@types/node": "10.1.2",
- "@types/react": "16.3.14",
+ "@types/jest": "23.0.0",
+ "@types/node": "10.3.0",
+ "@types/react": "16.3.16",
"@types/react-dom": "16.0.5",
- "@types/react-redux": "6.0.0",
- "@types/react-router": "4.0.25",
- "@types/react-router-dom": "4.2.6",
- "@types/react-router-redux": "5.0.14",
+ "@types/react-redux": "6.0.1",
+ "@types/react-router": "4.0.26",
+ "@types/react-router-dom": "4.2.7",
+ "@types/react-router-redux": "5.0.15",
"@types/redux-devtools": "3.0.44",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
- "typescript": "2.8.3"
+ "jest-localstorage-mock": "2.2.0",
+ "redux-devtools": "3.4.1",
+ "typescript": "2.9.1"
},
"moduleNameMapper": {
"^~/(.*)$": "<rootDir>/src/$1"
--- /dev/null
+#!/bin/bash -x
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+# The script uses the docker image composer-build:latest
+# Usage docker run -ti -v /var/lib/jenkins/workspace/build-packages-workbench2/:/tmp/workbench2 composer-build:latest /tmp/workbench2/run-tests-build.sh --build_version 1.0.1
+format_last_commit_here() {
+ local format="$1"; shift
+ TZ=UTC git log -n1 --first-parent "--format=format:$format" .
+}
+
+version_from_git() {
+ # Output the version being built, or if we're building a
+ # dev/prerelease, output a version number based on the git log for
+ # the current working directory.
+ if [[ -n "$ARVADOS_BUILDING_VERSION" ]]; then
+ echo "$ARVADOS_BUILDING_VERSION"
+ return
+ fi
+
+ local git_ts git_hash prefix
+ if [[ -n "$1" ]] ; then
+ prefix="$1"
+ else
+ prefix="0.1"
+ fi
+
+ declare $(format_last_commit_here "git_ts=%ct git_hash=%h")
+ ARVADOS_BUILDING_VERSION="$(git describe --abbrev=0).$(date -ud "@$git_ts" +%Y%m%d%H%M%S)"
+ echo "$ARVADOS_BUILDING_VERSION"
+}
+
+nohash_version_from_git() {
+ version_from_git $1 | cut -d. -f1-3
+}
+
+timestamp_from_git() {
+ format_last_commit_here "%ct"
+}
+
+WORKDIR="/tmp/workbench2"
+cd $WORKDIR
+if [[ -n "$2" ]]; then
+ build_version="$2"
+else
+ build_version="$(version_from_git)"
+fi
+rm -Rf $WORKDIR/node_modules
+rm -f $WORKDIR/*.deb; rm -f $WORKDIR/*.rpm
+# run test and build dist
+make test
+#make build
+yarn build
+
+# Build deb and rpm packages using fpm from dist passing the destination folder for the deploy to be /var/www/arvados-workbench2/
+fpm -s dir -t deb -n arvados-workbench2 -v "$build_version" "--maintainer=Ward Vandewege <ward@curoverse.com>" --description "workbench2 Package" --deb-no-default-config-files $WORKDIR/build/=/var/www/arvados-workbench2/workbench2/
+fpm -s dir -t rpm -n arvados-workbench2 -v "$build_version" "--maintainer=Ward Vandewege <ward@curoverse.com>" --description "workbench2 Package" $WORKDIR/build/=/var/www/arvados-workbench2/workbench2/
+
+mkdir $WORKDIR/packages
+mkdir $WORKDIR/packages/centos7
+mkdir $WORKDIR/packages/ubuntu1404
+mkdir $WORKDIR/packages/ubuntu1604
+mkdir $WORKDIR/packages/debian8
+mkdir $WORKDIR/packages/debian9
+cp $WORKDIR/*.rpm $WORKDIR/packages/centos7/
+cp $WORKDIR/*.deb $WORKDIR/packages/ubuntu1404/
+cp $WORKDIR/*.deb $WORKDIR/packages/ubuntu1604/
+cp $WORKDIR/*.deb $WORKDIR/packages/debian8
+cp $WORKDIR/*.deb $WORKDIR/packages/debian9
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+export enum FilterField {
+ UUID = "uuid"
+}
+
+export default class FilterBuilder {
+ private filters = "";
+
+ private addCondition(field: FilterField, cond: string, value?: string) {
+ if (value) {
+ this.filters += `["${field}","${cond}","${value}"]`;
+ }
+ return this;
+ }
+
+ public addEqual(field: FilterField, value?: string) {
+ return this.addCondition(field, "=", value);
+ }
+
+ public addLike(field: FilterField, value?: string) {
+ return this.addCondition(field, "like", value);
+ }
+
+ public addILike(field: FilterField, value?: string) {
+ return this.addCondition(field, "ilike", value);
+ }
+
+ public get() {
+ return "[" + this.filters + "]";
+ }
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import Axios, { AxiosInstance } from "axios";
+
+export const API_HOST = 'https://qr1hi.arvadosapi.com';
+
+export const serverApi: AxiosInstance = Axios.create({
+ baseURL: API_HOST + '/arvados/v1'
+});
+
+export function setServerApiAuthorizationHeader(token: string) {
+ serverApi.defaults.headers.common = {
+ 'Authorization': `OAuth2 ${token}`
+ };}
+
+export function removeServerApiAuthorizationHeader() {
+ delete serverApi.defaults.headers.common.Authorization;
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+export default class UrlBuilder {
+ private url: string = "";
+ private query: string = "";
+
+ constructor(host: string) {
+ this.url = host;
+ }
+
+ public addParam(param: string, value: string) {
+ if (this.query.length === 0) {
+ this.query += "?";
+ } else {
+ this.query += "&";
+ }
+ this.query += `${param}=${value}`;
+ return this;
+ }
+
+ public get() {
+ return this.url + this.query;
+ }
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Redirect, RouteProps } from "react-router";
+import * as React from "react";
+import { connect, DispatchProp } from "react-redux";
+import authActions from "../../store/auth/auth-action";
+import { authService, projectService } from "../../services/services";
+
+interface ApiTokenProps {
+}
+
+class ApiToken extends React.Component<ApiTokenProps & RouteProps & DispatchProp<any>, {}> {
+ static getUrlParameter(search: string, name: string) {
+ const safeName = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
+ const regex = new RegExp('[\\?&]' + safeName + '=([^&#]*)');
+ const results = regex.exec(search);
+ return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
+ };
+
+ componentDidMount() {
+ const search = this.props.location ? this.props.location.search : "";
+ const apiToken = ApiToken.getUrlParameter(search, 'api_token');
+ this.props.dispatch(authActions.SAVE_API_TOKEN(apiToken));
+ this.props.dispatch(authService.getUserDetails());
+ this.props.dispatch(projectService.getProjectList());
+ }
+ render() {
+ return <Redirect to="/"/>
+ }
+}
+
+export default connect()(ApiToken);
import Workbench from './views/workbench/workbench';
import ProjectList from './components/project-list/project-list';
import './index.css';
-import { Route, Router } from "react-router";
+import { Route } from "react-router";
import createBrowserHistory from "history/createBrowserHistory";
import configureStore from "./store/store";
import { ConnectedRouter } from "react-router-redux";
+import ApiToken from "./components/api-token/api-token";
+import authActions from "./store/auth/auth-action";
+import { projectService } from "./services/services";
import { TreeItem } from "./components/tree/tree";
import { Project } from "./models/project";
-const sampleProjects = [
- [
- 'Project 1', [
- ['Project 1.1', [['Project 1.1.1'], ['Project 1.1.2']]],
- ['Project 1.2', [['Project 1.2.1'], ['Project 1.2.2'], ['Project 1.2.3']]]
- ]
- ],
- [
- 'Project 2'
- ],
- [
- 'Project 3', [['Project 3.1'], ['Project 3.2']]
- ]
-];
-
-
function buildProjectTree(tree: any[], level = 0): Array<TreeItem<Project>> {
const projects = tree.map((t, idx) => ({
id: `l${level}i${idx}${t[0]}`,
}));
return projects;
}
-
-
const history = createBrowserHistory();
const projects = buildProjectTree(sampleProjects);
const store = configureStore({
- projects,
+ projects: [
+ ],
router: {
location: null
+ },
+ auth: {
+ user: undefined
}
}, history);
+store.dispatch(authActions.INIT());
+store.dispatch<any>(projectService.getProjectList());
+
+
const App = () =>
<Provider store={store}>
<ConnectedRouter history={history}>
<div>
- <Route path="/" component={Workbench} />
+ <Route path="/" component={Workbench}/>
+ <Route path="/token" component={ApiToken}/>
</div>
</ConnectedRouter>
</Provider>;
ReactDOM.render(
- <App />,
+ <App/>,
document.getElementById('root') as HTMLElement
);
export interface Project {
name: string;
createdAt: string;
+ modifiedAt: string;
+ uuid: string;
+ ownerUuid: string;
+ href: string;
icon?: any;
}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+export interface User {
+ email: string;
+ firstName: string;
+ lastName: string;
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { API_HOST, serverApi } from "../../common/api/server-api";
+import { User } from "../../models/user";
+import { Dispatch } from "redux";
+import actions from "../../store/auth/auth-action";
+
+export const API_TOKEN_KEY = 'apiToken';
+export const USER_EMAIL_KEY = 'userEmail';
+export const USER_FIRST_NAME_KEY = 'userFirstName';
+export const USER_LAST_NAME_KEY = 'userLastName';
+
+export interface UserDetailsResponse {
+ email: string;
+ first_name: string;
+ last_name: string;
+ is_admin: boolean;
+}
+
+export default class AuthService {
+
+ public saveApiToken(token: string) {
+ localStorage.setItem(API_TOKEN_KEY, token);
+ }
+
+ public removeApiToken() {
+ localStorage.removeItem(API_TOKEN_KEY);
+ }
+
+ public getApiToken() {
+ return localStorage.getItem(API_TOKEN_KEY) || undefined;
+ }
+
+ public getUser(): User | undefined {
+ const email = localStorage.getItem(USER_EMAIL_KEY);
+ const firstName = localStorage.getItem(USER_FIRST_NAME_KEY);
+ const lastName = localStorage.getItem(USER_LAST_NAME_KEY);
+ return email && firstName && lastName
+ ? { email, firstName, lastName }
+ : undefined;
+ }
+
+ public saveUser(user: User) {
+ localStorage.setItem(USER_EMAIL_KEY, user.email);
+ localStorage.setItem(USER_FIRST_NAME_KEY, user.firstName);
+ localStorage.setItem(USER_LAST_NAME_KEY, user.lastName);
+ }
+
+ public removeUser() {
+ localStorage.removeItem(USER_EMAIL_KEY);
+ localStorage.removeItem(USER_FIRST_NAME_KEY);
+ localStorage.removeItem(USER_LAST_NAME_KEY);
+ }
+
+ public login() {
+ const currentUrl = `${window.location.protocol}//${window.location.host}/token`;
+ window.location.assign(`${API_HOST}/login?return_to=${currentUrl}`);
+ }
+
+ public logout() {
+ const currentUrl = `${window.location.protocol}//${window.location.host}`;
+ window.location.assign(`${API_HOST}/logout?return_to=${currentUrl}`);
+ }
+
+ public getUserDetails = () => (dispatch: Dispatch) => {
+ dispatch(actions.USER_DETAILS_REQUEST());
+ serverApi
+ .get<UserDetailsResponse>('/users/current')
+ .then(resp => {
+ dispatch(actions.USER_DETAILS_SUCCESS(resp.data));
+ })
+ // .catch(err => {
+ // });
+ };
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { serverApi } from "../../common/api/server-api";
+import { Dispatch } from "redux";
+import actions from "../../store/project/project-action";
+import { Project } from "../../models/project";
+import UrlBuilder from "../../common/api/url-builder";
+import FilterBuilder, { FilterField } from "../../common/api/filter-builder";
+
+interface GroupsResponse {
+ offset: number;
+ limit: number;
+ items: Array<{
+ href: string;
+ kind: string;
+ etag: string;
+ uuid: string;
+ owner_uuid: string;
+ created_at: string;
+ modified_by_client_uuid: string;
+ modified_by_user_uuid: string;
+ modified_at: string;
+ name: string;
+ group_class: string;
+ description: string;
+ writable_by: string[];
+ delete_at: string;
+ trash_at: string;
+ is_trashed: boolean;
+ }>;
+}
+
+export default class ProjectService {
+ public getProjectList = (parentUuid?: string) => (dispatch: Dispatch) => {
+ dispatch(actions.PROJECTS_REQUEST());
+
+ const ub = new UrlBuilder('/groups');
+ const fb = new FilterBuilder();
+ fb.addEqual(FilterField.UUID, parentUuid);
+ const url = ub.addParam('filter', fb.get()).get();
+
+ serverApi.get<GroupsResponse>(url).then(groups => {
+ const projects = groups.data.items.map(g => ({
+ name: g.name,
+ createdAt: g.created_at,
+ modifiedAt: g.modified_at,
+ href: g.href,
+ uuid: g.uuid,
+ ownerUuid: g.owner_uuid
+ } as Project));
+ dispatch(actions.PROJECTS_SUCCESS(projects));
+ });
+ };
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import AuthService from "./auth-service/auth-service";
+import ProjectService from "./project-service/project-service";
+
+export const authService = new AuthService();
+export const projectService = new ProjectService();
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { ofType, default as unionize, UnionOf } from "unionize";
+import { UserDetailsResponse } from "../../services/auth-service/auth-service";
+
+const actions = unionize({
+ SAVE_API_TOKEN: ofType<string>(),
+ LOGIN: {},
+ LOGOUT: {},
+ INIT: {},
+ USER_DETAILS_REQUEST: {},
+ USER_DETAILS_SUCCESS: ofType<UserDetailsResponse>()
+}, {
+ tag: 'type',
+ value: 'payload'
+});
+
+export type AuthAction = UnionOf<typeof actions>;
+export default actions;
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import authReducer from "./auth-reducer";
+import actions from "./auth-action";
+import {
+ API_TOKEN_KEY,
+ USER_EMAIL_KEY,
+ USER_FIRST_NAME_KEY,
+ USER_LAST_NAME_KEY
+} from "../../services/auth-service/auth-service";
+import { API_HOST } from "../../common/api/server-api";
+
+import 'jest-localstorage-mock';
+
+describe('auth-reducer', () => {
+ beforeAll(() => {
+ localStorage.clear();
+ });
+
+ it('should return default state on initialisation', () => {
+ const initialState = undefined;
+ const state = authReducer(initialState, actions.INIT());
+ expect(state).toEqual({
+ apiToken: undefined,
+ user: undefined
+ });
+ });
+
+ it('should read user and api token from local storage on init if they are there', () => {
+ const initialState = undefined;
+
+ localStorage.setItem(API_TOKEN_KEY, "token");
+ localStorage.setItem(USER_EMAIL_KEY, "test@test.com");
+ localStorage.setItem(USER_FIRST_NAME_KEY, "John");
+ localStorage.setItem(USER_LAST_NAME_KEY, "Doe");
+
+ const state = authReducer(initialState, actions.INIT());
+ expect(state).toEqual({
+ apiToken: "token",
+ user: {
+ email: "test@test.com",
+ firstName: "John",
+ lastName: "Doe"
+ }
+ });
+ });
+
+ it('should store token in local storage', () => {
+ const initialState = undefined;
+
+ const state = authReducer(initialState, actions.SAVE_API_TOKEN("token"));
+ expect(state).toEqual({
+ apiToken: "token",
+ user: undefined
+ });
+
+ expect(localStorage.getItem(API_TOKEN_KEY)).toBe("token");
+ });
+
+ it('should set user details on success fetch', () => {
+ const initialState = undefined;
+
+ const userDetails = {
+ email: "test@test.com",
+ first_name: "John",
+ last_name: "Doe",
+ is_admin: true
+ };
+
+ const state = authReducer(initialState, actions.USER_DETAILS_SUCCESS(userDetails));
+ expect(state).toEqual({
+ apiToken: undefined,
+ user: {
+ email: "test@test.com",
+ firstName: "John",
+ lastName: "Doe"
+ }
+ });
+
+ expect(localStorage.getItem(API_TOKEN_KEY)).toBe("token");
+ });
+
+ it('should fire external url to login', () => {
+ const initialState = undefined;
+ window.location.assign = jest.fn();
+ authReducer(initialState, actions.LOGIN());
+ expect(window.location.assign).toBeCalledWith(
+ `${API_HOST}/login?return_to=${window.location.protocol}//${window.location.host}/token`
+ );
+ });
+
+ it('should fire external url to logout', () => {
+ const initialState = undefined;
+ window.location.assign = jest.fn();
+ authReducer(initialState, actions.LOGOUT());
+ expect(window.location.assign).toBeCalledWith(
+ `${API_HOST}/logout?return_to=${location.protocol}//${location.host}`
+ );
+ });
+});
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import actions, { AuthAction } from "./auth-action";
+import { User } from "../../models/user";
+import { authService } from "../../services/services";
+import { removeServerApiAuthorizationHeader, setServerApiAuthorizationHeader } from "../../common/api/server-api";
+import { UserDetailsResponse } from "../../services/auth-service/auth-service";
+
+export interface AuthState {
+ user?: User;
+ apiToken?: string;
+};
+
+const authReducer = (state: AuthState = {}, action: AuthAction) => {
+ return actions.match(action, {
+ SAVE_API_TOKEN: (token: string) => {
+ authService.saveApiToken(token);
+ setServerApiAuthorizationHeader(token);
+ return {...state, apiToken: token};
+ },
+ INIT: () => {
+ const user = authService.getUser();
+ const token = authService.getApiToken();
+ if (token) {
+ setServerApiAuthorizationHeader(token);
+ }
+ return {user, apiToken: token};
+ },
+ LOGIN: () => {
+ authService.login();
+ return state;
+ },
+ LOGOUT: () => {
+ authService.removeApiToken();
+ authService.removeUser();
+ removeServerApiAuthorizationHeader();
+ authService.logout();
+ return {...state, apiToken: undefined};
+ },
+ USER_DETAILS_SUCCESS: (ud: UserDetailsResponse) => {
+ const user = {
+ email: ud.email,
+ firstName: ud.first_name,
+ lastName: ud.last_name
+ };
+ authService.saveUser(user);
+ return {...state, user};
+ },
+ default: () => state
+ });
+};
+
+export default authReducer;
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Project } from "../../models/project";
+import { default as unionize, ofType, UnionOf } from "unionize";
+
+const actions = unionize({
+ CREATE_PROJECT: ofType<Project>(),
+ REMOVE_PROJECT: ofType<string>(),
+ PROJECTS_REQUEST: {},
+ PROJECTS_SUCCESS: ofType<Project[]>(),
+ TOGGLE_PROJECT_TREE_ITEM: ofType<string>()
+}, {
+ tag: 'type',
+ value: 'payload'
+});
+
+export type ProjectAction = UnionOf<typeof actions>;
+export default actions;
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import projectsReducer from "./project-reducer";
+import actions from "./project-action";
+
+describe('project-reducer', () => {
+ it('should add new project to the list', () => {
+ const initialState = undefined;
+ const project = {
+ name: 'test',
+ href: 'href',
+ createdAt: '2018-01-01',
+ modifiedAt: '2018-01-01',
+ ownerUuid: 'owner-test123',
+ uuid: 'test123'
+ };
+
+ const state = projectsReducer(initialState, actions.CREATE_PROJECT(project));
+ expect(state).toEqual([project]);
+ });
+
+ it('should load projects', () => {
+ const initialState = undefined;
+ const project = {
+ name: 'test',
+ href: 'href',
+ createdAt: '2018-01-01',
+ modifiedAt: '2018-01-01',
+ ownerUuid: 'owner-test123',
+ uuid: 'test123'
+ };
+
+ const projects = [project, project];
+ const state = projectsReducer(initialState, actions.PROJECTS_SUCCESS(projects));
+ expect(state).toEqual(projects);
+ });
+});
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Project } from "../../models/project";
+import actions, { ProjectAction } from "./project-action";
+import { TreeItem } from "../../components/tree/tree";
+import * as _ from "lodash";
+
+export type ProjectState = Array<TreeItem<Project>>;
+
+function findTreeItem<T>(tree: Array<TreeItem<T>>, itemId: string): TreeItem<T> | undefined {
+ let item;
+ for (const t of tree) {
+ item = t.id === itemId
+ ? t
+ : findTreeItem(t.items ? t.items : [], itemId);
+ if (item) {
+ break;
+ }
+ }
+ return item;
+}
+
+function resetTreeActivity<T>(tree: Array<TreeItem<T>>): boolean | undefined {
+ let item;
+ for (const leaf of tree) {
+ item = leaf.active === true
+ ? leaf.active = false
+ : resetTreeActivity(leaf.items ? leaf.items : []);
+ }
+ return item;
+}
+
+const projectsReducer = (state: ProjectState = [], action: ProjectAction) => {
+ return actions.match(action, {
+ CREATE_PROJECT: project => [...state, project],
+ REMOVE_PROJECT: () => state,
+ PROJECTS_REQUEST: () => state,
+ PROJECTS_SUCCESS: projects => {
+ return projects;
+ },
+ TOGGLE_PROJECT_TREE_ITEM: itemId => {
+ const tree = _.cloneDeep(state);
+ resetTreeActivity(tree);
+ const item = findTreeItem(tree, itemId);
+ if (item) {
+ item.open = !item.open;
+ item.active = true;
+ }
+ return tree;
+ },
+ default: () => state
+ });
+};
+
+export default projectsReducer;
+++ /dev/null
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { combineReducers } from "redux";
-import { StateType } from "typesafe-actions";
-import projectsReducer from "./project-reducer";
-import { routerReducer } from "react-router-redux";
-
-const rootReducer = combineReducers({
- projects: projectsReducer,
- router: routerReducer
-});
-
-export type RootState = StateType<typeof rootReducer>;
-
-export default rootReducer;
//
// SPDX-License-Identifier: AGPL-3.0
-import { createStore, applyMiddleware, compose, Middleware } from 'redux';
-import { default as rootReducer, RootState } from "./root-reducer";
-import { routerMiddleware } from "react-router-redux";
+import { createStore, applyMiddleware, compose, Middleware, combineReducers } from 'redux';
+import { routerMiddleware, routerReducer, RouterState } from "react-router-redux";
+import thunkMiddleware from 'redux-thunk';
import { History } from "history";
+import projectsReducer, { ProjectState } from "./project/project-reducer";
+import authReducer, { AuthState } from "./auth/auth-reducer";
const composeEnhancers =
(process.env.NODE_ENV === 'development' &&
window && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
compose;
+export interface RootState {
+ auth: AuthState,
+ projects: ProjectState,
+ router: RouterState
+}
+
+const rootReducer = combineReducers({
+ auth: authReducer,
+ projects: projectsReducer,
+ router: routerReducer
+});
+
+
export default function configureStore(initialState: RootState, history: History) {
const middlewares: Middleware[] = [
- routerMiddleware(history)
+ routerMiddleware(history),
+ thunkMiddleware
];
const enhancer = composeEnhancers(applyMiddleware(...middlewares));
return createStore(rootReducer, initialState!, enhancer);
it('renders without crashing', () => {
const div = document.createElement('div');
- const store = configureStore({ projects: [], router: { location: null } }, createBrowserHistory());
+ const store = configureStore({ projects: [], router: { location: null }, auth: {} }, createBrowserHistory());
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
-import { connect } from "react-redux";
-import { RootState } from "../../store/root-reducer";
+import { connect, DispatchProp } from "react-redux";
+import Tree from "../../components/tree/tree";
+import { Project } from "../../models/project";
import ProjectList from "../../components/project-list/project-list";
import { Route, Switch } from "react-router";
import { Link } from "react-router-dom";
+import Button from "@material-ui/core/Button/Button";
+import authActions from "../../store/auth/auth-action";
+import IconButton from "@material-ui/core/IconButton/IconButton";
+import Menu from "@material-ui/core/Menu/Menu";
+import MenuItem from "@material-ui/core/MenuItem/MenuItem";
+import { AccountCircle } from "@material-ui/icons";
+import { User } from "../../models/user";
+import Grid from "@material-ui/core/Grid/Grid";
+import { RootState } from "../../store/store";
import { actions as projectActions } from "../../store/project-action";
import ProjectTree, { WorkbenchProps } from '../../components/project-tree/project-tree';
overflow: 'hidden',
position: 'relative',
display: 'flex',
- width: '100%',
- height: '100%'
+ width: '100vw',
+ height: '100vh'
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
flexGrow: 1,
backgroundColor: theme.palette.background.default,
padding: theme.spacing.unit * 3,
+ height: '100%',
minWidth: 0,
},
toolbar: theme.mixins.toolbar
});
-class Workbench extends React.Component<WorkbenchProps & WithStyles<CssRules>> {
+interface WorkbenchDataProps {
+ projects: Project[];
+ user?: User;
+}
+
+interface WorkbenchActionProps {
+}
+
+type WorkbenchProps = WorkbenchDataProps & WorkbenchActionProps & DispatchProp & WithStyles<CssRules>;
+
+interface WorkbenchState {
+ anchorEl: any;
+}
+
+class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
+ constructor(props: WorkbenchProps) {
+ super(props);
+ this.state = {
+ anchorEl: null
+ }
+ }
+
+ login = () => {
+ this.props.dispatch(authActions.LOGIN());
+ };
+
+ logout = () => {
+ this.handleClose();
+ this.props.dispatch(authActions.LOGOUT());
+ };
+
+ handleOpenMenu = (event: React.MouseEvent<any>) => {
+ this.setState({
+ anchorEl: event.currentTarget
+ });
+ };
+
+ handleClose = () => {
+ this.setState({
+ anchorEl: null
+ });
+ };
+
render() {
- const { classes } = this.props;
+ const {classes, user} = this.props;
return (
<div className={classes.root}>
<AppBar position="absolute" className={classes.appBar}>
<Toolbar>
- <Typography variant="title" color="inherit" noWrap>
- Arvados<br />Workbench 2
+ <Typography variant="title" color="inherit" noWrap style={{flexGrow: 1}}>
+ <span>Arvados</span><br/><span style={{fontSize: 12}}>Workbench 2</span>
</Typography>
+ {user ?
+ <Grid container style={{width: 'auto'}}>
+ <Grid container style={{width: 'auto'}} alignItems='center'>
+ <Typography variant="title" color="inherit" noWrap>
+ {user.firstName} {user.lastName}
+ </Typography>
+ </Grid>
+ <Grid item>
+ <IconButton
+ aria-owns={this.state.anchorEl ? 'menu-appbar' : undefined}
+ aria-haspopup="true"
+ onClick={this.handleOpenMenu}
+ color="inherit">
+ <AccountCircle/>
+ </IconButton>
+ </Grid>
+ <Menu
+ id="menu-appbar"
+ anchorEl={this.state.anchorEl}
+ anchorOrigin={{
+ vertical: 'top',
+ horizontal: 'right',
+ }}
+ transformOrigin={{
+ vertical: 'top',
+ horizontal: 'right',
+ }}
+ open={!!this.state.anchorEl}
+ onClose={this.handleClose}>
+ <MenuItem onClick={this.logout}>Logout</MenuItem>
+ <MenuItem onClick={this.handleClose}>My account</MenuItem>
+ </Menu>
+ </Grid>
+ :
+ <Button color="inherit" onClick={this.login}>Login</Button>
+ }
</Toolbar>
</AppBar>
+ {user &&
<Drawer
variant="permanent"
classes={{
paper: classes.drawerPaper,
}}>
- <div className={classes.toolbar} />
- <ProjectTree
+ <div className={classes.toolbar}/>
+ <ProjectTree
projects={this.props.projects}
- toggleProjectTreeItem={this.props.toggleProjectTreeItem} />
- </Drawer>
+ toggleProjectTreeItem={this.props.toggleProjectTreeItem}/>
+ </Drawer>}
<main className={classes.content}>
- <div className={classes.toolbar} />
+ <div className={classes.toolbar}/>
<Switch>
- <Route exact path="/">
- <Typography noWrap>Hello new workbench!</Typography>
- </Route>
- <Route path="/project/:name" component={ProjectList} />
+ <Route path="/project/:name" component={ProjectList}/>
</Switch>
</main>
</div>
}
}
-export default connect(
+export default connect<WorkbenchDataProps>(
(state: RootState) => ({
- projects: state.projects
- }), {
+ projects: state.projects,
+ user: state.auth.user
+ }){
toggleProjectTreeItem: (id: string) => projectActions.toggleProjectTreeItem(id)
}
)(
"@babel/code-frame@^7.0.0-beta.35":
- version "7.0.0-beta.47"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.47.tgz#d18c2f4c4ba8d093a2bcfab5616593bfe2441a27"
+ version "7.0.0-beta.49"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.49.tgz#becd805482734440c9d137e46d77340e64d7f51b"
dependencies:
- "@babel/highlight" "7.0.0-beta.47"
+ "@babel/highlight" "7.0.0-beta.49"
-"@babel/highlight@7.0.0-beta.47":
- version "7.0.0-beta.47"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.47.tgz#8fbc83fb2a21f0bd2b95cdbeb238cf9689cad494"
+"@babel/highlight@7.0.0-beta.49":
+ version "7.0.0-beta.49"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.49.tgz#96bdc6b43e13482012ba6691b1018492d39622cc"
dependencies:
chalk "^2.0.0"
esutils "^2.0.2"
js-tokens "^3.0.0"
"@babel/runtime@^7.0.0-beta.42":
- version "7.0.0-beta.47"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.47.tgz#273f5e71629e80f6cbcd7507503848615e59f7e0"
+ version "7.0.0-beta.49"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.49.tgz#03b3bf07eb982072c8e851dd2ddd5110282e61bf"
dependencies:
- core-js "^2.5.3"
+ core-js "^2.5.6"
regenerator-runtime "^0.11.1"
-"@material-ui/core@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-1.0.0.tgz#857b871038bb300f2d25594ce0cd250be944e71b"
+"@material-ui/core@1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-1.2.0.tgz#ec372fd44f949faa604c4ccd4b7ee0bc5e08ac8c"
dependencies:
"@babel/runtime" "^7.0.0-beta.42"
"@types/jss" "^9.5.3"
"@types/react-transition-group" "^2.0.8"
brcast "^3.0.1"
classnames "^2.2.5"
+ csstype "^2.5.2"
+ debounce "^1.1.0"
deepmerge "^2.0.1"
dom-helpers "^3.2.1"
hoist-non-react-statics "^2.5.0"
jss-props-sort "^6.0.0"
jss-vendor-prefixer "^7.0.0"
keycode "^2.1.9"
- lodash "^4.2.0"
normalize-scroll-left "^0.1.2"
prop-types "^15.6.0"
- react-event-listener "^0.5.1"
+ react-event-listener "^0.6.0"
react-jss "^8.1.0"
react-popper "^0.10.0"
- react-scrollbar-size "^2.0.2"
react-transition-group "^2.2.1"
recompose "^0.26.0 || ^0.27.0"
scroll "^2.0.3"
- warning "^3.0.0"
+ warning "^4.0.1"
+
+"@material-ui/icons@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-1.1.0.tgz#4d025df7b0ba6ace8d6710079ed76013a4d26595"
+ dependencies:
+ recompose "^0.26.0 || ^0.27.0"
"@types/cheerio@*":
version "0.22.7"
version "4.6.2"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.6.2.tgz#12cfaba693ba20f114ed5765467ff25fdf67ddb0"
-"@types/jest@22.2.3":
- version "22.2.3"
- resolved "https://registry.yarnpkg.com/@types/jest/-/jest-22.2.3.tgz#0157c0316dc3722c43a7b71de3fdf3acbccef10d"
+"@types/jest@23.0.0":
+ version "23.0.0"
+ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.0.0.tgz#760cac74f00bb9c3075587716d2b3b4435663bc0"
"@types/jss@^9.5.3":
version "9.5.3"
version "4.14.109"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.109.tgz#b1c4442239730bf35cabaf493c772b18c045886d"
-"@types/node@*", "@types/node@10.1.2":
- version "10.1.2"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-10.1.2.tgz#1b928a0baa408fc8ae3ac012cc81375addc147c6"
+"@types/node@*", "@types/node@10.3.0":
+ version "10.3.0"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.3.0.tgz#078516315a84d56216b5d4fed8f75d59d3b16cac"
"@types/react-dom@16.0.5":
version "16.0.5"
"@types/node" "*"
"@types/react" "*"
-"@types/react-redux@6.0.0":
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-6.0.0.tgz#31711592b13ed6d6d2fd4506e5447b00e1f0b484"
+"@types/react-redux@6.0.1":
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-6.0.1.tgz#bb8f6cc19d00a999f9d932ab796212ad3921994b"
dependencies:
"@types/react" "*"
redux "^4.0.0"
-"@types/react-router-dom@4.2.6":
- version "4.2.6"
- resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.2.6.tgz#9f7eb3c0e6661a9607d878ff8675cc4ea95cd276"
+"@types/react-router-dom@4.2.7":
+ version "4.2.7"
+ resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.2.7.tgz#9d36bfe175f916dd8d7b6b0237feed6cce376b4c"
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react-router" "*"
-"@types/react-router-redux@5.0.14":
- version "5.0.14"
- resolved "https://registry.yarnpkg.com/@types/react-router-redux/-/react-router-redux-5.0.14.tgz#4f140248f65c74217e296854b1abe8c55e99764c"
+"@types/react-router-redux@5.0.15":
+ version "5.0.15"
+ resolved "https://registry.yarnpkg.com/@types/react-router-redux/-/react-router-redux-5.0.15.tgz#aebc593bd3426adb6ae2eba6ac8c919ee232ce7b"
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react-router" "*"
- redux "^3.7.2"
+ redux ">= 3.7.2"
-"@types/react-router@*", "@types/react-router@4.0.25":
- version "4.0.25"
- resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-4.0.25.tgz#1e25490780b80e0d8f96bf649379cca8638c1e5a"
+"@types/react-router@*", "@types/react-router@4.0.26":
+ version "4.0.26"
+ resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-4.0.26.tgz#4489c873642baa633014294a6d0a290001ba9860"
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react-transition-group@^2.0.8":
- version "2.0.9"
- resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.0.9.tgz#ed6a71fb711e524345844defec2a861c1a222a03"
+ version "2.0.11"
+ resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.0.11.tgz#feb274676a39383fffaa0dff710958d2251abefb"
dependencies:
"@types/react" "*"
-"@types/react@*", "@types/react@16.3.14":
- version "16.3.14"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-16.3.14.tgz#f90ac6834de172e13ecca430dcb6814744225d36"
+"@types/react@*", "@types/react@16.3.16":
+ version "16.3.16"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-16.3.16.tgz#78fc44a90b45701f50c8a7008f733680ba51fc86"
dependencies:
csstype "^2.2.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
acorn@^5.0.0, acorn@^5.3.0:
- version "5.5.3"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
+ version "5.6.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.6.1.tgz#c9e50c3e3717cf897f1b071ceadbb543bbc0a8d4"
address@1.0.3, address@^1.0.1:
version "1.0.3"
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"
version "1.7.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289"
+axios@0.18.0:
+ version "0.18.0"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102"
+ dependencies:
+ follow-redirects "^1.3.0"
+ is-buffer "^1.1.5"
+
babel-code-frame@6.26.0, babel-code-frame@^6.11.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
node-int64 "^0.4.0"
buffer-from@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531"
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04"
buffer-indexof@^1.0.0:
version "1.1.1"
lodash.uniq "^4.5.0"
caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
- version "1.0.30000844"
- resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000844.tgz#bca5798cda2b6931d68100c2d69e55fb338cbb41"
+ version "1.0.30000848"
+ resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000848.tgz#e149c981c72aa20439e3bc12c7cf8b3f7e1237c6"
caniuse-lite@^1.0.30000748, caniuse-lite@^1.0.30000792:
- version "1.0.30000844"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000844.tgz#de7c84cde0582143cf4f5abdf1b98e5a0539ad4a"
+ version "1.0.30000848"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000848.tgz#ec9c0a72ec8f9ef812e4f4b8628625af9c85ade0"
capture-exit@^1.2.0:
version "1.2.0"
version "1.2.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
-core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.3:
- version "2.5.6"
- resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.6.tgz#0fe6d45bf3cac3ac364a9d72de7576f4eb221b9d"
+core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.6:
+ version "2.5.7"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
version "0.3.2"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b"
-"cssstyle@>= 0.2.37 < 0.3.0":
- version "0.2.37"
- resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54"
+"cssstyle@>= 0.3.1 < 0.4.0":
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.3.1.tgz#6da9b4cff1bc5d716e6e5fe8e04fcb1b50a49adf"
dependencies:
cssom "0.3.x"
-csstype@^2.0.0, csstype@^2.2.0:
- version "2.5.2"
- resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.5.2.tgz#4534308476ceede8fbe148b9b99f9baf1c80fa06"
+csstype@^2.0.0, csstype@^2.2.0, csstype@^2.5.2:
+ version "2.5.3"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.5.3.tgz#2504152e6e1cc59b32098b7f5d6a63f16294c1f7"
currently-unhandled@^0.4.1:
version "0.4.1"
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
+debounce@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.1.0.tgz#6a1a4ee2a9dc4b7c24bb012558dbcdb05b37f408"
+
debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
version "1.0.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
-deep-extend@^0.5.1:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f"
+deep-extend@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
deepmerge@^2.0.1:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.0.tgz#511a54fff405fc346f0240bb270a3e9533a31102"
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.1.tgz#e862b4e45ea0555072bf51e7fd0d9845170ae768"
default-require-extensions@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30:
- version "1.3.47"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.47.tgz#764e887ca9104d01a0ac8eabee7dfc0e2ce14104"
+ version "1.3.48"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.48.tgz#d3b0d8593814044e092ece2108fc3ac9aea4b900"
elliptic@^6.0.0:
version "6.4.0"
is-arrayish "^0.2.1"
es-abstract@^1.5.1, es-abstract@^1.7.0:
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.11.0.tgz#cce87d518f0496893b1a30cd8461835535480681"
- dependencies:
- es-to-primitive "^1.1.1"
- function-bind "^1.1.1"
- has "^1.0.1"
- is-callable "^1.1.3"
- is-regex "^1.0.4"
-
-es-abstract@^1.6.1:
version "1.12.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165"
dependencies:
is-symbol "^1.0.1"
es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14:
- version "0.10.42"
- resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.42.tgz#8c07dd33af04d5dcd1310b5cef13bea63a89ba8d"
+ version "0.10.45"
+ resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.45.tgz#0bfdf7b473da5919d5adf3bd25ceb754fccc3653"
dependencies:
es6-iterator "~2.0.3"
es6-symbol "~3.1.1"
inherits "^2.0.1"
readable-stream "^2.0.4"
-follow-redirects@^1.0.0:
+follow-redirects@^1.0.0, follow-redirects@^1.3.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.0.tgz#234f49cf770b7f35b40e790f636ceba0c3a0ab77"
dependencies:
nan "^2.9.2"
node-pre-gyp "^0.10.0"
-function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1:
+function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
kind-of "^4.0.0"
has@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
dependencies:
- function-bind "^1.0.2"
+ function-bind "^1.1.1"
hash-base@^3.0.0:
version "3.0.4"
statuses ">= 1.4.0 < 2"
http-parser-js@>=0.4.0:
- version "0.4.12"
- resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.12.tgz#b9cfbf4a2cf26f0fc34b10ca1489a27771e3474f"
+ version "0.4.13"
+ resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.13.tgz#3bd6d6fde6e3172c9334c3b33b6c193d80fe1137"
http-proxy-middleware@~0.17.4:
version "0.17.4"
source-map "^0.5.3"
istanbul-lib-source-maps@^1.2.4:
- version "1.2.4"
- resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.4.tgz#cc7ccad61629f4efff8e2f78adb8c522c9976ec7"
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.5.tgz#ffe6be4e7ab86d3603e4290d54990b14506fc9b1"
dependencies:
debug "^3.1.0"
istanbul-lib-coverage "^1.2.0"
dependencies:
pretty-format "^22.4.3"
+jest-localstorage-mock@2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/jest-localstorage-mock/-/jest-localstorage-mock-2.2.0.tgz#ce9a9de01dfdde2ad8aa08adf73acc7e5cc394cf"
+
jest-matcher-utils@^22.4.0, jest-matcher-utils@^22.4.3:
version "22.4.3"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz#4632fe428ebc73ebc194d3c7b65d37b161f710ff"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
js-yaml@^3.4.3, js-yaml@^3.7.0:
- version "3.11.0"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef"
+ version "3.12.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
jsdom@^11.5.1:
- version "11.10.0"
- resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.10.0.tgz#a42cd54e88895dc765f03f15b807a474962ac3b5"
+ version "11.11.0"
+ resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.11.0.tgz#df486efad41aee96c59ad7a190e2449c7eb1110e"
dependencies:
abab "^1.0.4"
acorn "^5.3.0"
acorn-globals "^4.1.0"
array-equal "^1.0.0"
cssom ">= 0.3.2 < 0.4.0"
- cssstyle ">= 0.2.37 < 0.3.0"
+ cssstyle ">= 0.3.1 < 0.4.0"
data-urls "^1.0.0"
domexception "^1.0.0"
escodegen "^1.9.0"
html-encoding-sniffer "^1.0.2"
left-pad "^1.2.0"
- nwmatcher "^1.4.3"
+ nwsapi "^2.0.0"
parse5 "4.0.0"
pn "^1.1.0"
request "^2.83.0"
webidl-conversions "^4.0.2"
whatwg-encoding "^1.0.3"
whatwg-mimetype "^2.1.0"
- whatwg-url "^6.4.0"
+ whatwg-url "^6.4.1"
ws "^4.0.0"
xml-name-validator "^3.0.0"
css-vendor "^0.3.8"
jss@^9.3.3, jss@^9.7.0:
- version "9.8.1"
- resolved "https://registry.yarnpkg.com/jss/-/jss-9.8.1.tgz#e2ff250777ad657430e6edc47a63516541b888fa"
+ version "9.8.2"
+ resolved "https://registry.yarnpkg.com/jss/-/jss-9.8.2.tgz#09cabdfba831545bf094e399cfa45a1743daf4a6"
dependencies:
is-in-browser "^1.1.3"
symbol-observable "^1.1.0"
pseudomap "^1.0.2"
yallist "^2.1.2"
-macaddress@^0.2.8:
- version "0.2.8"
- resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
-
make-dir@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
bn.js "^4.0.0"
brorand "^1.0.1"
-"mime-db@>= 1.33.0 < 2", mime-db@~1.33.0:
+"mime-db@>= 1.33.0 < 2":
+ version "1.34.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.34.0.tgz#452d0ecff5c30346a6dc1e64b1eaee0d3719ff9a"
+
+mime-db@~1.33.0:
version "1.33.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
version "0.0.10"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
-minipass@^2.2.1, minipass@^2.2.4:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.1.tgz#4e872b959131a672837ab3cb554962bc84b1537d"
+minipass@^2.2.1, minipass@^2.3.3:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233"
dependencies:
- safe-buffer "^5.1.1"
+ safe-buffer "^5.1.2"
yallist "^3.0.0"
minizlib@^1.1.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
-nwmatcher@^1.4.3:
- version "1.4.4"
- resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.4.tgz#2285631f34a95f0d0395cd900c96ed39b58f346e"
+nwsapi@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.0.1.tgz#a50d59a2dcb14b6931401171713ced2d0eb3468f"
oauth-sign@~0.8.2:
version "0.8.2"
uniqs "^2.0.0"
postcss-filter-plugins@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c"
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz#82245fdf82337041645e477114d8e593aa18b8ec"
dependencies:
postcss "^5.0.4"
- uniqid "^4.0.0"
postcss-flexbugs-fixes@3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
punycode@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d"
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
q@^1.1.2:
version "1.5.1"
unpipe "1.0.0"
rc@^1.0.1, rc@^1.1.6, rc@^1.1.7:
- version "1.2.7"
- resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.7.tgz#8a10ca30d588d00464360372b890d06dacd02297"
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
dependencies:
- deep-extend "^0.5.1"
+ deep-extend "^0.6.0"
ini "~1.3.0"
minimist "^1.2.0"
strip-json-comments "~2.0.1"
strip-ansi "3.0.1"
text-table "0.2.0"
-react-dom@16.3.2:
- version "16.3.2"
- resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.2.tgz#cb90f107e09536d683d84ed5d4888e9640e0e4df"
+react-dom@16.4.0:
+ version "16.4.0"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.0.tgz#099f067dd5827ce36a29eaf9a6cdc7cbf6216b1e"
dependencies:
fbjs "^0.8.16"
loose-envify "^1.1.0"
version "4.0.0"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
-react-event-listener@^0.5.1:
- version "0.5.6"
- resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.5.6.tgz#f9349fda4b7735fc6886ca403bdcfd6057e89ceb"
+react-event-listener@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.0.tgz#f8cf2821f5ca1844e0df1dac1c7b9a3ecb686fd7"
dependencies:
"@babel/runtime" "^7.0.0-beta.42"
- fbjs "^0.8.16"
prop-types "^15.6.0"
warning "^3.0.0"
loose-envify "^1.3.1"
prop-types "^15.6.1"
-react@16.3.2:
- version "16.3.2"
- resolved "https://registry.yarnpkg.com/react/-/react-16.3.2.tgz#fdc8420398533a1e58872f59091b272ce2f91ea9"
+react@16.4.0:
+ version "16.4.0"
+ resolved "https://registry.yarnpkg.com/react/-/react-16.4.0.tgz#402c2db83335336fba1962c08b98c6272617d585"
dependencies:
fbjs "^0.8.16"
loose-envify "^1.1.0"
prop-types "^15.5.7"
redux-devtools-instrument "^1.0.1"
-redux@4.0.0, redux@^4.0.0:
+redux-thunk@2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
+
+redux@4.0.0, "redux@>= 3.7.2", redux@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.0.tgz#aa698a92b729315d22b34a0553d7e6533555cc03"
dependencies:
loose-envify "^1.1.0"
symbol-observable "^1.2.0"
-redux@^3.6.0, redux@^3.7.2:
+redux@^3.6.0:
version "3.7.2"
resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b"
dependencies:
version "1.1.1"
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
-stifle@^1.0.2:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/stifle/-/stifle-1.0.4.tgz#8b3bcdf52419b0a9c79e35adadce50123c1d8e99"
-
stream-browserify@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
stream-shift "^1.0.0"
stream-http@^2.7.2:
- version "2.8.2"
- resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.2.tgz#4126e8c6b107004465918aa2fc35549e77402c87"
+ version "2.8.3"
+ resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc"
dependencies:
builtin-status-codes "^3.0.0"
inherits "^2.0.1"
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:
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22"
tar@^4:
- version "4.4.2"
- resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.2.tgz#60685211ba46b38847b1ae7ee1a24d744a2cd462"
+ version "4.4.4"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd"
dependencies:
chownr "^1.0.1"
fs-minipass "^1.2.5"
- minipass "^2.2.4"
+ minipass "^2.3.3"
minizlib "^1.1.0"
mkdirp "^0.5.0"
safe-buffer "^5.1.2"
strip-json-comments "^2.0.1"
tslib@^1.8.0, tslib@^1.8.1:
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.1.tgz#a5d1f0532a49221c87755cfcc89ca37197242ba7"
+ version "1.9.2"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.2.tgz#8be0cc9a1f6dc7727c38deb16c2ebd1a2892988e"
tslint-config-prettier@^1.10.0:
version "1.13.0"
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
-typesafe-actions@2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/typesafe-actions/-/typesafe-actions-2.0.3.tgz#5451a4f8bf0d1e79f7d12af7582cc5f12d490708"
-
-typescript@2.8.3:
- version "2.8.3"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.3.tgz#5d817f9b6f31bb871835f4edf0089f21abe6c170"
+typescript@2.9.1:
+ version "2.9.1"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.1.tgz#fdb19d2c67a15d11995fd15640e373e09ab09961"
ua-parser-js@^0.7.9:
version "0.7.18"
commander "~2.13.0"
source-map "~0.6.1"
-uglify-js@3.3.x, uglify-js@^3.0.13:
- version "3.3.26"
- resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.3.26.tgz#858b74e5e7262e876c834b907a5fa57d4fa0d525"
+uglify-js@3.3.x:
+ version "3.3.28"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.3.28.tgz#0efb9a13850e11303361c1051f64d2ec68d9be06"
dependencies:
commander "~2.15.0"
source-map "~0.6.1"
optionalDependencies:
uglify-to-browserify "~1.0.0"
+uglify-js@^3.0.13:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.0.tgz#796762282b5b5f0eafe7d5c8c708d1d7bd5ba11d"
+ dependencies:
+ commander "~2.15.0"
+ source-map "~0.6.1"
+
uglify-to-browserify@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
is-extendable "^0.1.1"
set-value "^0.4.3"
+unionize@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/unionize/-/unionize-2.1.2.tgz#2513b148de515bec93f045d1685bd88eab62b608"
+
uniq@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
-uniqid@^4.0.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1"
- dependencies:
- macaddress "^0.2.8"
-
uniqs@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
uri-js@^4.2.1:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.1.tgz#4595a80a51f356164e22970df64c7abd6ade9850"
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
dependencies:
punycode "^2.1.0"
define-properties "^1.1.2"
object.getownpropertydescriptors "^2.0.3"
-util@0.10.3, util@^0.10.3:
+util@0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
dependencies:
inherits "2.0.1"
+util@^0.10.3:
+ version "0.10.4"
+ resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
+ dependencies:
+ inherits "2.0.3"
+
utila@~0.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/utila/-/utila-0.3.3.tgz#d7e8e7d7e309107092b05f8d9688824d633a4226"
dependencies:
loose-envify "^1.0.0"
+warning@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.1.tgz#66ce376b7fbfe8a887c22bdf0e7349d73d397745"
+ dependencies:
+ loose-envify "^1.0.0"
+
watch@~0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986"
version "2.1.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz#f0f21d76cbba72362eb609dbed2a30cd17fcc7d4"
-whatwg-url@^6.4.0:
+whatwg-url@^6.4.0, whatwg-url@^6.4.1:
version "6.4.1"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.4.1.tgz#fdb94b440fd4ad836202c16e9737d511f012fd67"
dependencies:
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
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"