From 10d433cfab5c394a9c8051a8cf081eac5f3b194b Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Thu, 26 Mar 2020 21:23:50 -0300 Subject: [PATCH] Port WebSocket from mm-redux and batch actions (#4060) * Port WebSocket from mm-redux and batch actions * Update mm-redux and fix tests * Change action name * Naming batch actions * Fix unit tests * Dispatch connection change only if its different * Remove comment * Add Lint to TypeScript and fix linting errors * Add WebSocket Unit Tests * Revert from unwanted RN 0.62 --- .eslintrc.json | 19 +- app/actions/device/index.js | 17 +- app/actions/helpers/channels.ts | 355 ++ app/actions/views/channel.js | 371 +- app/actions/views/channel.test.js | 4 +- app/actions/views/emoji.js | 2 +- app/actions/views/post.js | 154 +- app/actions/views/root.js | 2 +- app/actions/views/select_server.js | 2 +- app/actions/views/select_server.test.js | 2 +- app/actions/views/select_team.js | 2 +- app/actions/views/thread.js | 14 +- app/actions/views/thread.test.js | 8 +- app/actions/views/typing.js | 7 +- app/actions/views/user.js | 87 +- app/actions/websocket.test.js | 1096 +++++ app/actions/websocket.ts | 1155 +++++ app/client/websocket.ts | 247 ++ .../channel_loader/channel_loader.test.js | 2 +- app/components/formatted_markdown_text.js | 2 +- app/components/network_indicator/index.js | 2 +- .../network_indicator/network_indicator.js | 3 +- app/components/sidebars/drawer_layout.js | 91 +- .../channel_item/channel_item.test.js | 2 +- .../sidebars/main/main_sidebar_base.js | 4 +- .../sidebars/main/teams_list/teams_list.js | 4 +- app/constants/index.js | 2 + app/constants/websocket.ts | 45 + app/init/fetch.test.js | 4 + app/init/global_event_handler.js | 8 +- app/initial_state.js | 5 + app/mattermost.js | 4 +- .../error_teams_list/error_teams_list.test.js | 8 +- .../flagged_posts/flagged_posts.test.js | 2 +- app/screens/pinned_posts/pinned_posts.test.js | 2 +- .../recent_mentions/recent_mentions.test.js | 2 +- app/screens/user_profile/user_profile.js | 4 +- app/store/middleware.js | 9 + app/utils/channels.js | 11 - app/utils/error_handling.js | 2 +- app/utils/latinise.js | 2 +- app/utils/url.js | 2 +- app/utils/users.js | 4 +- babel.config.js | 6 + jest.config.js | 33 + package-lock.json | 3755 ++++++++++------- package.json | 41 +- packager/moduleNames.js | 2 - packager/modulePaths.js | 2 - scripts/make-dist-assets.js | 26 +- share_extension/android/actions/index.js | 5 +- .../extension_post/extension_post.test.js | 2 +- test/setup.js | 8 +- test/test_helper.js | 319 +- test/test_store.js | 55 + tsconfig.json | 50 + tsconfig.test.json | 7 + 57 files changed, 6016 insertions(+), 2065 deletions(-) create mode 100644 app/actions/helpers/channels.ts create mode 100644 app/actions/websocket.test.js create mode 100644 app/actions/websocket.ts create mode 100644 app/client/websocket.ts create mode 100644 app/constants/websocket.ts create mode 100644 jest.config.js create mode 100644 test/test_store.js create mode 100644 tsconfig.json create mode 100644 tsconfig.test.json diff --git a/.eslintrc.json b/.eslintrc.json index 65b5c1797..fafc6c6b1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,9 +1,13 @@ { "extends": [ - "plugin:mattermost/react" + "plugin:mattermost/react", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" ], + "parser": "@typescript-eslint/parser", "plugins": [ - "mattermost" + "mattermost", + "@typescript-eslint" ], "settings": { "react": { @@ -20,7 +24,16 @@ "rules": { "global-require": 0, "react/display-name": [2, { "ignoreTranspilerName": false }], - "react/jsx-filename-extension": [2, {"extensions": [".js"]}] + "react/jsx-filename-extension": [2, {"extensions": [".js"]}], + "no-undefined": 0, + "@typescript-eslint/camelcase": 0, + "@typescript-eslint/no-undefined": 0, + "@typescript-eslint/no-non-null-assertion": 0, + "@typescript-eslint/no-unused-vars": 0, + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-use-before-define": 0, + "@typescript-eslint/no-var-requires": 0, + "@typescript-eslint/explicit-function-return-type": 0 }, "overrides": [ { diff --git a/app/actions/device/index.js b/app/actions/device/index.js index fe4db6433..87778046e 100644 --- a/app/actions/device/index.js +++ b/app/actions/device/index.js @@ -2,16 +2,21 @@ // See LICENSE.txt for license information. import {networkStatusChangedAction} from 'redux-offline'; +import {batchActions} from 'redux-batched-actions'; import {DeviceTypes} from 'app/constants'; export function connection(isOnline) { - return async (dispatch) => { - dispatch(networkStatusChangedAction(isOnline)); - dispatch({ - type: DeviceTypes.CONNECTION_CHANGED, - data: isOnline, - }); + return async (dispatch, getState) => { + const state = getState(); + if (isOnline !== undefined && isOnline !== state.device.connection) { //eslint-disable-line no-undefined + dispatch(batchActions([ + networkStatusChangedAction(isOnline), { + type: DeviceTypes.CONNECTION_CHANGED, + data: isOnline, + }, + ], 'BATCH_CONNECTION_CHANGED')); + } }; } diff --git a/app/actions/helpers/channels.ts b/app/actions/helpers/channels.ts new file mode 100644 index 000000000..3e05b574b --- /dev/null +++ b/app/actions/helpers/channels.ts @@ -0,0 +1,355 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {ChannelTypes, PreferenceTypes, RoleTypes, UserTypes} from 'mattermost-redux/action_types'; +import {Client4} from 'mattermost-redux/client'; +import {General, Preferences} from 'mattermost-redux/constants'; +import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'; +import {getConfig} from 'mattermost-redux/selectors/entities/general'; +import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences'; +import {getCurrentUserId, getUsers, getUserIdsInChannels} from 'mattermost-redux/selectors/entities/users'; +import {getUserIdFromChannelName, isAutoClosed} from 'mattermost-redux/utils/channel_utils'; +import {getPreferenceKey} from 'mattermost-redux/utils/preference_utils'; + +import {ActionResult, GenericAction} from 'mattermost-redux/types/actions'; +import {Channel, ChannelMembership} from 'mattermost-redux/types/channels'; +import {PreferenceType} from 'mattermost-redux/types/preferences'; +import {GlobalState} from 'mattermost-redux/types/store'; +import {UserProfile} from 'mattermost-redux/types/users'; +import {RelationOneToMany} from 'mattermost-redux/types/utilities'; + +import {isDirectChannelVisible, isGroupChannelVisible} from '@utils/channels'; +import {buildPreference} from '@utils/preferences'; + +export async function loadSidebarDirectMessagesProfiles(state: GlobalState, channels: Array, channelMembers: Array) { + const currentUserId = getCurrentUserId(state); + const usersInChannel: RelationOneToMany = getUserIdsInChannels(state); + const directChannels = Object.values(channels).filter((c) => c.type === General.DM_CHANNEL || c.type === General.GM_CHANNEL); + const prefs: Array = []; + const promises: Array> = []; //only fetch profiles that we don't have and the Direct channel should be visible + const actions = []; + + // Prepare preferences and start fetching profiles to batch them + directChannels.forEach((c) => { + const profileIds = Array.from(usersInChannel[c.id] || []); + const profilesInChannel: Array = profileIds.filter((u: string) => u !== currentUserId); + switch (c.type) { + case General.DM_CHANNEL: { + const dm = fetchDirectMessageProfileIfNeeded(state, c, channelMembers, profilesInChannel); + if (dm.preferences.length) { + prefs.push(...dm.preferences); + } + + if (dm.promise) { + promises.push(dm.promise); + } + break; + } + case General.GM_CHANNEL: { + const gm = fetchGroupMessageProfilesIfNeeded(state, c, channelMembers, profilesInChannel); + + if (gm.preferences.length) { + prefs.push(...gm.preferences); + } + + if (gm.promise) { + promises.push(gm.promise); + } + break; + } + } + }); + + // Save preferences if there are any changes + if (prefs.length) { + Client4.savePreferences(currentUserId, prefs); + actions.push({ + type: PreferenceTypes.RECEIVED_PREFERENCES, + data: prefs, + }); + } + + const profilesAction = await getProfilesFromPromises(promises); + if (profilesAction) { + actions.push(profilesAction); + } + + return actions; +} + +export async function fetchMyChannel(channelId: string) { + try { + const data = await Client4.getChannel(channelId); + + return {data}; + } catch (error) { + return {error}; + } +} + +export async function fetchMyChannelMember(channelId: string) { + try { + const data = await Client4.getMyChannelMember(channelId); + + return {data}; + } catch (error) { + return {error}; + } +} + +export function markChannelAsUnread(state: GlobalState, teamId: string, channelId: string, mentions: Array): Array { + const {myMembers} = state.entities.channels; + const {currentUserId} = state.entities.users; + + const actions: GenericAction[] = [{ + type: ChannelTypes.INCREMENT_TOTAL_MSG_COUNT, + data: { + channelId, + amount: 1, + }, + }, { + type: ChannelTypes.INCREMENT_UNREAD_MSG_COUNT, + data: { + teamId, + channelId, + amount: 1, + onlyMentions: myMembers[channelId] && myMembers[channelId].notify_props && + myMembers[channelId].notify_props.mark_unread === General.MENTION, + }, + }]; + + if (mentions && mentions.indexOf(currentUserId) !== -1) { + actions.push({ + type: ChannelTypes.INCREMENT_UNREAD_MENTION_COUNT, + data: { + teamId, + channelId, + amount: 1, + }, + }); + } + + return actions; +} + +export function makeDirectChannelVisibleIfNecessary(state: GlobalState, otherUserId: string): GenericAction|null { + const myPreferences = getMyPreferences(state); + const currentUserId = getCurrentUserId(state); + + let preference = myPreferences[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUserId)]; + + if (!preference || preference.value === 'false') { + preference = { + user_id: currentUserId, + category: Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, + name: otherUserId, + value: 'true', + }; + + return { + type: PreferenceTypes.RECEIVED_PREFERENCES, + data: [preference], + }; + } + + return null; +} + +export async function makeGroupMessageVisibleIfNecessary(state: GlobalState, channelId: string) { + try { + const myPreferences = getMyPreferences(state); + const currentUserId = getCurrentUserId(state); + + let preference = myPreferences[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, channelId)]; + + if (!preference || preference.value === 'false') { + preference = { + user_id: currentUserId, + category: Preferences.CATEGORY_GROUP_CHANNEL_SHOW, + name: channelId, + value: 'true', + }; + + const profilesInChannel = await fetchUsersInChannel(state, channelId); + + return [{ + type: UserTypes.RECEIVED_BATCHED_PROFILES_IN_CHANNEL, + data: [profilesInChannel], + }, { + type: PreferenceTypes.RECEIVED_PREFERENCES, + data: [preference], + }]; + } + + return null; + } catch { + return null; + } +} + +export async function fetchChannelAndMyMember(channelId: string): Promise> { + const actions: Array = []; + + try { + const [channel, member] = await Promise.all([ + Client4.getChannel(channelId), + Client4.getMyChannelMember(channelId), + ]); + + actions.push({ + type: ChannelTypes.RECEIVED_CHANNEL, + data: channel, + }, + { + type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER, + data: member, + }); + + const roles = await Client4.getRolesByNames(member.roles.split(' ')); + if (roles.length) { + actions.push({ + type: RoleTypes.RECEIVED_ROLES, + data: roles, + }); + } + } catch { + // do nothing + } + + return actions; +} + +export async function getAddedDmUsersIfNecessary(state: GlobalState, preferences: PreferenceType[]): Promise> { + const userIds: string[] = []; + const actions: Array = []; + + for (const preference of preferences) { + if (preference.category === Preferences.CATEGORY_DIRECT_CHANNEL_SHOW && preference.value === 'true') { + userIds.push(preference.name); + } + } + + if (userIds.length !== 0) { + const profiles = getUsers(state); + const currentUserId = getCurrentUserId(state); + + const needProfiles: string[] = []; + + for (const userId of userIds) { + if (!profiles[userId] && userId !== currentUserId) { + needProfiles.push(userId); + } + } + + if (needProfiles.length > 0) { + const data = await Client4.getProfilesByIds(userIds); + if (profiles.lenght) { + actions.push({ + type: UserTypes.RECEIVED_PROFILES_LIST, + data, + }); + } + } + } + + return actions; +} + +function fetchDirectMessageProfileIfNeeded(state: GlobalState, channel: Channel, channelMembers: Array, profilesInChannel: Array) { + const currentUserId = getCurrentUserId(state); + const myPreferences = getMyPreferences(state); + const users = getUsers(state); + const config = getConfig(state); + const currentChannelId = getCurrentChannelId(state); + const otherUserId = getUserIdFromChannelName(currentUserId, channel.name); + const otherUser = users[otherUserId]; + const dmVisible = isDirectChannelVisible(currentUserId, myPreferences, channel); + const dmAutoClosed = isAutoClosed(config, myPreferences, channel, channel.last_post_at, otherUser ? otherUser.delete_at : 0, currentChannelId); + const member = channelMembers.find((cm) => cm.channel_id === channel.id); + const dmIsUnread = member ? member.mention_count > 0 : false; + const dmFetchProfile = dmIsUnread || (dmVisible && !dmAutoClosed); + const preferences = []; + + // when then DM is hidden but has new messages + if ((!dmVisible || dmAutoClosed) && dmIsUnread) { + preferences.push(buildPreference(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, currentUserId, otherUserId)); + preferences.push(buildPreference(Preferences.CATEGORY_CHANNEL_OPEN_TIME, currentUserId, channel.id, Date.now().toString())); + } + + if (dmFetchProfile && !profilesInChannel.includes(otherUserId) && otherUserId !== currentUserId) { + return { + preferences, + promise: fetchUsersInChannel(state, channel.id), + }; + } + + return {preferences}; +} + +function fetchGroupMessageProfilesIfNeeded(state: GlobalState, channel: Channel, channelMembers: Array, profilesInChannel: Array) { + const currentUserId = getCurrentUserId(state); + const myPreferences = getMyPreferences(state); + const config = getConfig(state); + const gmVisible = isGroupChannelVisible(myPreferences, channel.id); + const gmAutoClosed = isAutoClosed(config, myPreferences, channel, channel.last_post_at, 0); + const channelMember = channelMembers.find((cm) => cm.channel_id === channel.id); + let hasMentions = false; + let isUnread = false; + + if (channelMember) { + hasMentions = channelMember.mention_count > 0; + isUnread = channelMember.msg_count < channel.total_msg_count; + } + + const gmIsUnread = hasMentions || isUnread; + const gmFetchProfile = gmIsUnread || (gmVisible && !gmAutoClosed); + const preferences = []; + + // when then GM is hidden but has new messages + if ((!gmVisible || gmAutoClosed) && gmIsUnread) { + preferences.push(buildPreference(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, currentUserId, channel.id)); + preferences.push(buildPreference(Preferences.CATEGORY_CHANNEL_OPEN_TIME, currentUserId, channel.id, Date.now().toString())); + } + + if (gmFetchProfile && !profilesInChannel.length) { + return { + preferences, + promise: fetchUsersInChannel(state, channel.id), + }; + } + + return {preferences}; +} + +async function fetchUsersInChannel(state: GlobalState, channelId: string): Promise { + try { + const currentUserId = getCurrentUserId(state); + const profiles = await Client4.getProfilesInChannel(channelId); + + // When fetching profiles in channels we exclude our own user + const users = profiles.filter((p: UserProfile) => p.id !== currentUserId); + const data = { + channelId, + users, + }; + + return {data}; + } catch (error) { + return {error}; + } +} + +async function getProfilesFromPromises(promises: Array>): Promise { + // Get the profiles returned by the promises + if (!promises.length) { + return null; + } + + const result = await Promise.all(promises); + const data = result.filter((p: any) => !p.error); + + return { + type: UserTypes.RECEIVED_BATCHED_PROFILES_IN_CHANNEL, + data, + }; +} \ No newline at end of file diff --git a/app/actions/views/channel.js b/app/actions/views/channel.js index ba1a9980d..c01f55290 100644 --- a/app/actions/views/channel.js +++ b/app/actions/views/channel.js @@ -5,17 +5,14 @@ import {batchActions} from 'redux-batched-actions'; import {ViewTypes} from 'app/constants'; -import {UserTypes, ChannelTypes} from 'mattermost-redux/action_types'; +import {ChannelTypes, RoleTypes, UserTypes} from 'mattermost-redux/action_types'; import { fetchMyChannelsAndMembers, getChannelByNameAndTeamName, - markChannelAsRead, - markChannelAsViewed, leaveChannel as serviceLeaveChannel, } from 'mattermost-redux/actions/channels'; import {getFilesForPost} from 'mattermost-redux/actions/files'; import {savePreferences} from 'mattermost-redux/actions/preferences'; -import {loadRolesIfNeeded} from 'mattermost-redux/actions/roles'; import {getTeamMembersByIds, selectTeam} from 'mattermost-redux/actions/teams'; import {getProfilesInChannel} from 'mattermost-redux/actions/users'; import {Client4} from 'mattermost-redux/client'; @@ -27,9 +24,7 @@ import { getChannelsNameMapInTeam, isManuallyUnread, } from 'mattermost-redux/selectors/entities/channels'; -import {getConfig} from 'mattermost-redux/selectors/entities/general'; -import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences'; -import {getCurrentUserId, getUserIdsInChannels, getUsers} from 'mattermost-redux/selectors/entities/users'; +import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; import {getTeamByName} from 'mattermost-redux/selectors/entities/teams'; import { @@ -44,15 +39,13 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter'; import {getLastCreateAt} from 'mattermost-redux/utils/post_utils'; import {getPreferencesByCategory} from 'mattermost-redux/utils/preference_utils'; -import {INSERT_TO_COMMENT, INSERT_TO_DRAFT} from 'app/constants/post_textbox'; -import {getChannelReachable} from 'app/selectors/channel'; -import telemetry from 'app/telemetry'; -import {isDirectChannelVisible, isGroupChannelVisible, isDirectMessageVisible, isGroupMessageVisible, isDirectChannelAutoClosed} from 'app/utils/channels'; -import {isPendingPost} from 'app/utils/general'; -import {buildPreference} from 'app/utils/preferences'; - -import {getPosts, getPostsBefore, getPostsSince, getPostThread} from './post'; -import {forceLogoutIfNecessary} from './user'; +import {loadSidebarDirectMessagesProfiles} from '@actions/helpers/channels'; +import {getPosts, getPostsBefore, getPostsSince, getPostThread} from '@actions/views/post'; +import {INSERT_TO_COMMENT, INSERT_TO_DRAFT} from '@constants/post_textbox'; +import {getChannelReachable} from '@selectors/channel'; +import telemetry from '@telemetry'; +import {isDirectChannelVisible, isGroupChannelVisible} from '@utils/channels'; +import {isPendingPost} from '@utils/general'; const MAX_RETRIES = 3; @@ -72,106 +65,6 @@ export function loadChannelsByTeamName(teamName, errorHandler) { }; } -export function loadProfilesAndTeamMembersForDMSidebar(teamId) { - return async (dispatch, getState) => { - const state = getState(); - const {currentUserId, profilesInChannel} = state.entities.users; - const {channels, myMembers} = state.entities.channels; - const {myPreferences} = state.entities.preferences; - const {membersInTeam} = state.entities.teams; - const dmPrefs = getPreferencesByCategory(myPreferences, Preferences.CATEGORY_DIRECT_CHANNEL_SHOW); - const gmPrefs = getPreferencesByCategory(myPreferences, Preferences.CATEGORY_GROUP_CHANNEL_SHOW); - const members = []; - const loadProfilesForChannels = []; - const prefs = []; - - function buildPref(name) { - return { - user_id: currentUserId, - category: Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, - name, - value: 'true', - }; - } - - // Find DM's and GM's that need to be shown - const directChannels = Object.values(channels).filter((c) => (isDirectChannel(c) || isGroupChannel(c))); - directChannels.forEach((channel) => { - const member = myMembers[channel.id]; - if (isDirectChannel(channel) && !isDirectChannelVisible(currentUserId, myPreferences, channel) && member && member.mention_count > 0) { - const teammateId = getUserIdFromChannelName(currentUserId, channel.name); - let pref = dmPrefs.get(teammateId); - if (pref) { - pref = {...pref, value: 'true'}; - } else { - pref = buildPref(teammateId); - } - dmPrefs.set(teammateId, pref); - prefs.push(pref); - } else if (isGroupChannel(channel) && !isGroupChannelVisible(myPreferences, channel) && member && (member.mention_count > 0 || member.msg_count < channel.total_msg_count)) { - const id = channel.id; - let pref = gmPrefs.get(id); - if (pref) { - pref = {...pref, value: 'true'}; - } else { - pref = buildPref(id); - } - gmPrefs.set(id, pref); - prefs.push(pref); - } - }); - - if (prefs.length) { - savePreferences(currentUserId, prefs)(dispatch, getState); - } - - for (const [key, pref] of dmPrefs) { - if (pref.value === 'true') { - members.push(key); - } - } - - for (const [key, pref] of gmPrefs) { - //only load the profiles in channels if we don't already have them - if (pref.value === 'true' && !profilesInChannel[key]) { - loadProfilesForChannels.push(key); - } - } - - if (loadProfilesForChannels.length) { - for (let i = 0; i < loadProfilesForChannels.length; i++) { - const channelId = loadProfilesForChannels[i]; - getProfilesInChannel(channelId, 0)(dispatch, getState); - } - } - - let membersToLoad = members; - if (membersInTeam[teamId]) { - membersToLoad = members.filter((m) => !membersInTeam[teamId].hasOwnProperty(m)); - } - - if (membersToLoad.length) { - getTeamMembersByIds(teamId, membersToLoad)(dispatch, getState); - } - - const actions = []; - for (let i = 0; i < members.length; i++) { - const channelName = getDirectChannelName(currentUserId, members[i]); - const channel = getChannelByName(channels, channelName); - if (channel) { - actions.push({ - type: UserTypes.RECEIVED_PROFILE_IN_CHANNEL, - data: {id: channel.id, user_id: members[i]}, - }); - } - } - - if (actions.length) { - dispatch(batchActions(actions)); - } - }; -} - export function loadPostsIfNecessaryWithRetry(channelId) { return async (dispatch, getState) => { const state = getState(); @@ -211,7 +104,8 @@ export function loadPostsIfNecessaryWithRetry(channelId) { type: ViewTypes.RECEIVED_POSTS_FOR_CHANNEL_AT_TIME, channelId, time, - }); + }, + setChannelRetryFailed(false)); if (received?.order) { const count = received.order.length; @@ -220,7 +114,7 @@ export function loadPostsIfNecessaryWithRetry(channelId) { } actions.push(setLoadMorePostsVisible(loadMorePostsVisible)); - dispatch(batchActions(actions)); + dispatch(batchActions(actions, 'BATCH_LOAD_POSTS_IN_CHANNEL')); }; } @@ -229,7 +123,6 @@ export async function retryGetPostsAction(action, dispatch, getState, maxTries = const {data} = await dispatch(action); // eslint-disable-line no-await-in-loop if (data) { - dispatch(setChannelRetryFailed(false)); return data; } } @@ -365,7 +258,7 @@ export function handleSelectChannel(channelId) { teamId: channel.team_id || currentTeamId, }, }); - dispatch(batchActions(actions)); + dispatch(batchActions(actions, 'BATCH_SWITCH_CHANNEL')); } console.log('channel switch to', channel?.display_name, channelId, (Date.now() - dt), 'ms'); //eslint-disable-line @@ -406,10 +299,16 @@ export function handleSelectChannelByName(channelName, teamName, errorHandler) { } export function handlePostDraftChanged(channelId, draft) { - return { - type: ViewTypes.POST_DRAFT_CHANGED, - channelId, - draft, + return (dispatch, getState) => { + const state = getState(); + + if (state.views.channel.drafts[channelId]?.draft !== draft) { + dispatch({ + type: ViewTypes.POST_DRAFT_CHANGED, + channelId, + draft, + }); + } }; } @@ -425,13 +324,15 @@ export function insertToDraft(value) { } export function markChannelViewedAndRead(channelId, previousChannelId, markOnServer = true) { - return (dispatch) => { - dispatch(markChannelAsRead(channelId, previousChannelId, markOnServer)); - dispatch(markChannelAsViewed(channelId, previousChannelId)); + return (dispatch, getState) => { + const state = getState(); + const actions = markAsViewedAndReadBatch(state, channelId, previousChannelId, markOnServer); + + dispatch(batchActions(actions, 'BATCH_MARK_CHANNEL_VIEWED_AND_READ')); }; } -function markAsViewedAndReadBatch(state, channelId, prevChannelId = '', markOnServer = true) { +export function markAsViewedAndReadBatch(state, channelId, prevChannelId = '', markOnServer = true) { const actions = []; const {channels, myMembers} = state.entities.channels; const channel = channels[channelId]; @@ -512,8 +413,7 @@ export function markChannelViewedAndReadOnReconnect(channelId) { return; } - dispatch(markChannelAsRead(channelId)); - dispatch(markChannelAsViewed(channelId)); + dispatch(markChannelViewedAndRead(channelId)); }; } @@ -582,7 +482,13 @@ export function refreshChannelWithRetry(channelId) { return async (dispatch, getState) => { dispatch(setChannelRefreshing(true)); const posts = await retryGetPostsAction(getPosts(channelId), dispatch, getState); - dispatch(setChannelRefreshing(false)); + const actions = [setChannelRefreshing(false)]; + + if (posts) { + actions.push(setChannelRetryFailed(false)); + } + + dispatch(batchActions(actions, 'BATCH_REEFRESH_CHANNEL')); return posts; }; } @@ -683,6 +589,10 @@ export function increasePostVisibility(channelId, postId) { channelId, }]; + if (result) { + actions.push(setChannelRetryFailed(false)); + } + let hasMorePost = false; if (result?.order) { const count = result.order.length; @@ -691,7 +601,7 @@ export function increasePostVisibility(channelId, postId) { actions.push(setLoadMorePostsVisible(hasMorePost)); } - dispatch(batchActions(actions)); + dispatch(batchActions(actions, 'BATCH_LOAD_MORE_POSTS')); telemetry.end(['posts:loading']); telemetry.save(); @@ -706,13 +616,14 @@ function setLoadMorePostsVisible(visible) { }; } -export function loadChannelsForTeam(teamId) { +export function loadChannelsForTeam(teamId, skipDispatch = false) { return async (dispatch, getState) => { const state = getState(); const currentUserId = getCurrentUserId(state); + const data = {sync: true, teamId}; + const actions = []; if (currentUserId) { - const data = {sync: true, teamId}; for (let i = 0; i <= MAX_RETRIES; i++) { try { console.log('Fetching channels attempt', teamId, (i + 1)); //eslint-disable-line no-console @@ -725,8 +636,7 @@ export function loadChannelsForTeam(teamId) { data.channelMembers = channelMembers; break; } catch (err) { - const result = await dispatch(forceLogoutIfNecessary(err)); //eslint-disable-line no-await-in-loop - if (result || i === MAX_RETRIES) { + if (i === MAX_RETRIES) { const hasChannelsLoaded = state.entities.channels.channelsInTeam[teamId]?.size > 0; return {error: hasChannelsLoaded ? null : err}; } @@ -734,175 +644,50 @@ export function loadChannelsForTeam(teamId) { } if (data.channels) { - const roles = new Set(); - const members = data.channelMembers; - for (const member of members) { - for (const role of member.roles.split(' ')) { - roles.add(role); - } - } - - if (roles.size > 0) { - dispatch(loadRolesIfNeeded(roles)); - } - - // Fetch needed profiles from channel creators and direct channels - dispatch(loadSidebarDirectMessagesProfiles(data)); - - dispatch({ + actions.push({ type: ChannelTypes.RECEIVED_MY_CHANNELS_WITH_MEMBERS, data, }); - } - return {data}; + if (!skipDispatch) { + const rolesToLoad = new Set(); + const members = data.channelMembers; + for (const member of members) { + for (const role of member.roles.split(' ')) { + rolesToLoad.add(role); + } + } + + if (rolesToLoad.size > 0) { + data.roles = await Client4.getRolesByNames(Array.from(rolesToLoad)); + if (data.roles.length) { + actions.push({ + type: RoleTypes.RECEIVED_ROLES, + data: data.roles, + }); + } + } + + dispatch(batchActions(actions, 'BATCH_LOAD_CHANNELS_FOR_TEAM')); + } + + // Fetch needed profiles from channel creators and direct channels + dispatch(loadSidebar(data)); + } } - return {error: 'Cannot fetch channels without a current user'}; + return {data}; }; } -export function loadSidebarDirectMessagesProfiles(data) { +function loadSidebar(data) { return async (dispatch, getState) => { const state = getState(); const {channels, channelMembers} = data; - const currentUserId = getCurrentUserId(state); - const usersInChannel = getUserIdsInChannels(state); - const directChannels = Object.values(channels).filter((c) => c.type === General.DM_CHANNEL || c.type === General.GM_CHANNEL); - const prefs = []; - const promises = []; //only fetch profiles that we don't have and the Direct channel should be visible - // Prepare preferences and start fetching profiles to batch them - directChannels.forEach((c) => { - const profilesInChannel = Array.from(usersInChannel[c.id] || []).filter((u) => u.id !== currentUserId); - switch (c.type) { - case General.DM_CHANNEL: { - const dm = fetchDirectMessageProfileIfNeeded(state, c, channelMembers, profilesInChannel, prefs); - - if (dm) { - promises.push(dispatch(dm)); - } - break; - } - case General.GM_CHANNEL: { - const gm = fetchGroupMessageProfilesIfNeeded(state, c, channelMembers, profilesInChannel, prefs); - - if (gm) { - promises.push(dispatch(gm)); - } - break; - } - } - }); - - // Save preferences if there are any changes - if (prefs.length) { - dispatch(savePreferences(currentUserId, prefs)); - } - - getProfilesFromPromises(dispatch, promises, directChannels); - - return {data: true}; - }; -} - -export function getUsersInChannel(channelId) { - return async (dispatch, getState) => { - try { - const state = getState(); - const currentUserId = getCurrentUserId(state); - const profiles = await Client4.getProfilesInChannel(channelId); - - // When fetching profiles in channels we exclude our own user - const users = profiles.filter((p) => p.id !== currentUserId); - const data = { - channelId, - users, - }; - - return {data}; - } catch (error) { - return {error}; + const sidebarActions = await loadSidebarDirectMessagesProfiles(state, channels, channelMembers); + if (sidebarActions.length) { + dispatch(batchActions(sidebarActions, 'BATCH_LOAD_SIDEBAR')); } }; -} - -async function getProfilesFromPromises(dispatch, promiseArray, directChannels) { - // Get the profiles returned by the promises and retry those that failed - let promises = promiseArray; - for (let i = 0; i <= MAX_RETRIES; i++) { - if (!promises.length) { - return; - } - - const result = await Promise.all(promises); //eslint-disable-line no-await-in-loop - const failed = []; - - result.forEach((p, index) => { - if (p.error) { - failed.push(directChannels[index].id); - } - }); - - dispatch({ - type: UserTypes.RECEIVED_BATCHED_PROFILES_IN_CHANNEL, - data: result, - }); - - if (failed.length) { - promises = failed.map((id) => dispatch(getUsersInChannel(id))); //eslint-disable-line no-loop-func - continue; - } - - return; - } -} - -function fetchDirectMessageProfileIfNeeded(state, channel, channelMembers, profilesInChannel, newPreferences) { - const currentUserId = getCurrentUserId(state); - const preferences = getMyPreferences(state); - const users = getUsers(state); - const config = getConfig(state); - const currentChannelId = getCurrentChannelId(state); - const otherUserId = getUserIdFromChannelName(currentUserId, channel.name); - const otherUser = users[otherUserId]; - const dmVisible = isDirectMessageVisible(preferences, channel.id); - const dmAutoClosed = isDirectChannelAutoClosed(config, preferences, channel.id, channel.last_post_at, otherUser?.delete_at, currentChannelId); //eslint-disable-line camelcase - const dmIsUnread = channelMembers[channel.id]?.mention_count > 0; //eslint-disable-line camelcase - const dmFetchProfile = dmIsUnread || (dmVisible && !dmAutoClosed); - - // when then DM is hidden but has new messages - if ((!dmVisible || dmAutoClosed) && dmIsUnread) { - newPreferences.push(buildPreference(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, currentUserId, otherUserId)); - newPreferences.push(buildPreference(Preferences.CATEGORY_CHANNEL_OPEN_TIME, currentUserId, channel.id, Date.now().toString())); - } - - if (dmFetchProfile && !profilesInChannel.includes(otherUserId) && otherUserId !== currentUserId) { - return getUsersInChannel(channel.id); - } - - return null; -} - -function fetchGroupMessageProfilesIfNeeded(state, channel, channelMembers, profilesInChannel, newPreferences) { - const currentUserId = getCurrentUserId(state); - const preferences = getMyPreferences(state); - const config = getConfig(state); - const gmVisible = isGroupMessageVisible(preferences, channel.id); - const gmAutoClosed = isDirectChannelAutoClosed(config, preferences, channel.id, channel.last_post_at); - const channelMember = channelMembers[channel.id]; - const gmIsUnread = channelMember?.mention_count > 0 || channelMember?.msg_count < channel.total_msg_count; //eslint-disable-line camelcase - const gmFetchProfile = gmIsUnread || (gmVisible && !gmAutoClosed); - - // when then GM is hidden but has new messages - if ((!gmVisible || gmAutoClosed) && gmIsUnread) { - newPreferences.push(buildPreference(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, currentUserId, channel.id)); - newPreferences.push(buildPreference(Preferences.CATEGORY_CHANNEL_OPEN_TIME, currentUserId, channel.id, Date.now().toString())); - } - - if (gmFetchProfile && !profilesInChannel.length) { - return getUsersInChannel(channel.id); - } - - return null; -} +} \ No newline at end of file diff --git a/app/actions/views/channel.test.js b/app/actions/views/channel.test.js index 03615be55..4280d0058 100644 --- a/app/actions/views/channel.test.js +++ b/app/actions/views/channel.test.js @@ -211,7 +211,7 @@ describe('Actions.Views.Channel', () => { expect(postActions.getPosts).toBeCalled(); const storeActions = store.getActions(); - const storeBatchActions = storeActions.filter(({type}) => type === 'BATCHING_REDUCER.BATCH'); + const storeBatchActions = storeActions.filter(({type}) => type === 'BATCH_LOAD_POSTS_IN_CHANNEL'); const receivedPosts = storeActions.find(({type}) => type === MOCK_RECEIVED_POSTS); const receivedPostsAtAction = storeBatchActions[0].payload.some((action) => action.type === 'RECEIVED_POSTS_FOR_CHANNEL_AT_TIME'); @@ -297,7 +297,7 @@ describe('Actions.Views.Channel', () => { await store.dispatch(handleSelectChannel(channelId)); const storeActions = store.getActions(); - const storeBatchActions = storeActions.find(({type}) => type === 'BATCHING_REDUCER.BATCH'); + const storeBatchActions = storeActions.find(({type}) => type === 'BATCH_SWITCH_CHANNEL'); const selectChannelWithMember = storeBatchActions?.payload.find(({type}) => type === ChannelTypes.SELECT_CHANNEL); const viewedAction = storeActions.find(({type}) => type === MOCK_CHANNEL_MARK_AS_VIEWED); const readAction = storeActions.find(({type}) => type === MOCK_CHANNEL_MARK_AS_READ); diff --git a/app/actions/views/emoji.js b/app/actions/views/emoji.js index 87978f1f0..26d95ba09 100644 --- a/app/actions/views/emoji.js +++ b/app/actions/views/emoji.js @@ -79,7 +79,7 @@ export function getEmojisInPosts(posts) { } if (actions.length) { - dispatch(batchActions(actions)); + dispatch(batchActions(actions, 'BATCH_GET_EMOJIS_FOR_POSTS')); } } }; diff --git a/app/actions/views/post.js b/app/actions/views/post.js index 75193d12a..b44660716 100644 --- a/app/actions/views/post.js +++ b/app/actions/views/post.js @@ -8,6 +8,7 @@ import { doPostAction, getNeededAtMentionedUsernames, receivedNewPost, + receivedPost, receivedPosts, receivedPostsBefore, receivedPostsInChannel, @@ -16,6 +17,7 @@ import { } from 'mattermost-redux/actions/posts'; import {Client4} from 'mattermost-redux/client'; import {Posts} from 'mattermost-redux/constants'; +import {getPost as selectPost} from 'mattermost-redux/selectors/entities/posts'; import {removeUserFromList} from 'mattermost-redux/utils/user_utils'; import {ViewTypes} from 'app/constants'; @@ -91,12 +93,37 @@ export function getPosts(channelId, page = 0, perPage = Posts.POST_CHUNK_SIZE) { if (posts?.length) { actions.push(receivedPosts(data)); const additional = await dispatch(getPostsAdditionalDataBatch(posts)); - if (additional.length) { - actions.push(...additional); + if (additional.data.length) { + actions.push(...additional.data); } } - dispatch(batchActions(actions)); + dispatch(batchActions(actions, 'BATCH_GET_POSTS')); + + return {data}; + } catch (error) { + return {error}; + } + }; +} + +export function getPost(postId) { + return async (dispatch) => { + try { + const data = await Client4.getPost(postId); + + if (data) { + const actions = [ + receivedPost(data), + ]; + + const additional = await dispatch(getPostsAdditionalDataBatch([data])); + if (additional.data.length) { + actions.push(...additional.data); + } + + dispatch(batchActions(actions, 'BATCH_GET_POST')); + } return {data}; } catch (error) { @@ -118,11 +145,11 @@ export function getPostsSince(channelId, since) { ]; const additional = await dispatch(getPostsAdditionalDataBatch(posts)); - if (additional.length) { - actions.push(...additional); + if (additional.data.length) { + actions.push(...additional.data); } - dispatch(batchActions(actions)); + dispatch(batchActions(actions, 'BATCH_GET_POSTS_SINCE')); } return {data}; @@ -145,11 +172,11 @@ export function getPostsBefore(channelId, postId, page = 0, perPage = Posts.POST ]; const additional = await dispatch(getPostsAdditionalDataBatch(posts)); - if (additional.length) { - actions.push(...additional); + if (additional.data.length) { + actions.push(...additional.data); } - dispatch(batchActions(actions)); + dispatch(batchActions(actions, 'BATCH_GET_POSTS_BEFORE')); } return {data}; @@ -159,7 +186,7 @@ export function getPostsBefore(channelId, postId, page = 0, perPage = Posts.POST }; } -export function getPostThread(rootId) { +export function getPostThread(rootId, skipDispatch = false) { return async (dispatch) => { try { const data = await Client4.getPostThread(rootId); @@ -172,11 +199,15 @@ export function getPostThread(rootId) { ]; const additional = await dispatch(getPostsAdditionalDataBatch(posts)); - if (additional.length) { - actions.push(...additional); + if (additional.data.length) { + actions.push(...additional.data); } - dispatch(batchActions(actions)); + if (skipDispatch) { + return {data: actions}; + } + + dispatch(batchActions(actions, 'BATCH_GET_POSTS_THREAD')); } return {data}; @@ -219,11 +250,11 @@ export function getPostsAround(channelId, postId, perPage = Posts.POST_CHUNK_SIZ ]; const additional = await dispatch(getPostsAdditionalDataBatch(posts)); - if (additional.length) { - actions.push(...additional); + if (additional.data.length) { + actions.push(...additional.data); } - dispatch(batchActions(actions)); + dispatch(batchActions(actions, 'BATCH_GET_POSTS_AROUND')); } return {data}; @@ -233,12 +264,40 @@ export function getPostsAround(channelId, postId, perPage = Posts.POST_CHUNK_SIZ }; } -function getPostsAdditionalDataBatch(posts = []) { +export function handleNewPostBatch(WebSocketMessage) { return async (dispatch, getState) => { - const actions = []; + const state = getState(); + const post = JSON.parse(WebSocketMessage.data.post); + const actions = [receivedNewPost(post)]; + + // If we don't have the thread for this post, fetch it from the server + // and include the actions in the batch + if (post.root_id) { + const rootPost = selectPost(state, post.root_id); + + if (!rootPost) { + const thread = await dispatch(getPostThread(post.root_id, true)); + if (thread.actions?.length) { + actions.push(...thread.actions); + } + } + } + + const additional = await dispatch(getPostsAdditionalDataBatch([post])); + if (additional.data.length) { + actions.push(...additional.data); + } + + return actions; + }; +} + +export function getPostsAdditionalDataBatch(posts = []) { + return async (dispatch, getState) => { + const data = []; if (!posts.length) { - return actions; + return {data}; } // Custom Emojis used in the posts @@ -249,7 +308,7 @@ function getPostsAdditionalDataBatch(posts = []) { const state = getState(); const promises = []; const promiseTrace = []; - const extra = dispatch(profilesStatusesAndToLoadFromPosts(posts)); + const extra = userMetadataToLoadFromPosts(state, posts); if (extra?.userIds.length) { promises.push(Client4.getProfilesByIds(extra.userIds)); @@ -273,7 +332,7 @@ function getPostsAdditionalDataBatch(posts = []) { const type = promiseTrace[index]; switch (type) { case 'statuses': - actions.push({ + data.push({ type: UserTypes.RECEIVED_STATUSES, data: p, }); @@ -282,7 +341,7 @@ function getPostsAdditionalDataBatch(posts = []) { const {currentUserId} = state.entities.users; removeUserFromList(currentUserId, p); - actions.push({ + data.push({ type: UserTypes.RECEIVED_PROFILES_LIST, data: p, }); @@ -296,42 +355,39 @@ function getPostsAdditionalDataBatch(posts = []) { // do nothing } - return actions; + return {data}; }; } -function profilesStatusesAndToLoadFromPosts(posts = []) { - return (dispatch, getState) => { - const state = getState(); - const {currentUserId, profiles, statuses} = state.entities.users; +function userMetadataToLoadFromPosts(state, posts = []) { + const {currentUserId, profiles, statuses} = state.entities.users; - // Profiles of users mentioned in the posts - const usernamesToLoad = getNeededAtMentionedUsernames(state, posts); + // Profiles of users mentioned in the posts + const usernamesToLoad = getNeededAtMentionedUsernames(state, posts); - // Statuses and profiles of the users who made the posts - const userIdsToLoad = new Set(); - const statusesToLoad = new Set(); + // Statuses and profiles of the users who made the posts + const userIdsToLoad = new Set(); + const statusesToLoad = new Set(); - posts.forEach((post) => { - const userId = post.user_id; + posts.forEach((post) => { + const userId = post.user_id; - if (!statuses[userId]) { - statusesToLoad.add(userId); - } + if (!statuses[userId]) { + statusesToLoad.add(userId); + } - if (userId === currentUserId) { - return; - } + if (userId === currentUserId) { + return; + } - if (!profiles[userId]) { - userIdsToLoad.add(userId); - } - }); + if (!profiles[userId]) { + userIdsToLoad.add(userId); + } + }); - return { - usernames: Array.from(usernamesToLoad), - userIds: Array.from(userIdsToLoad), - statuses: Array.from(statusesToLoad), - }; + return { + usernames: Array.from(usernamesToLoad), + userIds: Array.from(userIdsToLoad), + statuses: Array.from(statusesToLoad), }; } \ No newline at end of file diff --git a/app/actions/views/root.js b/app/actions/views/root.js index ba484c6ef..3dc498b9a 100644 --- a/app/actions/views/root.js +++ b/app/actions/views/root.js @@ -116,7 +116,7 @@ export function handleSelectTeamAndChannel(teamId, channelId) { } if (actions.length) { - dispatch(batchActions(actions)); + dispatch(batchActions(actions, 'BATCH_SELECT_TEAM_AND_CHANNEL')); } EphemeralStore.setStartFromNotification(false); diff --git a/app/actions/views/select_server.js b/app/actions/views/select_server.js index 4f5232bde..5defda3db 100644 --- a/app/actions/views/select_server.js +++ b/app/actions/views/select_server.js @@ -11,7 +11,7 @@ export function handleServerUrlChanged(serverUrl) { {type: GeneralTypes.CLIENT_CONFIG_RESET}, {type: GeneralTypes.CLIENT_LICENSE_RESET}, {type: ViewTypes.SERVER_URL_CHANGED, serverUrl}, - ]); + ], 'BATCH_SERVER_URL_CHANGED'); } export function setServerUrl(serverUrl) { diff --git a/app/actions/views/select_server.test.js b/app/actions/views/select_server.test.js index 6da27d860..ea64379a1 100644 --- a/app/actions/views/select_server.test.js +++ b/app/actions/views/select_server.test.js @@ -26,7 +26,7 @@ describe('Actions.Views.SelectServer', () => { {type: GeneralTypes.CLIENT_CONFIG_RESET}, {type: GeneralTypes.CLIENT_LICENSE_RESET}, {type: ViewTypes.SERVER_URL_CHANGED, serverUrl}, - ]); + ], 'BATCH_SERVER_URL_CHANGED'); store.dispatch(handleServerUrlChanged(serverUrl)); expect(store.getActions()).toEqual([actions]); diff --git a/app/actions/views/select_team.js b/app/actions/views/select_team.js index 59d9b6c12..b69479e50 100644 --- a/app/actions/views/select_team.js +++ b/app/actions/views/select_team.js @@ -23,7 +23,7 @@ export function handleTeamChange(teamId) { dispatch(batchActions([ {type: TeamTypes.SELECT_TEAM, data: teamId}, {type: ChannelTypes.SELECT_CHANNEL, data: '', extra: {}}, - ])); + ], 'BATCH_SWITCH_TEAM')); }; } diff --git a/app/actions/views/thread.js b/app/actions/views/thread.js index 03da3f4ec..de6240e5c 100644 --- a/app/actions/views/thread.js +++ b/app/actions/views/thread.js @@ -4,10 +4,16 @@ import {ViewTypes} from 'app/constants'; export function handleCommentDraftChanged(rootId, draft) { - return { - type: ViewTypes.COMMENT_DRAFT_CHANGED, - rootId, - draft, + return (dispatch, getState) => { + const state = getState(); + + if (state.views.thread.drafts[rootId]?.draft !== draft) { + dispatch({ + type: ViewTypes.COMMENT_DRAFT_CHANGED, + rootId, + draft, + }); + } }; } diff --git a/app/actions/views/thread.test.js b/app/actions/views/thread.test.js index e587f1094..6ae47a62a 100644 --- a/app/actions/views/thread.test.js +++ b/app/actions/views/thread.test.js @@ -17,7 +17,13 @@ describe('Actions.Views.Thread', () => { let store; beforeEach(() => { - store = mockStore({}); + store = mockStore({ + views: { + thread: { + drafts: {}, + }, + }, + }); }); test('handleCommentDraftChanged', () => { diff --git a/app/actions/views/typing.js b/app/actions/views/typing.js index bcd7e069c..dd26ee703 100644 --- a/app/actions/views/typing.js +++ b/app/actions/views/typing.js @@ -1,13 +1,14 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {userTyping as wsUserTyping} from 'mattermost-redux/actions/websocket'; +import {userTyping as wsUserTyping} from '@actions/websocket'; export function userTyping(channelId, rootId) { return async (dispatch, getState) => { - const {websocket} = getState(); + const state = getState(); + const {websocket} = state; if (websocket.connected) { - wsUserTyping(channelId, rootId)(dispatch, getState); + wsUserTyping(state, channelId, rootId); } }; } diff --git a/app/actions/views/user.js b/app/actions/views/user.js index 1cd9a9e1e..4f24efaa3 100644 --- a/app/actions/views/user.js +++ b/app/actions/views/user.js @@ -1,20 +1,21 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {GeneralTypes, UserTypes} from 'mattermost-redux/action_types'; +import {batchActions} from 'redux-batched-actions'; + +import {GeneralTypes, RoleTypes, UserTypes} from 'mattermost-redux/action_types'; import {getDataRetentionPolicy} from 'mattermost-redux/actions/general'; import * as HelperActions from 'mattermost-redux/actions/helpers'; -import {loadRolesIfNeeded} from 'mattermost-redux/actions/roles'; import {autoUpdateTimezone} from 'mattermost-redux/actions/timezone'; import {Client4} from 'mattermost-redux/client'; import {General} from 'mattermost-redux/constants'; import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone'; -import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; +import {getCurrentUserId, getStatusForUserId} from 'mattermost-redux/selectors/entities/users'; import {setAppCredentials} from 'app/init/credentials'; -import {setCSRFFromCookie} from 'app/utils/security'; -import {getDeviceTimezoneAsync} from 'app/utils/timezone'; +import {setCSRFFromCookie} from '@utils/security'; +import {getDeviceTimezoneAsync} from '@utils/timezone'; const HTTP_UNAUTHORIZED = 401; @@ -46,14 +47,42 @@ export function completeLogin(user, deviceToken) { }; } -export function loadMe(user, deviceToken) { +export function getMe() { + return async (dispatch) => { + try { + const data = {}; + data.me = await Client4.getMe(); + + const actions = [{ + type: UserTypes.RECEIVED_ME, + data: data.me, + }]; + + const roles = data.me.roles.split(' '); + data.roles = await Client4.getRolesByNames(roles); + if (data.roles.length) { + actions.push({ + type: RoleTypes.RECEIVED_ROLES, + data: data.roles, + }); + } + + dispatch(batchActions(actions, 'BATCH_GET_ME')); + return {data}; + } catch (error) { + return {error}; + } + }; +} + +export function loadMe(user, deviceToken, skipDispatch = false) { return async (dispatch, getState) => { const state = getState(); const data = {user}; const deviceId = state.entities?.general?.deviceToken; try { - if (deviceId && !deviceToken) { + if (deviceId && !deviceToken && !skipDispatch) { await Client4.attachDevice(deviceId); } @@ -75,6 +104,7 @@ export function loadMe(user, deviceToken) { const teamUnreadRequest = Client4.getMyTeamUnreads(); const preferencesRequest = Client4.getMyPreferences(); const configRequest = Client4.getClientConfigOld(); + const actions = []; const [teams, teamMembers, teamUnreads, preferences, config] = await Promise.all([ teamsRequest, @@ -91,22 +121,33 @@ export function loadMe(user, deviceToken) { data.config = config; data.url = Client4.getUrl(); - dispatch({ + actions.push({ type: UserTypes.LOGIN, data, }); - const roles = new Set(); + const rolesToLoad = new Set(); for (const role of data.user.roles.split(' ')) { - roles.add(role); + rolesToLoad.add(role); } + for (const teamMember of teamMembers) { for (const role of teamMember.roles.split(' ')) { - roles.add(role); + rolesToLoad.add(role); } } - if (roles.size > 0) { - dispatch(loadRolesIfNeeded(roles)); + if (rolesToLoad.size > 0) { + data.roles = await Client4.getRolesByNames(Array.from(rolesToLoad)); + if (data.roles.length) { + actions.push({ + type: RoleTypes.RECEIVED_ROLES, + data: data.roles, + }); + } + } + + if (!skipDispatch) { + dispatch(batchActions(actions, 'BATCH_LOAD_ME')); } } catch (error) { console.log('login error', error.stack); // eslint-disable-line no-console @@ -182,15 +223,19 @@ export function forceLogoutIfNecessary(error) { export function setCurrentUserStatusOffline() { return (dispatch, getState) => { - const currentUserId = getCurrentUserId(getState()); + const state = getState(); + const currentUserId = getCurrentUserId(state); + const status = getStatusForUserId(state, currentUserId); - return dispatch({ - type: UserTypes.RECEIVED_STATUS, - data: { - user_id: currentUserId, - status: General.OFFLINE, - }, - }); + if (status !== General.OFFLINE) { + dispatch({ + type: UserTypes.RECEIVED_STATUS, + data: { + user_id: currentUserId, + status: General.OFFLINE, + }, + }); + } }; } diff --git a/app/actions/websocket.test.js b/app/actions/websocket.test.js new file mode 100644 index 000000000..31fa29963 --- /dev/null +++ b/app/actions/websocket.test.js @@ -0,0 +1,1096 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import assert from 'assert'; +import nock from 'nock'; +import {Server, WebSocket as MockWebSocket} from 'mock-socket'; +import {batchActions} from 'redux-batched-actions'; +import thunk from 'redux-thunk'; +import configureMockStore from 'redux-mock-store'; + +import {ChannelTypes, GeneralTypes, RoleTypes, TeamTypes, UserTypes} from 'mattermost-redux/action_types'; +import * as ChannelActions from 'mattermost-redux/actions/channels'; +import * as PostActions from 'mattermost-redux/actions/posts'; +import * as PreferenceActions from 'mattermost-redux/actions/preferences'; +import * as TeamActions from 'mattermost-redux/actions/teams'; +import * as UserActions from 'mattermost-redux/actions/users'; +import {Client4} from 'mattermost-redux/client'; +import {General, Posts, RequestStatus} from 'mattermost-redux/constants'; +import * as PostSelectors from 'mattermost-redux/selectors/entities/posts'; +import EventEmitter from 'mattermost-redux/utils/event_emitter'; + +import * as Actions from '@actions/websocket'; +import {WebsocketEvents} from '@constants'; + +import TestHelper from 'test/test_helper'; +import configureStore from 'test/test_store'; +import initial_state from 'app/initial_state'; + +require('isomorphic-fetch'); + +global.WebSocket = MockWebSocket; + +const mockConfigRequest = (config = {}) => { + nock(Client4.getBaseRoute()). + get('/config/client?format=old'). + reply(200, config); +}; + +const mockChanelsRequest = (teamId, channels = []) => { + nock(Client4.getUserRoute('me')). + get(`/teams/${teamId}/channels?include_deleted=true`). + reply(200, channels); +}; + +const mockRolesRequest = (rolesToLoad = []) => { + nock(Client4.getRolesRoute()). + post('/names', JSON.stringify(rolesToLoad)). + reply(200, rolesToLoad); +}; + +const mockTeamMemberRequest = (tm = []) => { + nock(Client4.getUserRoute('me')). + log(console.log). + get('/teams/members'). + reply(200, tm); +}; + +describe('Actions.Websocket', () => { + let store; + let mockServer; + beforeAll(async () => { + store = await configureStore(); + await TestHelper.initBasic(Client4); + + const connUrl = (Client4.getUrl() + '/api/v4/websocket').replace(/^http:/, 'ws:'); + mockServer = new Server(connUrl); + return store.dispatch(Actions.init({websocketUrl: Client4.getUrl().replace(/^http:/, 'ws:')})); + }); + + afterAll(async () => { + Actions.close()(); + mockServer.stop(); + await TestHelper.tearDown(); + }); + + it('WebSocket Connect', () => { + const ws = store.getState().requests.general.websocket; + assert.ok(ws.status === RequestStatus.SUCCESS); + }); + + it('Websocket Handle New Post if post does not exist', async () => { + PostSelectors.getPost = jest.fn(); + const channelId = TestHelper.basicChannel.id; + const message = JSON.stringify({event: WebsocketEvents.POSTED, data: {channel_display_name: TestHelper.basicChannel.display_name, channel_name: TestHelper.basicChannel.name, channel_type: 'O', post: `{"id": "71k8gz5ompbpfkrzaxzodffj8w", "create_at": 1508245311774, "update_at": 1508245311774, "edit_at": 0, "delete_at": 0, "is_pinned": false, "user_id": "${TestHelper.basicUser.id}", "channel_id": "${channelId}", "root_id": "", "parent_id": "", "original_id": "", "message": "Unit Test", "type": "", "props": {}, "hashtags": "", "pending_post_id": "t36kso9nwtdhbm8dbkd6g4eeby: 1508245311749"}`, sender_name: TestHelper.basicUser.username, team_id: TestHelper.basicTeam.id}, broadcast: {omit_users: null, user_id: '', channel_id: channelId, team_id: ''}, seq: 2}); + + nock(Client4.getBaseRoute()). + post('/users/ids'). + reply(200, [TestHelper.basicUser.id]); + + nock(Client4.getBaseRoute()). + post('/users/status/ids'). + reply(200, [{user_id: TestHelper.basicUser.id, status: 'online', manual: false, last_activity_at: 1507662212199}]); + + // Mock that post already exists and check it is not added + PostSelectors.getPost.mockReturnValueOnce(true); + mockServer.emit('message', message); + let entities = store.getState().entities; + let posts = entities.posts.posts; + assert.deepEqual(posts, {}); + + // Mock that post does not exist and check it is added + PostSelectors.getPost.mockReturnValueOnce(false); + mockServer.emit('message', message); + await TestHelper.wait(100); + entities = store.getState().entities; + posts = entities.posts.posts; + const postId = Object.keys(posts)[0]; + assert.ok(posts[postId].message.indexOf('Unit Test') > -1); + entities = store.getState().entities; + }); + + it('Websocket Handle New Post emits INCREASE_POST_VISIBILITY_BY_ONE for current channel when post does not exist', async () => { + PostSelectors.getPost = jest.fn(); + const emit = jest.spyOn(EventEmitter, 'emit'); + const currentChannelId = TestHelper.generateId(); + const otherChannelId = TestHelper.generateId(); + const messageFor = (channelId) => ({event: WebsocketEvents.POSTED, data: {channel_display_name: TestHelper.basicChannel.display_name, channel_name: TestHelper.basicChannel.name, channel_type: 'O', post: `{"id": "71k8gz5ompbpfkrzaxzodffj8w", "create_at": 1508245311774, "update_at": 1508245311774, "edit_at": 0, "delete_at": 0, "is_pinned": false, "user_id": "${TestHelper.basicUser.id}", "channel_id": "${channelId}", "root_id": "", "parent_id": "", "original_id": "", "message": "Unit Test", "type": "", "props": {}, "hashtags": "", "pending_post_id": "t36kso9nwtdhbm8dbkd6g4eeby: 1508245311749"}`, sender_name: TestHelper.basicUser.username, team_id: TestHelper.basicTeam.id}, broadcast: {omit_users: null, user_id: '', channel_id: channelId, team_id: ''}, seq: 2}); + + await store.dispatch(ChannelActions.selectChannel(currentChannelId)); + await TestHelper.wait(100); + + // Post does not exist and is not for current channel + PostSelectors.getPost.mockReturnValueOnce(false); + mockServer.emit('message', JSON.stringify(messageFor(otherChannelId))); + expect(emit).not.toHaveBeenCalled(); + + // Post exists and is not for current channel + PostSelectors.getPost.mockReturnValueOnce(true); + mockServer.emit('message', JSON.stringify(messageFor(otherChannelId))); + expect(emit).not.toHaveBeenCalled(); + + // Post exists and is for current channel + PostSelectors.getPost.mockReturnValueOnce(true); + mockServer.emit('message', JSON.stringify(messageFor(currentChannelId))); + expect(emit).not.toHaveBeenCalled(); + + // Post does not exist and is for current channel + PostSelectors.getPost.mockReturnValueOnce(false); + mockServer.emit('message', JSON.stringify(messageFor(currentChannelId))); + expect(emit).toHaveBeenCalledWith(WebsocketEvents.INCREASE_POST_VISIBILITY_BY_ONE); + }); + + it('Websocket Handle New Post if status is manually set do not set to online', async () => { + const userId = TestHelper.generateId(); + + store = await configureStore({ + entities: { + users: { + statuses: { + [userId]: General.DND, + }, + isManualStatus: { + [userId]: true, + }, + }, + }, + }); + await store.dispatch(Actions.init({websocketUrl: Client4.getUrl().replace(/^http:/, 'ws:')})); + + const channelId = TestHelper.basicChannel.id; + const message = JSON.stringify({ + event: WebsocketEvents.POSTED, + data: { + channel_display_name: TestHelper.basicChannel.display_name, + channel_name: TestHelper.basicChannel.name, + channel_type: 'O', + post: `{"id": "71k8gz5ompbpfkrzaxzodffj8w", "create_at": 1508245311774, "update_at": 1508245311774, "edit_at": 0, "delete_at": 0, "is_pinned": false, "user_id": "${userId}", "channel_id": "${channelId}", "root_id": "", "parent_id": "", "original_id": "", "message": "Unit Test", "type": "", "props": {}, "hashtags": "", "pending_post_id": "t36kso9nwtdhbm8dbkd6g4eeby: 1508245311749"}`, + sender_name: TestHelper.basicUser.username, + team_id: TestHelper.basicTeam.id, + }, + broadcast: { + omit_users: null, + user_id: userId, + channel_id: channelId, + team_id: '', + }, + seq: 2, + }); + + mockServer.emit('message', message); + const entities = store.getState().entities; + const statuses = entities.users.statuses; + assert.equal(statuses[userId], General.DND); + }); + + it('Websocket Handle Post Edited', async () => { + const post = {id: '71k8gz5ompbpfkrzaxzodffj8w'}; + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.POST_EDITED, data: {post: `{"id": "71k8gz5ompbpfkrzaxzodffj8w","create_at": 1508245311774,"update_at": 1585236976007,"edit_at": 1585236976007,"delete_at": 0,"is_pinned": false,"user_id": "${TestHelper.basicUser.id}","channel_id": "${TestHelper.basicChannel.id}","root_id": "","parent_id": "","original_id": "","message": "Unit Test (edited)","type": "","props": {},"hashtags": "","pending_post_id": ""}`}, broadcast: {omit_users: null, user_id: '', channel_id: '18k9ffsuci8xxm7ak68zfdyrce', team_id: ''}, seq: 2})); + + await TestHelper.wait(300); + + const {posts} = store.getState().entities.posts; + assert.ok(posts); + assert.ok(posts[post.id]); + assert.ok(posts[post.id].message.indexOf('(edited)') > -1); + }); + + it('Websocket Handle Post Deleted', async () => { + const post = TestHelper.fakePost(); + post.channel_id = TestHelper.basicChannel.id; + + post.id = '71k8gz5ompbpfkrzaxzodffj8w'; + store.dispatch(PostActions.receivedPost(post)); + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.POST_DELETED, data: {post: `{"id": "71k8gz5ompbpfkrzaxzodffj8w","create_at": 1508245311774,"update_at": 1508247709215,"edit_at": 1508247709215,"delete_at": 0,"is_pinned": false,"user_id": "${TestHelper.basicUser.id}","channel_id": "${post.channel_id}","root_id": "","parent_id": "","original_id": "","message": "Unit Test","type": "","props": {},"hashtags": "","pending_post_id": ""}`}, broadcast: {omit_users: null, user_id: '', channel_id: '18k9ffsuci8xxm7ak68zfdyrce', team_id: ''}, seq: 7})); + + const entities = store.getState().entities; + const {posts} = entities.posts; + assert.strictEqual(posts[post.id].state, Posts.POST_DELETED); + }); + + it('Websocket handle Post Unread', async () => { + const teamId = TestHelper.generateId(); + const channelId = TestHelper.generateId(); + const userId = TestHelper.generateId(); + + store = await configureStore({ + entities: { + channels: { + channels: { + [channelId]: {id: channelId}, + }, + myMembers: { + [channelId]: {msg_count: 10, mention_count: 0, last_viewed_at: 0}, + }, + }, + teams: { + myMembers: { + [teamId]: {msg_count: 10, mention_count: 0}, + }, + }, + }, + }); + await store.dispatch(Actions.init({websocketUrl: Client4.getUrl().replace(/^http:/, 'ws:')})); + + mockServer.emit('message', JSON.stringify({ + event: WebsocketEvents.POST_UNREAD, + data: { + last_viewed_at: 25, + msg_count: 3, + mention_count: 2, + delta_msg: 7, + }, + broadcast: {omit_users: null, user_id: userId, channel_id: channelId, team_id: teamId}, + seq: 7, + })); + + const state = store.getState(); + assert.equal(state.entities.channels.manuallyUnread[channelId], true); + assert.equal(state.entities.channels.myMembers[channelId].msg_count, 3); + assert.equal(state.entities.channels.myMembers[channelId].mention_count, 2); + assert.equal(state.entities.channels.myMembers[channelId].last_viewed_at, 25); + assert.equal(state.entities.teams.myMembers[teamId].msg_count, 3); + assert.equal(state.entities.teams.myMembers[teamId].mention_count, 2); + }); + + it('Websocket handle Post Unread When marked on the same client', async () => { + const teamId = TestHelper.generateId(); + const channelId = TestHelper.generateId(); + const userId = TestHelper.generateId(); + + store = await configureStore({ + entities: { + channels: { + channels: { + [channelId]: {id: channelId}, + }, + myMembers: { + [channelId]: {msg_count: 5, mention_count: 4, last_viewed_at: 14}, + }, + manuallyUnread: { + [channelId]: true, + }, + }, + teams: { + myMembers: { + [teamId]: {msg_count: 5, mention_count: 4}, + }, + }, + }, + }); + await store.dispatch(Actions.init({websocketUrl: Client4.getUrl().replace(/^http:/, 'ws:')})); + + mockServer.emit('message', JSON.stringify({ + event: WebsocketEvents.POST_UNREAD, + data: { + last_viewed_at: 25, + msg_count: 5, + mention_count: 4, + delta_msg: 1, + }, + broadcast: {omit_users: null, user_id: userId, channel_id: channelId, team_id: teamId}, + seq: 17, + })); + + const state = store.getState(); + assert.equal(state.entities.channels.manuallyUnread[channelId], true); + assert.equal(state.entities.channels.myMembers[channelId].msg_count, 5); + assert.equal(state.entities.channels.myMembers[channelId].mention_count, 4); + assert.equal(state.entities.channels.myMembers[channelId].last_viewed_at, 14); + assert.equal(state.entities.teams.myMembers[teamId].msg_count, 5); + assert.equal(state.entities.teams.myMembers[teamId].mention_count, 4); + }); + + it('Websocket Handle Reaction Added to Post', async () => { + const emoji = '+1'; + const post = {id: 'w7yo9377zbfi9mgiq5gbfpn3ha'}; + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.REACTION_ADDED, data: {reaction: `{"user_id":"${TestHelper.basicUser.id}","post_id":"w7yo9377zbfi9mgiq5gbfpn3ha","emoji_name":"${emoji}","create_at":1508249125852}`}, broadcast: {omit_users: null, user_id: '', channel_id: TestHelper.basicChannel.id, team_id: ''}, seq: 12})); + + await TestHelper.wait(300); + const nextEntities = store.getState().entities; + const {reactions} = nextEntities.posts; + const reactionsForPost = reactions[post.id]; + + assert.ok(reactionsForPost.hasOwnProperty(`${TestHelper.basicUser.id}-${emoji}`)); + }); + + // If we move this test lower it will fail cause of a permissions issue + it('Websocket handle team updated', async () => { + const team = {id: '55pfercbm7bsmd11p5cjpgsbwr'}; + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.UPDATE_TEAM, data: {team: `{"id":"55pfercbm7bsmd11p5cjpgsbwr","create_at":1495553950859,"update_at":1508250370054,"delete_at":0,"display_name":"${TestHelper.basicTeam.display_name}","name":"${TestHelper.basicTeam.name}","description":"description","email":"","type":"O","company_name":"","allowed_domains":"","invite_id":"m93f54fu5bfntewp8ctwonw19w","allow_open_invite":true}`}, broadcast: {omit_users: null, user_id: '', channel_id: '', team_id: ''}, seq: 26})); + + await TestHelper.wait(300); + + const entities = store.getState().entities; + const {teams} = entities.teams; + const updated = teams[team.id]; + assert.ok(updated); + assert.strictEqual(updated.allow_open_invite, true); + }); + + it('Websocket handle team patched', async () => { + const team = {id: '55pfercbm7bsmd11p5cjpgsbwr'}; + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.UPDATE_TEAM, data: {team: `{"id":"55pfercbm7bsmd11p5cjpgsbwr","create_at":1495553950859,"update_at":1508250370054,"delete_at":0,"display_name":"${TestHelper.basicTeam.display_name}","name":"${TestHelper.basicTeam.name}","description":"description","email":"","type":"O","company_name":"","allowed_domains":"","invite_id":"m93f54fu5bfntewp8ctwonw19w","allow_open_invite":true}`}, broadcast: {omit_users: null, user_id: '', channel_id: '', team_id: ''}, seq: 26})); + + await TestHelper.wait(300); + + const entities = store.getState().entities; + const {teams} = entities.teams; + const updated = teams[team.id]; + assert.ok(updated); + assert.strictEqual(updated.allow_open_invite, true); + }); + + it('WebSocket Leave Team', async () => { + const team = TestHelper.basicTeam; + store.dispatch(batchActions([ + {type: UserTypes.RECEIVED_ME, data: TestHelper.basicUser}, + {type: TeamTypes.RECEIVED_TEAM, data: TestHelper.basicTeam}, + {type: TeamTypes.RECEIVED_MY_TEAM_MEMBER, data: TestHelper.basicTeamMember}, + ])); + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.LEAVE_TEAM, data: {team_id: team.id, user_id: TestHelper.basicUser.id}, broadcast: {omit_users: null, user_id: '', channel_id: '', team_id: team.id}, seq: 35})); + + const {myMembers} = store.getState().entities.teams; + assert.ifError(myMembers[team.id]); + }); + + it('Websocket Handle User Added', async () => { + const user = {...TestHelper.fakeUser(), id: TestHelper.generateId()}; + store.dispatch({type: UserTypes.RECEIVED_PROFILE_IN_CHANNEL, data: {id: TestHelper.basicChannel.id, user_id: user.id}}); + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.USER_ADDED, data: {team_id: TestHelper.basicTeam.id, user_id: user.id}, broadcast: {omit_users: null, user_id: '', channel_id: TestHelper.basicChannel.id, team_id: ''}, seq: 42})); + + const entities = store.getState().entities; + const profilesInChannel = entities.users.profilesInChannel; + assert.ok(profilesInChannel[TestHelper.basicChannel.id].has(user.id)); + }); + + it('Websocket Handle User Removed', async () => { + const user = {...TestHelper.fakeUser(), id: TestHelper.generateId()}; + store.dispatch({type: UserTypes.RECEIVED_PROFILE_NOT_IN_CHANNEL, data: {id: TestHelper.basicChannel.id, user_id: user.id}}); + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.USER_REMOVED, data: {remover_id: TestHelper.basicUser.id, user_id: user.id}, broadcast: {omit_users: null, user_id: '', channel_id: TestHelper.basicChannel.id, team_id: ''}, seq: 42})); + + const state = store.getState(); + const entities = state.entities; + const profilesNotInChannel = entities.users.profilesNotInChannel; + + assert.ok(profilesNotInChannel[TestHelper.basicChannel.id].has(user.id)); + }); + + it('Websocket Handle User Removed when Current is Guest', async () => { + const basicGuestUser = TestHelper.fakeUserWithId(); + basicGuestUser.roles = 'system_guest'; + + const user = {...TestHelper.fakeUser(), id: TestHelper.generateId()}; + + // add user first + store.dispatch({type: UserTypes.RECEIVED_PROFILE_IN_CHANNEL, data: {id: TestHelper.basicChannel.id, user_id: user.id}}); + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.USER_ADDED, data: {team_id: TestHelper.basicTeam.id, user_id: user.id}, broadcast: {omit_users: null, user_id: '', channel_id: TestHelper.basicChannel.id, team_id: ''}, seq: 42})); + + assert.ok(store.getState().entities.users.profilesInChannel[TestHelper.basicChannel.id].has(user.id)); + + // remove user + store.dispatch({type: UserTypes.RECEIVED_PROFILE_NOT_IN_CHANNEL, data: {id: TestHelper.basicChannel.id, user_id: user.id}}); + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.USER_REMOVED, data: {remover_id: basicGuestUser.id, user_id: user.id}, broadcast: {omit_users: null, user_id: '', channel_id: TestHelper.basicChannel.id, team_id: ''}, seq: 42})); + + assert.ok(!store.getState().entities.users.profilesInChannel[TestHelper.basicChannel.id].has(user.id)); + }); + + it('Websocket Handle User Updated', async () => { + const user = {...TestHelper.fakeUser(), id: TestHelper.generateId()}; + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.USER_UPDATED, data: {user: {id: user.id, create_at: 1495570297229, update_at: 1508253268652, delete_at: 0, username: 'tim', auth_data: '', auth_service: '', email: 'tim@bladekick.com', nickname: '', first_name: 'tester4', last_name: '', position: '', roles: 'system_user', locale: 'en'}}, broadcast: {omit_users: null, user_id: '', channel_id: '', team_id: ''}, seq: 53})); + + store.subscribe(() => { + const state = store.getState(); + const entities = state.entities; + const profiles = entities.users.profiles; + + assert.strictEqual(profiles[user.id].first_name, 'tester4'); + }); + }); + + it('Websocket Handle Channel Member Updated', async () => { + const channelMember = TestHelper.basicChannelMember; + const mockStore = configureMockStore([thunk]); + const st = mockStore(initial_state); + await st.dispatch(Actions.init({websocketUrl: Client4.getUrl().replace(/^http:/, 'ws:')})); + channelMember.roles = 'channel_user channel_admin'; + const rolesToLoad = channelMember.roles.split(' '); + + nock(Client4.getRolesRoute()). + post('/names', JSON.stringify(rolesToLoad)). + reply(200, rolesToLoad); + + mockServer.emit('message', JSON.stringify({ + event: WebsocketEvents.CHANNEL_MEMBER_UPDATED, + data: { + channelMember: JSON.stringify(channelMember), + }, + })); + + await TestHelper.wait(300); + const storeActions = st.getActions(); + const batch = storeActions.find((a) => a.type === 'BATCH_WS_CHANNEL_MEMBER_UPDATE'); + expect(batch).not.toBeNull(); + const memberAction = batch.payload.find((a) => a.type === ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER); + expect(memberAction).not.toBeNull(); + const rolesActions = batch.payload.find((a) => a.type === RoleTypes.RECEIVED_ROLES); + expect(rolesActions).not.toBeNull(); + expect(rolesActions.data).toEqual(rolesToLoad); + }); + + it('Websocket Handle Channel Created', async () => { + const channelId = TestHelper.basicChannel.id; + const channel = {id: channelId, display_name: 'test', name: TestHelper.basicChannel.name}; + await store.dispatch(Actions.init({websocketUrl: Client4.getUrl().replace(/^http:/, 'ws:')})); + store.dispatch({type: ChannelTypes.RECEIVED_CHANNEL, data: channel}); + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.CHANNEL_CREATED, data: {channel_id: channelId, team_id: TestHelper.basicTeam.id}, broadcast: {omit_users: null, user_id: 't36kso9nwtdhbm8dbkd6g4eeby', channel_id: '', team_id: ''}, seq: 57})); + + await TestHelper.wait(300); + + const state = store.getState(); + const entities = state.entities; + const {channels} = entities.channels; + + assert.ok(channels[channel.id]); + }); + + it('Websocket Handle Channel Updated', async () => { + const channelName = 'Test name'; + const channelId = TestHelper.basicChannel.id; + + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.CHANNEL_UPDATED, data: {channel: `{"id":"${channelId}","create_at":1508253647983,"update_at":1508254198797,"delete_at":0,"team_id":"55pfercbm7bsmd11p5cjpgsbwr","type":"O","display_name":"${channelName}","name":"${TestHelper.basicChannel.name}","header":"header","purpose":"","last_post_at":1508253648004,"total_msg_count":0,"extra_update_at":1508253648001,"creator_id":"${TestHelper.basicUser.id}"}`}, broadcast: {omit_users: null, user_id: '', channel_id: channelId, team_id: ''}, seq: 62})); + + await TestHelper.wait(300); + + const state = store.getState(); + const entities = state.entities; + const {channels} = entities.channels; + + assert.strictEqual(channels[channelId].display_name, channelName); + }); + + it('Websocket Handle Channel Deleted', async () => { + const time = Date.now(); + await store.dispatch(TeamActions.selectTeam(TestHelper.basicTeam)); + await store.dispatch(ChannelActions.selectChannel(TestHelper.basicChannel.id)); + + store.dispatch({type: ChannelTypes.RECEIVED_CHANNEL, data: {id: TestHelper.generateId(), name: General.DEFAULT_CHANNEL, team_id: TestHelper.basicTeam.id, display_name: General.DEFAULT_CHANNEL}}); + store.dispatch({type: ChannelTypes.RECEIVED_CHANNEL, data: TestHelper.basicChannel}); + + nock(Client4.getUserRoute('me')). + get(`/teams/${TestHelper.basicTeam.id}/channels/members`). + reply(201, [{user_id: TestHelper.basicUser.id, channel_id: TestHelper.basicChannel.id}]); + + mockServer.emit('message', JSON.stringify({ + event: WebsocketEvents.CHANNEL_DELETED, + data: { + channel_id: TestHelper.basicChannel.id, + delete_at: time, + }, + broadcast: { + omit_users: null, + user_id: '', + channel_id: '', + team_id: TestHelper.basicTeam.id, + }, + seq: 68, + })); + + await TestHelper.wait(300); + + const state = store.getState(); + const entities = state.entities; + const {channels, currentChannelId} = entities.channels; + + assert.ok(channels[currentChannelId].name === General.DEFAULT_CHANNEL); + }); + + it('Websocket Handle Channel Unarchive', async () => { + await store.dispatch(TeamActions.selectTeam(TestHelper.basicTeam)); + await store.dispatch(ChannelActions.selectChannel(TestHelper.basicChannel.id)); + + store.dispatch({type: ChannelTypes.RECEIVED_CHANNEL, data: {id: TestHelper.generateId(), name: General.DEFAULT_CHANNEL, team_id: TestHelper.basicTeam.id, display_name: General.DEFAULT_CHANNEL}}); + store.dispatch({type: ChannelTypes.RECEIVED_CHANNEL, data: TestHelper.basicChannel}); + + nock(Client4.getUserRoute('me')). + get(`/teams/${TestHelper.basicTeam.id}/channels/members`). + reply(201, [{user_id: TestHelper.basicUser.id, channel_id: TestHelper.basicChannel.id}]); + + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.CHANNEL_UNARCHIVE, data: {channel_id: TestHelper.basicChannel.id}, broadcast: {omit_users: null, user_id: '', channel_id: '', team_id: TestHelper.basicTeam.id}, seq: 68})); + + await TestHelper.wait(300); + + const state = store.getState(); + const entities = state.entities; + const {channels, currentChannelId} = entities.channels; + + assert.ok(channels[currentChannelId].delete_at === 0); + }); + + it('Websocket Handle Direct Channel', async () => { + const channel = {id: TestHelper.generateId(), name: TestHelper.basicUser.id + '__' + TestHelper.generateId(), type: 'D'}; + + nock(Client4.getChannelsRoute()). + get(`/${channel.id}/members/me`). + reply(201, {user_id: TestHelper.basicUser.id, channel_id: channel.id}); + + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.DIRECT_ADDED, data: {teammate_id: 'btaxe5msnpnqurayosn5p8twuw'}, broadcast: {omit_users: null, user_id: '', channel_id: channel.id, team_id: ''}, seq: 2})); + store.dispatch({type: ChannelTypes.RECEIVED_CHANNEL, data: channel}); + + await TestHelper.wait(300); + + const {channels} = store.getState().entities.channels; + assert.ok(Object.keys(channels).length); + }); + + it('Websocket handle user added to team', async () => { + const team = {id: TestHelper.generateId()}; + + nock(Client4.getBaseRoute()). + get(`/teams/${team.id}`). + reply(200, team); + + nock(Client4.getBaseRoute()). + get('/users/me/teams/unread'). + reply(200, [{team_id: team.id, msg_count: 0, mention_count: 0}]); + + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.ADDED_TO_TEAM, data: {team_id: team.id, user_id: TestHelper.basicUser.id}, broadcast: {omit_users: null, user_id: TestHelper.basicUser.id, channel_id: '', team_id: ''}, seq: 2})); + + await TestHelper.wait(300); + + const {teams, myMembers} = store.getState().entities.teams; + assert.ok(teams[team.id]); + assert.ok(myMembers[team.id]); + + const member = myMembers[team.id]; + assert.ok(member.hasOwnProperty('mention_count')); + }); + + it('Websocket handle emoji added', async () => { + const created = {id: '1mmgakhhupfgfm8oug6pooc5no'}; + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.EMOJI_ADDED, data: {emoji: `{"id":"1mmgakhhupfgfm8oug6pooc5no","create_at":1508263941321,"update_at":1508263941321,"delete_at":0,"creator_id":"t36kso9nwtdhbm8dbkd6g4eeby","name":"${TestHelper.generateId()}"}`}, broadcast: {omit_users: null, user_id: '', channel_id: '', team_id: ''}, seq: 2})); + + await TestHelper.wait(200); + + const state = store.getState(); + + const emojis = state.entities.emojis.customEmoji; + assert.ok(emojis); + assert.ok(emojis[created.id]); + }); + + it('handle license changed', async () => { + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.LICENSE_CHANGED, data: {license: {IsLicensed: 'true'}}})); + + await TestHelper.wait(200); + + const state = store.getState(); + + const license = state.entities.general.license; + assert.ok(license); + assert.ok(license.IsLicensed); + }); + + it('handle config changed', async () => { + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.CONFIG_CHANGED, data: {config: {EnableCustomEmoji: 'true', EnableLinkPreviews: 'false'}}})); + + await TestHelper.wait(200); + + const state = store.getState(); + + const config = state.entities.general.config; + assert.ok(config); + assert.ok(config.EnableCustomEmoji === 'true'); + assert.ok(config.EnableLinkPreviews === 'false'); + }); + + it('handle open dialog', async () => { + mockServer.emit('message', JSON.stringify({event: WebsocketEvents.OPEN_DIALOG, data: {dialog: JSON.stringify({url: 'someurl', trigger_id: 'sometriggerid', dialog: {}})}})); + + await TestHelper.wait(200); + + const state = store.getState(); + + const dialog = state.entities.integrations.dialog; + assert.ok(dialog); + assert.ok(dialog.url === 'someurl'); + assert.ok(dialog.trigger_id === 'sometriggerid'); + assert.ok(dialog.dialog); + }); +}); + +describe('Actions.Websocket doReconnect', () => { + const mockStore = configureMockStore([thunk]); + const me = TestHelper.fakeUserWithId(); + const team = TestHelper.fakeTeamWithId(); + const teamMember = TestHelper.fakeTeamMember(me.id, team.id); + const channel1 = TestHelper.fakeChannelWithId(team.id); + const channel2 = TestHelper.fakeChannelWithId(team.id); + const cMember1 = TestHelper.fakeChannelMember(me.id, channel1.id); + const cMember2 = TestHelper.fakeChannelMember(me.id, channel2.id); + + const currentTeamId = team.id; + const currentUserId = me.id; + const currentChannelId = channel1.id; + + const initialState = { + entities: { + general: { + config: {}, + }, + teams: { + currentTeamId, + myMembers: { + [currentTeamId]: teamMember, + }, + teams: { + [currentTeamId]: team, + }, + }, + channels: { + currentChannelId, + channels: { + currentChannelId: channel1, + }, + }, + users: { + currentUserId, + profiles: { + [me.id]: me, + }, + }, + preferences: { + myPreferences: {}, + }, + posts: { + posts: {}, + postsInChannel: {}, + }, + }, + websocket: { + connected: false, + lastConnectAt: 0, + lastDisconnectAt: 0, + }, + }; + + beforeAll(async () => { + return TestHelper.initBasic(Client4); + }); + + beforeEach(() => { + nock(Client4.getBaseRoute()). + get('/users/me'). + reply(200, me); + + nock(Client4.getUserRoute('me')). + get('/teams'). + reply(200, [team]); + + nock(Client4.getUserRoute('me')). + get('/teams/unread'). + reply(200, [{id: team.id, msg_count: 0, mention_count: 0}]); + + nock(Client4.getBaseRoute()). + get('/users/me/preferences'). + reply(200, []); + + nock(Client4.getUserRoute('me')). + get(`/teams/${team.id}/channels/members`). + reply(200, [cMember1, cMember2]); + + nock(Client4.getChannelRoute(channel1.id)). + get(`/posts?page=0&per_page=${Posts.POST_CHUNK_SIZE}`). + reply(200, { + posts: { + post1: {id: 'post1', create_at: 0, message: 'hey'}, + }, + order: ['post1'], + }); + }); + + afterAll(async () => { + Actions.close()(); + await TestHelper.tearDown(); + }); + + it('handle doReconnect', async () => { + const state = {...initialState}; + const testStore = await mockStore(state); + const timestamp = 1000; + const expectedActions = [ + GeneralTypes.WEBSOCKET_SUCCESS, + 'BATCH_WS_RECONNECT', + 'BATCH_GET_POSTS', + ]; + + mockConfigRequest(); + mockTeamMemberRequest([teamMember]); + mockChanelsRequest(team.id, [channel1, channel2]); + + let rolesToLoad = Array.from(new Set(me.roles.split(' '). + concat(teamMember.roles.split(' ')))); + mockRolesRequest(rolesToLoad); + + rolesToLoad = Array.from(new Set(cMember1.roles.split(' '). + concat(cMember2.roles.split(' ')))); + mockRolesRequest(rolesToLoad); + + await testStore.dispatch(Actions.doReconnect(timestamp)); + await TestHelper.wait(300); + const actionTypes = testStore.getActions().map((a) => a.type); + expect(actionTypes).toEqual(expectedActions); + }); + + it('handle doReconnect after the current channel was archived or the user left it', async () => { + const state = { + ...initialState, + entities: { + ...initialState.entities, + channels: { + ...initialState.entities.channels, + currentChannelId: 'channel-3', + }, + }, + }; + const testStore = await mockStore(state); + const timestamp = 1000; + const expectedActions = [ + GeneralTypes.WEBSOCKET_SUCCESS, + 'BATCH_WS_RECONNECT', + ]; + const expectedMissingActions = [ + 'BATCH_GET_POSTS', + ]; + + mockConfigRequest(); + mockTeamMemberRequest([teamMember]); + mockChanelsRequest(team.id, [channel1, channel2]); + + let rolesToLoad = Array.from(new Set(me.roles.split(' '). + concat(teamMember.roles.split(' ')))); + mockRolesRequest(rolesToLoad); + + rolesToLoad = Array.from(new Set(cMember1.roles.split(' '). + concat(cMember2.roles.split(' ')))); + mockRolesRequest(rolesToLoad); + + await testStore.dispatch(Actions.doReconnect(timestamp)); + await TestHelper.wait(300); + + const actions = testStore.getActions().map((a) => a.type); + + expect(actions).toEqual(expect.arrayContaining(expectedActions)); + expect(actions).not.toEqual(expect.arrayContaining(expectedMissingActions)); + }); + + it('handle doReconnect after the current channel was archived and setting is on', async () => { + const archived = { + ...channel1, + delete_at: 123, + }; + const state = { + ...initialState, + channels: { + currentChannelId, + channels: { + currentChannelId: archived, + }, + }, + }; + const testStore = await mockStore(state); + const timestamp = 1000; + const expectedActions = [ + GeneralTypes.WEBSOCKET_SUCCESS, + 'BATCH_WS_RECONNECT', + ]; + + mockConfigRequest({ExperimentalViewArchivedChannels: 'true'}); + mockTeamMemberRequest([teamMember]); + mockChanelsRequest(team.id, [archived, channel2]); + + let rolesToLoad = Array.from(new Set(me.roles.split(' '). + concat(teamMember.roles.split(' ')))); + mockRolesRequest(rolesToLoad); + + rolesToLoad = Array.from(new Set(cMember1.roles.split(' '). + concat(cMember2.roles.split(' ')))); + mockRolesRequest(rolesToLoad); + + await testStore.dispatch(Actions.doReconnect(timestamp)); + await TestHelper.wait(300); + + const actions = testStore.getActions().map((a) => a.type); + expect(actions).toEqual(expect.arrayContaining(expectedActions)); + }); + + it('handle doReconnect after the current channel was archived and setting is off', async () => { + const archived = { + ...channel1, + delete_at: 123, + }; + + const state = { + ...initialState, + channels: { + currentChannelId, + channels: { + currentChannelId: archived, + }, + }, + }; + const testStore = await mockStore(state); + const timestamp = 1000; + const expectedActions = [ + GeneralTypes.WEBSOCKET_SUCCESS, + 'BATCH_WS_RECONNECT', + ]; + const expectedMissingActions = [ + 'BATCH_GET_POSTS', + ]; + + mockConfigRequest({ExperimentalViewArchivedChannels: 'false'}); + mockTeamMemberRequest([teamMember]); + mockChanelsRequest(team.id, [archived, channel2]); + + let rolesToLoad = Array.from(new Set(me.roles.split(' '). + concat(teamMember.roles.split(' ')))); + mockRolesRequest(rolesToLoad); + + rolesToLoad = Array.from(new Set(cMember1.roles.split(' '). + concat(cMember2.roles.split(' ')))); + mockRolesRequest(rolesToLoad); + + await testStore.dispatch(Actions.doReconnect(timestamp)); + await TestHelper.wait(300); + + const actions = testStore.getActions().map((a) => a.type); + expect(actions).toEqual(expectedActions); + expect(actions).not.toEqual(expect.arrayContaining(expectedMissingActions)); + }); + + it('handle doReconnect after user left current team', async () => { + const state = {...initialState}; + state.entities.teams.myMembers = {}; + const testStore = await mockStore(state); + const timestamp = 1000; + const expectedActions = [ + GeneralTypes.WEBSOCKET_SUCCESS, + 'BATCH_WS_LEAVE_TEAM', + 'BATCH_WS_RECONNECT', + ]; + const expectedMissingActions = [ + 'BATCH_GET_POSTS', + ]; + + mockConfigRequest(); + mockTeamMemberRequest([]); + mockChanelsRequest(team.id, [channel1, channel2]); + + let rolesToLoad = me.roles.split(' '); + mockRolesRequest(rolesToLoad); + + rolesToLoad = Array.from(new Set(cMember1.roles.split(' '). + concat(cMember2.roles.split(' ')))); + mockRolesRequest(rolesToLoad); + + await testStore.dispatch(Actions.doReconnect(timestamp)); + await TestHelper.wait(300); + const actions = testStore.getActions().map((a) => a.type); + expect(actions).toEqual(expectedActions); + expect(actions).not.toEqual(expect.arrayContaining(expectedMissingActions)); + }); +}); + +describe('Actions.Websocket notVisibleUsersActions', () => { + const mockStore = configureMockStore([thunk]); + + const channel1 = TestHelper.fakeChannelWithId(''); + const channel2 = TestHelper.fakeChannelWithId(''); + + const me = TestHelper.fakeUserWithId(); + const user = TestHelper.fakeUserWithId(); + const user2 = TestHelper.fakeUserWithId(); + const user3 = TestHelper.fakeUserWithId(); + const user4 = TestHelper.fakeUserWithId(); + const user5 = TestHelper.fakeUserWithId(); + + it('should do nothing if the known users and the profiles list are the same', () => { + const membersInChannel = { + [channel1.id]: { + [user.id]: {channel_id: channel1.id, user_id: user.id}, + [user2.id]: {channel_id: channel1.id, user_id: user2.id}, + }, + [channel2.id]: { + [user3.id]: {channel_id: channel2.id, user_id: user3.id}, + }, + }; + + const profiles = { + [me.id]: me, + [user.id]: user, + [user2.id]: user2, + [user3.id]: user3, + }; + + const state = { + entities: { + channels: { + membersInChannel, + }, + users: { + currentUserId: me.id, + profiles, + }, + }, + }; + + const actions = Actions.notVisibleUsersActions(state); + expect(actions.length).toEqual(0); + }); + + it('should do nothing if there are known users in my memberships but not in the profiles list', () => { + const membersInChannel = { + [channel1.id]: { + [user.id]: {channel_id: channel1.id, user_id: user.id}, + [user2.id]: {channel_id: channel1.id, user_id: user2.id}, + }, + [channel2.id]: { + [user3.id]: {channel_id: channel2.id, user_id: user3.id}, + }, + }; + + const profiles = { + [me.id]: me, + [user3.id]: user3, + }; + + const state = { + entities: { + channels: { + membersInChannel, + }, + users: { + currentUserId: me.id, + profiles, + }, + }, + }; + + const actions = Actions.notVisibleUsersActions(state); + expect(actions.length).toEqual(0); + }); + + it('should remove the users if there are unknown users in the profiles list', async () => { + const membersInChannel = { + [channel1.id]: { + [user.id]: {channel_id: channel1.id, user_id: user.id}, + }, + [channel2.id]: { + [user3.id]: {channel_id: channel2.id, user_id: user3.id}, + }, + }; + + const profiles = { + [me.id]: me, + [user.id]: user, + [user2.id]: user2, + [user3.id]: user3, + [user4.id]: user4, + [user5.id]: user5, + }; + + const state = { + entities: { + channels: { + membersInChannel, + }, + users: { + currentUserId: me.id, + profiles, + }, + }, + }; + + const expectedAction = [ + {type: UserTypes.PROFILE_NO_LONGER_VISIBLE, data: {user_id: user2.id}}, + {type: UserTypes.PROFILE_NO_LONGER_VISIBLE, data: {user_id: user4.id}}, + {type: UserTypes.PROFILE_NO_LONGER_VISIBLE, data: {user_id: user5.id}}, + ]; + const actions = Actions.notVisibleUsersActions(state); + expect(actions.length).toEqual(3); + expect(actions).toEqual(expectedAction); + }); +}); + +describe('Actions.Websocket handleUserTypingEvent', () => { + const mockStore = configureMockStore([thunk]); + + const currentUserId = 'user-id'; + const otherUserId = 'other-user-id'; + const currentChannelId = 'channel-id'; + const otherChannelId = 'other-channel-id'; + + const initialState = { + entities: { + general: { + config: {}, + }, + channels: { + currentChannelId, + channels: { + currentChannelId: { + id: currentChannelId, + name: 'channel', + }, + }, + }, + users: { + currentUserId, + profiles: { + [currentUserId]: {}, + [otherUserId]: {}, + }, + statuses: { + [currentUserId]: General.ONLINE, + [otherUserId]: General.OFFLINE, + }, + }, + preferences: { + myPreferences: {}, + }, + }, + }; + + it('dispatches actions for current channel if other user is typing', async () => { + const state = {...initialState}; + const testStore = await mockStore(state); + const msg = {broadcast: {channel_id: currentChannelId}, data: {parent_id: 'parent-id', user_id: otherUserId}}; + + nock(Client4.getUsersRoute()). + post('/status/ids', JSON.stringify([otherUserId])). + reply(200, ['away']); + + const expectedActionsTypes = [ + WebsocketEvents.TYPING, + UserTypes.RECEIVED_STATUSES, + ]; + + await testStore.dispatch(Actions.handleUserTypingEvent(msg)); + await TestHelper.wait(300); + const actionTypes = testStore.getActions().map((action) => action.type); + expect(actionTypes).toEqual(expectedActionsTypes); + }); + + it('does not dispatch actions for non current channel', async () => { + const state = {...initialState}; + const testStore = await mockStore(state); + const msg = {broadcast: {channel_id: otherChannelId}, data: {parent_id: 'parent-id', user_id: otherUserId}}; + + const expectedActionsTypes = []; + + await testStore.dispatch(Actions.handleUserTypingEvent(msg)); + const actionTypes = testStore.getActions().map((action) => action.type); + expect(actionTypes).toEqual(expectedActionsTypes); + }); +}); \ No newline at end of file diff --git a/app/actions/websocket.ts b/app/actions/websocket.ts new file mode 100644 index 000000000..c0d815684 --- /dev/null +++ b/app/actions/websocket.ts @@ -0,0 +1,1155 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Client4} from 'mattermost-redux/client'; +import websocketClient from '@websocket'; + +import {ChannelTypes, GeneralTypes, EmojiTypes, PostTypes, PreferenceTypes, TeamTypes, UserTypes, RoleTypes, IntegrationTypes} from 'mattermost-redux/action_types'; +import {General, Preferences} from 'mattermost-redux/constants'; +import { + getAllChannels, + getChannel, + getChannelsNameMapInTeam, + getCurrentChannelId, + getMyChannelMember as selectMyChannelMember, + getRedirectChannelNameForTeam, + getCurrentChannelStats, + getChannelMembersInChannels, + isManuallyUnread, + getKnownUsers, +} from 'mattermost-redux/selectors/entities/channels'; +import {getConfig} from 'mattermost-redux/selectors/entities/general'; +import {getAllPosts, getPost as selectPost} from 'mattermost-redux/selectors/entities/posts'; +import {getCurrentTeamId, getTeams as getTeamsSelector} from 'mattermost-redux/selectors/entities/teams'; +import {getCurrentUser, getCurrentUserId, getUsers, getUserStatuses} from 'mattermost-redux/selectors/entities/users'; +import {getChannelByName, getUserIdFromChannelName} from 'mattermost-redux/utils/channel_utils'; +import EventEmitter from 'mattermost-redux/utils/event_emitter'; +import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers'; +import {isGuest, removeUserFromList} from 'mattermost-redux/utils/user_utils'; +import {isFromWebhook, isSystemMessage, shouldIgnorePost} from 'mattermost-redux/utils/post_utils'; + +import {DispatchFunc, GenericAction, GetStateFunc, batchActions} from 'mattermost-redux/types/actions'; + +import {getCustomEmojiForReaction, getUnreadPostData, postDeleted, receivedNewPost, receivedPost} from 'mattermost-redux/actions/posts'; +import {markChannelAsRead} from 'mattermost-redux/actions/channels'; +import {getProfilesByIds, getStatusesByIds} from 'mattermost-redux/actions/users'; +import {Channel, ChannelMembership} from 'mattermost-redux/types/channels'; +import {PreferenceType} from 'mattermost-redux/types/preferences'; +import {TeamMembership} from 'mattermost-redux/types/teams'; +import {Dictionary} from 'mattermost-redux/types/utilities'; + +import {WebsocketEvents} from '@constants'; +import { + fetchMyChannel, + fetchMyChannelMember, + fetchChannelAndMyMember, + getAddedDmUsersIfNecessary, + makeDirectChannelVisibleIfNecessary, + makeGroupMessageVisibleIfNecessary, + markChannelAsUnread, +} from '@actions/helpers/channels'; +import {loadChannelsForTeam, markAsViewedAndReadBatch} from '@actions/views/channel'; +import {getPost, getPosts, getPostsAdditionalDataBatch, getPostThread} from '@actions/views/post'; +import {getMe, loadMe} from '@actions/views/user'; +import {GlobalState} from 'mattermost-redux/types/store'; + +export type WebsocketBroadcast = { + omit_users: Dictionary; + user_id: string; + channel_id: string; + team_id: string; +} + +export type WebSocketMessage = { + event: string; + data: any; + broadcast: WebsocketBroadcast; + seq: number; +} + +export function init(additionalOptions: any = {}) { + return async (dispatch: DispatchFunc, getState: GetStateFunc) => { + const config = getConfig(getState()); + let connUrl = additionalOptions.websocketUrl || config.WebsocketURL || Client4.getUrl(); + const authToken = Client4.getToken(); + + connUrl += `${Client4.getUrlVersion()}/websocket`; + websocketClient.setFirstConnectCallback(() => dispatch(handleFirstConnect())); + websocketClient.setEventCallback((evt: WebSocketMessage) => dispatch(handleEvent(evt))); + websocketClient.setReconnectCallback(() => dispatch(handleReconnect())); + websocketClient.setCloseCallback((connectFailCount: number) => dispatch(handleClose(connectFailCount))); + + const websocketOpts = { + connectionUrl: connUrl, + ...additionalOptions, + }; + + return websocketClient.initialize(authToken, websocketOpts); + }; +} + +let reconnect = false; +export function close(shouldReconnect = false) { + reconnect = shouldReconnect; + websocketClient.close(true); + + return { + type: GeneralTypes.WEBSOCKET_CLOSED, + timestamp: Date.now(), + data: null, + }; +} + +export function doFirstConnect(now: number) { + return async (dispatch: DispatchFunc, getState: GetStateFunc) => { + const state = getState(); + const {lastDisconnectAt} = state.websocket; + const actions: Array = [{ + type: GeneralTypes.WEBSOCKET_SUCCESS, + timestamp: now, + data: null, + }]; + + if (isMinimumServerVersion(Client4.getServerVersion(), 5, 14) && lastDisconnectAt) { + const currentUserId = getCurrentUserId(state); + const users = getUsers(state); + const userIds = Object.keys(users); + const userUpdates = await Client4.getProfilesByIds(userIds, {since: lastDisconnectAt}); + + if (userUpdates.length) { + removeUserFromList(currentUserId, userUpdates); + actions.push({ + type: UserTypes.RECEIVED_PROFILES_LIST, + data: userUpdates, + }); + } + } + + dispatch(batchActions(actions, 'BATCH_WS_CONNCET')); + + return {data: true}; + }; +} + +export function doReconnect(now: number) { + return async (dispatch: DispatchFunc, getState: GetStateFunc) => { + const state = getState(); + const currentTeamId = getCurrentTeamId(state); + const currentChannelId = getCurrentChannelId(state); + const currentUserId = getCurrentUserId(state); + const currentUser = getCurrentUser(state); + const users = getUsers(state); + const {lastDisconnectAt} = state.websocket; + const actions: Array = []; + + dispatch({ + type: GeneralTypes.WEBSOCKET_SUCCESS, + timestamp: now, + data: null, + }); + + try { + const {data: me}: any = await dispatch(loadMe(null, null, true)); + + if (!me.error) { + const roles = []; + + if (me.roles?.length) { + roles.push(...me.roles); + } + + actions.push({ + type: PreferenceTypes.RECEIVED_ALL_PREFERENCES, + data: me.preferences, + }, { + type: TeamTypes.RECEIVED_MY_TEAM_UNREADS, + data: me.teamUnreads, + }, { + type: TeamTypes.RECEIVED_TEAMS_LIST, + data: me.teams, + }, { + type: TeamTypes.RECEIVED_MY_TEAM_MEMBERS, + data: me.teamMembers, + }); + + const currentTeamMembership = me.teamMembers.find((tm: TeamMembership) => tm.team_id === currentTeamId && tm.delete_at === 0); + + if (currentTeamMembership) { + const {data: myData}: any = await dispatch(loadChannelsForTeam(currentTeamId, true)); + + if (myData?.channels && myData?.channelMembers) { + actions.push({ + type: ChannelTypes.RECEIVED_MY_CHANNELS_WITH_MEMBERS, + data: myData, + }); + + const stillMemberOfCurrentChannel = myData.channelMembers.find((cm: ChannelMembership) => cm.channel_id === currentChannelId); + + const channelStillExists = myData.channels.find((c: Channel) => c.id === currentChannelId); + const config = me.config || getConfig(getState()); + const viewArchivedChannels = config.ExperimentalViewArchivedChannels === 'true'; + if (!stillMemberOfCurrentChannel || !channelStillExists || (!viewArchivedChannels && channelStillExists.delete_at !== 0)) { + EventEmitter.emit(General.SWITCH_TO_DEFAULT_CHANNEL, currentTeamId); + } else { + dispatch(getPosts(currentChannelId)); + } + } + + if (myData.roles?.length) { + roles.push(...myData.roles); + } + } else { + // If the user is no longer a member of this team when reconnecting + const newMsg = { + data: { + user_id: currentUserId, + team_id: currentTeamId, + }, + }; + dispatch(handleLeaveTeamEvent(newMsg)); + } + + if (roles.length) { + actions.push({ + type: RoleTypes.RECEIVED_ROLES, + data: roles, + }); + } + + if (isMinimumServerVersion(Client4.getServerVersion(), 5, 14) && lastDisconnectAt) { + const userIds = Object.keys(users); + const userUpdates = await Client4.getProfilesByIds(userIds, {since: lastDisconnectAt}); + + if (userUpdates.length) { + removeUserFromList(currentUserId, userUpdates); + actions.push({ + type: UserTypes.RECEIVED_PROFILES_LIST, + data: userUpdates, + }); + } + } + + if (actions.length) { + dispatch(batchActions(actions, 'BATCH_WS_RECONNECT')); + } + } + } catch (e) { + // do nothing + } + + return {data: true}; + }; +} + +function handleFirstConnect() { + return (dispatch: DispatchFunc) => { + const now = Date.now(); + + if (reconnect) { + reconnect = false; + return dispatch(doReconnect(now)); + } + return dispatch(doFirstConnect(now)); + }; +} + +function handleReconnect() { + return (dispatch: DispatchFunc) => { + return dispatch(doReconnect(Date.now())); + }; +} + +function handleClose(connectFailCount: number) { + return { + type: GeneralTypes.WEBSOCKET_FAILURE, + error: connectFailCount, + data: null, + timestamp: Date.now(), + }; +} + +function handleEvent(msg: WebSocketMessage) { + return (dispatch: DispatchFunc) => { + switch (msg.event) { + case WebsocketEvents.POSTED: + case WebsocketEvents.EPHEMERAL_MESSAGE: + return dispatch(handleNewPostEvent(msg)); + case WebsocketEvents.POST_EDITED: + return dispatch(handlePostEdited(msg)); + case WebsocketEvents.POST_DELETED: + return dispatch(handlePostDeleted(msg)); + case WebsocketEvents.POST_UNREAD: + return dispatch(handlePostUnread(msg)); + case WebsocketEvents.LEAVE_TEAM: + return dispatch(handleLeaveTeamEvent(msg)); + case WebsocketEvents.UPDATE_TEAM: + return dispatch(handleUpdateTeamEvent(msg)); + case WebsocketEvents.ADDED_TO_TEAM: + return dispatch(handleTeamAddedEvent(msg)); + case WebsocketEvents.USER_ADDED: + return dispatch(handleUserAddedEvent(msg)); + case WebsocketEvents.USER_REMOVED: + return dispatch(handleUserRemovedEvent(msg)); + case WebsocketEvents.USER_UPDATED: + return dispatch(handleUserUpdatedEvent(msg)); + case WebsocketEvents.ROLE_ADDED: + return dispatch(handleRoleAddedEvent(msg)); + case WebsocketEvents.ROLE_REMOVED: + return dispatch(handleRoleRemovedEvent(msg)); + case WebsocketEvents.ROLE_UPDATED: + return dispatch(handleRoleUpdatedEvent(msg)); + case WebsocketEvents.USER_ROLE_UPDATED: + return dispatch(handleUserRoleUpdated(msg)); + case WebsocketEvents.CHANNEL_CREATED: + return dispatch(handleChannelCreatedEvent(msg)); + case WebsocketEvents.CHANNEL_DELETED: + return dispatch(handleChannelDeletedEvent(msg)); + case WebsocketEvents.CHANNEL_UNARCHIVED: + return dispatch(handleChannelUnarchiveEvent(msg)); + case WebsocketEvents.CHANNEL_UPDATED: + return dispatch(handleChannelUpdatedEvent(msg)); + case WebsocketEvents.CHANNEL_CONVERTED: + return dispatch(handleChannelConvertedEvent(msg)); + case WebsocketEvents.CHANNEL_VIEWED: + return dispatch(handleChannelViewedEvent(msg)); + case WebsocketEvents.CHANNEL_MEMBER_UPDATED: + return dispatch(handleChannelMemberUpdatedEvent(msg)); + case WebsocketEvents.CHANNEL_SCHEME_UPDATED: + return dispatch(handleChannelSchemeUpdatedEvent(msg)); + case WebsocketEvents.DIRECT_ADDED: + return dispatch(handleDirectAddedEvent(msg)); + case WebsocketEvents.PREFERENCE_CHANGED: + return dispatch(handlePreferenceChangedEvent(msg)); + case WebsocketEvents.PREFERENCES_CHANGED: + return dispatch(handlePreferencesChangedEvent(msg)); + case WebsocketEvents.PREFERENCES_DELETED: + return dispatch(handlePreferencesDeletedEvent(msg)); + case WebsocketEvents.STATUS_CHANGED: + return dispatch(handleStatusChangedEvent(msg)); + case WebsocketEvents.TYPING: + return dispatch(handleUserTypingEvent(msg)); + case WebsocketEvents.HELLO: + handleHelloEvent(msg); + break; + case WebsocketEvents.REACTION_ADDED: + return dispatch(handleReactionAddedEvent(msg)); + case WebsocketEvents.REACTION_REMOVED: + return dispatch(handleReactionRemovedEvent(msg)); + case WebsocketEvents.EMOJI_ADDED: + return dispatch(handleAddEmoji(msg)); + case WebsocketEvents.LICENSE_CHANGED: + return dispatch(handleLicenseChangedEvent(msg)); + case WebsocketEvents.CONFIG_CHANGED: + return dispatch(handleConfigChangedEvent(msg)); + case WebsocketEvents.OPEN_DIALOG: + return dispatch(handleOpenDialogEvent(msg)); + } + + return {data: true}; + }; +} + +function handleNewPostEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc, getState: GetStateFunc) => { + const state = getState(); + const currentChannelId = getCurrentChannelId(state); + const post = JSON.parse(msg.data.post); + const actions: Array = []; + + const exists = selectPost(state, post.pending_post_id); + + if (!exists) { + if (getCurrentChannelId(state) === post.channel_id) { + EventEmitter.emit(WebsocketEvents.INCREASE_POST_VISIBILITY_BY_ONE); + } + + const myChannel = getChannel(state, post.channel_id); + if (!myChannel) { + const channel = await fetchMyChannel(post.channel_id); + if (channel.data) { + actions.push({ + type: ChannelTypes.RECEIVED_CHANNEL, + data: channel.data, + }); + } + } + + const myChannelMember = selectMyChannelMember(state, post.channel_id); + if (!myChannelMember) { + const member = await fetchMyChannelMember(post.channel_id); + if (member.data) { + actions.push({ + type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER, + data: member.data, + }); + } + } + + actions.push(receivedNewPost(post)); + + // If we don't have the thread for this post, fetch it from the server + // and include the actions in the batch + if (post.root_id) { + const rootPost = selectPost(state, post.root_id); + + if (!rootPost) { + const thread: any = await dispatch(getPostThread(post.root_id, true)); + if (thread.data?.length) { + actions.push(...thread.data); + } + } + } + + if (post.channel_id === currentChannelId) { + const id = post.channel_id + post.root_id; + const {typing} = state.entities; + + if (typing[id]) { + actions.push({ + type: WebsocketEvents.STOP_TYPING, + data: { + id, + userId: post.user_id, + now: Date.now(), + }, + }); + } + } + + // Fetch and batch additional post data + const additional: any = await dispatch(getPostsAdditionalDataBatch([post])); + if (additional.data.length) { + actions.push(...additional.data); + } + + if (msg.data.channel_type === General.DM_CHANNEL) { + const currentUserId = getCurrentUserId(state); + const otherUserId = getUserIdFromChannelName(currentUserId, msg.data.channel_name); + const dmAction = makeDirectChannelVisibleIfNecessary(state, otherUserId); + if (dmAction) { + actions.push(dmAction); + } + } else if (msg.data.channel_type === General.GM_CHANNEL) { + const gmActions = await makeGroupMessageVisibleIfNecessary(state, post.channel_id); + if (gmActions) { + actions.push(...gmActions); + } + } + + if (!shouldIgnorePost(post)) { + let markAsRead = false; + let markAsReadOnServer = false; + + if (!isManuallyUnread(state, post.channel_id)) { + if ( + post.user_id === getCurrentUserId(state) && + !isSystemMessage(post) && + !isFromWebhook(post) + ) { + markAsRead = true; + markAsReadOnServer = false; + } else if (post.channel_id === currentChannelId) { + markAsRead = true; + markAsReadOnServer = true; + } + } + + if (markAsRead) { + const readActions = markAsViewedAndReadBatch(state, post.channel_id, undefined, markAsReadOnServer); + actions.push(...readActions); + } else { + const unreadActions = markChannelAsUnread(state, msg.data.team_id, post.channel_id, msg.data.mentions); + actions.push(...unreadActions); + } + } + + dispatch(batchActions(actions, 'BATCH_WS_NEW_POST')); + } + + return {data: true}; + }; +} + +function handlePostEdited(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc) => { + const data = JSON.parse(msg.data.post); + const actions = [receivedPost(data)]; + + const additional: any = await dispatch(getPostsAdditionalDataBatch([data])); + if (additional.data.length) { + actions.push(...additional.data); + } + + dispatch(batchActions(actions, 'BATCH_WS_POST_EDITED')); + return {data: true}; + }; +} + +function handlePostDeleted(msg: WebSocketMessage) { + const data = JSON.parse(msg.data.post); + + return postDeleted(data); +} + +function handlePostUnread(msg: WebSocketMessage) { + return (dispatch: DispatchFunc, getState: GetStateFunc) => { + const state = getState(); + const manual = isManuallyUnread(state, msg.broadcast.channel_id); + + if (!manual) { + const member = selectMyChannelMember(state, msg.broadcast.channel_id); + const delta = member ? member.msg_count - msg.data.msg_count : msg.data.msg_count; + const info = { + ...msg.data, + user_id: msg.broadcast.user_id, + team_id: msg.broadcast.team_id, + channel_id: msg.broadcast.channel_id, + deltaMsgs: delta, + }; + const data = getUnreadPostData(info, state); + dispatch({ + type: ChannelTypes.POST_UNREAD_SUCCESS, + data, + }); + return {data}; + } + + return {data: null}; + }; +} + +function handleLeaveTeamEvent(msg: Partial) { + return (dispatch: DispatchFunc, getState: GetStateFunc) => { + const state = getState(); + const teams = getTeamsSelector(state); + const currentTeamId = getCurrentTeamId(state); + const currentUser = getCurrentUser(state); + + if (currentUser.id === msg.data.user_id) { + const actions: Array = [{type: TeamTypes.LEAVE_TEAM, data: teams[msg.data.team_id]}]; + if (isGuest(currentUser.roles)) { + const notVisible = notVisibleUsersActions(state); + if (notVisible.length) { + actions.push(...notVisible); + } + } + dispatch(batchActions(actions, 'BATCH_WS_LEAVE_TEAM')); + + // if they are on the team being removed deselect the current team and channel + if (currentTeamId === msg.data.team_id) { + EventEmitter.emit('leave_team'); + } + } + return {data: true}; + }; +} + +function handleUpdateTeamEvent(msg: WebSocketMessage) { + return { + type: TeamTypes.UPDATED_TEAM, + data: JSON.parse(msg.data.team), + }; +} + +function handleTeamAddedEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc) => { + try { + const [team, teamUnreads] = await Promise.all([ + Client4.getTeam(msg.data.team_id), + Client4.getMyTeamUnreads(), + ]); + + const actions = [{ + type: TeamTypes.RECEIVED_TEAM, + data: team, + }, { + type: TeamTypes.RECEIVED_MY_TEAM_UNREADS, + data: teamUnreads, + }]; + + dispatch(batchActions(actions, 'BATCH_WS_TEAM_ADDED')); + } catch { + // do nothing + } + + return {data: true}; + }; +} + +function handleUserRoleUpdated(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc) => { + try { + const roles = msg.data.roles.split(' '); + const data = await Client4.getRolesByNames(roles); + + dispatch({ + type: RoleTypes.RECEIVED_ROLES, + data: data.roles, + }); + } catch { + // do nothing + } + + return {data: true}; + }; +} + +function handleUserAddedEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc, getState: GetStateFunc) => { + try { + const state = getState(); + const currentChannelId = getCurrentChannelId(state); + const currentTeamId = getCurrentTeamId(state); + const currentUserId = getCurrentUserId(state); + const teamId = msg.data.team_id; + const actions: Array = [{ + type: ChannelTypes.CHANNEL_MEMBER_ADDED, + data: { + channel_id: msg.broadcast.channel_id, + user_id: msg.data.user_id, + }, + }]; + + if (msg.broadcast.channel_id === currentChannelId) { + const stat = await Client4.getChannelStats(currentChannelId); + actions.push({ + type: ChannelTypes.RECEIVED_CHANNEL_STATS, + data: stat, + }); + } + + if (teamId === currentTeamId && msg.data.user_id === currentUserId) { + const channelActions = await fetchChannelAndMyMember(msg.broadcast.channel_id); + + if (channelActions.length) { + actions.push(...channelActions); + } + } + + dispatch(batchActions(actions, 'BATCH_WS_USER_ADDED')); + } catch (error) { + //do nothing + } + return {data: true}; + }; +} + +function handleUserRemovedEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc, getState: GetStateFunc) => { + try { + const state = getState(); + const channels = getAllChannels(state); + const currentChannelId = getCurrentChannelId(state); + const currentTeamId = getCurrentTeamId(state); + const currentUser = getCurrentUser(state); + const actions: Array = [{ + type: ChannelTypes.CHANNEL_MEMBER_REMOVED, + data: { + channel_id: msg.broadcast.channel_id, + user_id: msg.data.user_id, + }, + }]; + + const channel = channels[currentChannelId]; + + if (msg.data.user_id !== currentUser.id) { + const members = getChannelMembersInChannels(state); + const isMember = Object.values(members).some((member) => member[msg.data.user_id]); + if (channel && isGuest(currentUser.roles) && !isMember) { + actions.push({ + type: UserTypes.PROFILE_NO_LONGER_VISIBLE, + data: {user_id: msg.data.user_id}, + }, { + type: TeamTypes.REMOVE_MEMBER_FROM_TEAM, + data: {team_id: channel.team_id, user_id: msg.data.user_id}, + }); + } + } + + if (msg.broadcast.user_id === currentUser.id && currentTeamId) { + const {data: myData}: any = await dispatch(loadChannelsForTeam(currentTeamId, true)); + + if (myData?.channels && myData?.channelMembers) { + actions.push({ + type: ChannelTypes.RECEIVED_MY_CHANNELS_WITH_MEMBERS, + data: myData, + }); + } + + if (channel) { + actions.push({ + type: ChannelTypes.LEAVE_CHANNEL, + data: { + id: msg.data.channel_id, + user_id: currentUser.id, + team_id: channel.team_id, + type: channel.type, + }, + }); + } + + if (msg.data.channel_id === currentChannelId) { + // emit the event so the client can change his own state + EventEmitter.emit(General.SWITCH_TO_DEFAULT_CHANNEL, currentTeamId); + } + if (isGuest(currentUser.roles)) { + const notVisible = notVisibleUsersActions(state); + if (notVisible.length) { + actions.push(...notVisible); + } + } + } else if (msg.data.channel_id === currentChannelId) { + const stat = await Client4.getChannelStats(currentChannelId); + actions.push({ + type: ChannelTypes.RECEIVED_CHANNEL_STATS, + data: stat, + }); + } + + dispatch(batchActions(actions, 'BATCH_WS_USER_REMOVED')); + } catch { + // do nothing + } + + return {data: true}; + }; +} + +function handleUserUpdatedEvent(msg: WebSocketMessage) { + return (dispatch: DispatchFunc, getState: GetStateFunc) => { + const currentUser = getCurrentUser(getState()); + const user = msg.data.user; + + if (user.id === currentUser.id) { + if (user.update_at > currentUser.update_at) { + // Need to request me to make sure we don't override with sanitized fields from the + // websocket event + dispatch(getMe()); + } + } else { + dispatch({ + type: UserTypes.RECEIVED_PROFILES, + data: { + [user.id]: user, + }, + }); + } + return {data: true}; + }; +} + +function handleRoleAddedEvent(msg: WebSocketMessage) { + const role = JSON.parse(msg.data.role); + + return { + type: RoleTypes.RECEIVED_ROLE, + data: role, + }; +} + +function handleRoleRemovedEvent(msg: WebSocketMessage) { + const role = JSON.parse(msg.data.role); + + return { + type: RoleTypes.ROLE_DELETED, + data: role, + }; +} + +function handleRoleUpdatedEvent(msg: WebSocketMessage) { + const role = JSON.parse(msg.data.role); + + return { + type: RoleTypes.RECEIVED_ROLE, + data: role, + }; +} + +function handleChannelCreatedEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc, getState: GetStateFunc) => { + const {channel_id: channelId, team_id: teamId} = msg.data; + const state = getState(); + const channels = getAllChannels(state); + const currentTeamId = getCurrentTeamId(state); + + if (teamId === currentTeamId && !channels[channelId]) { + const channelActions = await fetchChannelAndMyMember(msg.broadcast.channel_id); + if (channelActions.length) { + dispatch(batchActions(channelActions, 'BATCH_WS_CHANNEL_CREATED')); + } + } + return {data: true}; + }; +} + +function handleChannelDeletedEvent(msg: WebSocketMessage) { + return (dispatch: DispatchFunc, getState: GetStateFunc) => { + const state = getState(); + const currentChannelId = getCurrentChannelId(state); + const currentTeamId = getCurrentTeamId(state); + const config = getConfig(state); + const viewArchivedChannels = config.ExperimentalViewArchivedChannels === 'true'; + const actions: Array = [{ + type: ChannelTypes.RECEIVED_CHANNEL_DELETED, + data: { + id: msg.data.channel_id, + deleteAt: msg.data.delete_at, + team_id: msg.broadcast.team_id, + viewArchivedChannels, + }, + }]; + + if (msg.broadcast.team_id === currentTeamId) { + if (msg.data.channel_id === currentChannelId && !viewArchivedChannels) { + const channelsInTeam = getChannelsNameMapInTeam(state, currentTeamId); + const channel = getChannelByName(channelsInTeam, getRedirectChannelNameForTeam(state, currentTeamId)); + if (channel && channel.id) { + actions.push({type: ChannelTypes.SELECT_CHANNEL, data: channel.id}); + } + EventEmitter.emit(General.DEFAULT_CHANNEL, ''); + } + } + + dispatch(batchActions(actions, 'BATCH_WS_CHANNEL_ARCHIVED')); + + return {data: true}; + }; +} + +function handleChannelUnarchiveEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc, getState: GetStateFunc) => { + const state = getState(); + const currentTeamId = getCurrentTeamId(state); + const config = getConfig(state); + const viewArchivedChannels = config.ExperimentalViewArchivedChannels === 'true'; + + if (msg.broadcast.team_id === currentTeamId) { + const actions: Array = [{ + type: ChannelTypes.RECEIVED_CHANNEL_UNARCHIVED, + data: { + id: msg.data.channel_id, + team_id: msg.data.team_id, + deleteAt: 0, + viewArchivedChannels, + }, + }]; + + const {data: myData}: any = await dispatch(loadChannelsForTeam(currentTeamId, true)); + if (myData?.channels && myData?.channelMembers) { + actions.push({ + type: ChannelTypes.RECEIVED_MY_CHANNELS_WITH_MEMBERS, + data: myData, + }); + } + + dispatch(batchActions(actions, 'BATCH_WS_CHANNEL_UNARCHIVED')); + } + return {data: true}; + }; +} + +function handleChannelUpdatedEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc, getState: GetStateFunc) => { + let channel; + try { + channel = msg.data ? JSON.parse(msg.data.channel) : null; + } catch (err) { + return {error: err}; + } + + const currentChannelId = getCurrentChannelId(getState()); + if (channel) { + dispatch({ + type: ChannelTypes.RECEIVED_CHANNEL, + data: channel, + }); + + if (currentChannelId === channel.id) { + // Emit an event with the channel received as we need to handle + // the changes without listening to the store + EventEmitter.emit(WebsocketEvents.CHANNEL_UPDATED, channel); + } + } + return {data: true}; + }; +} + +// handleChannelConvertedEvent handles updating of channel which is converted from public to private + +function handleChannelConvertedEvent(msg: WebSocketMessage) { + return (dispatch: DispatchFunc, getState: GetStateFunc) => { + const channelId = msg.data.channel_id; + if (channelId) { + const channel = getChannel(getState(), channelId); + if (channel) { + dispatch({ + type: ChannelTypes.RECEIVED_CHANNEL, + data: {...channel, type: General.PRIVATE_CHANNEL}, + }); + } + } + return {data: true}; + }; +} + +function handleChannelViewedEvent(msg: WebSocketMessage) { + return (dispatch: DispatchFunc, getState: GetStateFunc) => { + const state = getState(); + const {channel_id: channelId} = msg.data; + const currentChannelId = getCurrentChannelId(state); + const currentUserId = getCurrentUserId(state); + + if (channelId !== currentChannelId && currentUserId === msg.broadcast.user_id) { + dispatch(markChannelAsRead(channelId, undefined, false)); + } + return {data: true}; + }; +} + +function handleChannelMemberUpdatedEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc) => { + try { + const channelMember = JSON.parse(msg.data.channelMember); + const rolesToLoad = channelMember.roles.split(' '); + const actions: Array = [{ + type: ChannelTypes.RECEIVED_MY_CHANNEL_MEMBER, + data: channelMember, + }]; + + const roles = await Client4.getRolesByNames(rolesToLoad); + if (roles.length) { + actions.push({ + type: RoleTypes.RECEIVED_ROLES, + data: roles, + }); + } + + dispatch(batchActions(actions, 'BATCH_WS_CHANNEL_MEMBER_UPDATE')); + } catch { + //do nothing + } + return {data: true}; + }; +} + +function handleChannelSchemeUpdatedEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc) => { + const channelActions = await fetchChannelAndMyMember(msg.broadcast.channel_id); + if (channelActions.length) { + dispatch(batchActions(channelActions, 'BATCH_WS_SCHEME_UPDATE')); + } + return {data: true}; + }; +} + +function handleDirectAddedEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc) => { + const channelActions = await fetchChannelAndMyMember(msg.broadcast.channel_id); + if (channelActions.length) { + dispatch(batchActions(channelActions, 'BATCH_WS_DM_ADDED')); + } + return {data: true}; + }; +} + +function handlePreferenceChangedEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc, getState: GetStateFunc) => { + const preference = JSON.parse(msg.data.preference); + const actions: Array = [{ + type: PreferenceTypes.RECEIVED_PREFERENCES, + data: [preference], + }]; + + const dmActions = await getAddedDmUsersIfNecessary(getState(), [preference]); + if (dmActions.length) { + actions.push(...dmActions); + } + + dispatch(batchActions(actions, 'BATCH_WS_PREFERENCE_CHANGED')); + return {data: true}; + }; +} + +function handlePreferencesChangedEvent(msg: WebSocketMessage) { + return async (dispatch: DispatchFunc, getState: GetStateFunc) => { + const preferences: PreferenceType[] = JSON.parse(msg.data.preferences); + const posts = getAllPosts(getState()); + const actions: Array = [{ + type: PreferenceTypes.RECEIVED_PREFERENCES, + data: preferences, + }]; + + preferences.forEach((pref) => { + if (pref.category === Preferences.CATEGORY_FLAGGED_POST && !posts[pref.name]) { + dispatch(getPost(pref.name)); + } + }); + + const dmActions = await getAddedDmUsersIfNecessary(getState(), preferences); + if (dmActions.length) { + actions.push(...dmActions); + } + + dispatch(batchActions(actions, 'BATCH_WS_PREFERENCES_CHANGED')); + return {data: true}; + }; +} + +function handlePreferencesDeletedEvent(msg: WebSocketMessage) { + const preferences = JSON.parse(msg.data.preferences); + + return {type: PreferenceTypes.DELETED_PREFERENCES, data: preferences}; +} + +function handleStatusChangedEvent(msg: WebSocketMessage) { + return { + type: UserTypes.RECEIVED_STATUSES, + data: [{user_id: msg.data.user_id, status: msg.data.status}], + }; +} + +function handleHelloEvent(msg: WebSocketMessage) { + const serverVersion = msg.data.server_version; + if (serverVersion && Client4.serverVersion !== serverVersion) { + Client4.serverVersion = serverVersion; + EventEmitter.emit(General.SERVER_VERSION_CHANGED, serverVersion); + } +} + +export function handleUserTypingEvent(msg: WebSocketMessage) { + return (dispatch: DispatchFunc, getState: GetStateFunc) => { + const state = getState(); + const currentChannelId = getCurrentChannelId(state); + + if (currentChannelId === msg.broadcast.channel_id) { + const profiles = getUsers(state); + const statuses = getUserStatuses(state); + const currentUserId = getCurrentUserId(state); + const config = getConfig(state); + const userId = msg.data.user_id; + + const data = { + id: msg.broadcast.channel_id + msg.data.parent_id, + userId, + now: Date.now(), + }; + + dispatch({ + type: WebsocketEvents.TYPING, + data, + }); + + setTimeout(() => { + const newState = getState(); + const {typing} = newState.entities; + + if (typing && typing[data.id]) { + dispatch({ + type: WebsocketEvents.STOP_TYPING, + data, + }); + } + }, parseInt(config.TimeBetweenUserTypingUpdatesMilliseconds!, 10)); + + if (!profiles[userId] && userId !== currentUserId) { + dispatch(getProfilesByIds([userId])); + } + + const status = statuses[userId]; + if (status !== General.ONLINE) { + dispatch(getStatusesByIds([userId])); + } + } + + return {data: true}; + }; +} + +function handleReactionAddedEvent(msg: WebSocketMessage) { + return (dispatch: DispatchFunc) => { + const {data} = msg; + const reaction = JSON.parse(data.reaction); + + dispatch(getCustomEmojiForReaction(reaction.emoji_name)); + + dispatch({ + type: PostTypes.RECEIVED_REACTION, + data: reaction, + }); + return {data: true}; + }; +} + +function handleReactionRemovedEvent(msg: WebSocketMessage) { + const {data} = msg; + const reaction = JSON.parse(data.reaction); + + return { + type: PostTypes.REACTION_DELETED, + data: reaction, + }; +} + +function handleAddEmoji(msg: WebSocketMessage) { + const data = JSON.parse(msg.data.emoji); + + return { + type: EmojiTypes.RECEIVED_CUSTOM_EMOJI, + data, + }; +} + +function handleLicenseChangedEvent(msg: WebSocketMessage) { + const data = msg.data.license; + + return { + type: GeneralTypes.CLIENT_LICENSE_RECEIVED, + data, + }; +} + +function handleConfigChangedEvent(msg: WebSocketMessage) { + const data = msg.data.config; + + EventEmitter.emit(General.CONFIG_CHANGED, data); + return { + type: GeneralTypes.CLIENT_CONFIG_RECEIVED, + data, + }; +} + +function handleOpenDialogEvent(msg: WebSocketMessage) { + return (dispatch: DispatchFunc) => { + const data = (msg.data && msg.data.dialog) || {}; + dispatch({type: IntegrationTypes.RECEIVED_DIALOG, data: JSON.parse(data)}); + return {data: true}; + }; +} + +// Helpers +export function notVisibleUsersActions(state: GlobalState): Array { + const knownUsers = getKnownUsers(state); + const allUsers = Object.keys(getUsers(state)); + const usersToRemove = new Set(allUsers.filter((x) => !knownUsers.has(x))); + + const actions = []; + for (const userToRemove of usersToRemove.values()) { + actions.push({type: UserTypes.PROFILE_NO_LONGER_VISIBLE, data: {user_id: userToRemove}}); + } + + return actions; +} + +let lastTimeTypingSent = 0; +export function userTyping(state: GlobalState, channelId: string, parentPostId: string) { + const config = getConfig(state); + const t = Date.now(); + const stats = getCurrentChannelStats(state); + const membersInChannel = stats ? stats.member_count : 0; + + if (((t - lastTimeTypingSent) > parseInt(config.TimeBetweenUserTypingUpdatesMilliseconds!, 10)) && + (membersInChannel < parseInt(config.MaxNotificationsPerChannel!, 10)) && (config.EnableUserTypingMessages === 'true')) { + websocketClient.userTyping(channelId, parentPostId); + lastTimeTypingSent = t; + } +} \ No newline at end of file diff --git a/app/client/websocket.ts b/app/client/websocket.ts new file mode 100644 index 000000000..968399ebe --- /dev/null +++ b/app/client/websocket.ts @@ -0,0 +1,247 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Platform} from 'react-native'; + +const MAX_WEBSOCKET_FAILS = 7; +const MIN_WEBSOCKET_RETRY_TIME = 3000; // 3 sec + +const MAX_WEBSOCKET_RETRY_TIME = 300000; // 5 mins + +class WebSocketClient { + conn?: WebSocket; + connectionUrl: string; + token: string|null; + sequence: number; + connectFailCount: number; + eventCallback?: Function; + firstConnectCallback?: Function; + reconnectCallback?: Function; + errorCallback?: Function; + closeCallback?: Function; + connectingCallback?: Function; + stop: boolean; + connectionTimeout: any; + + constructor() { + this.connectionUrl = ''; + this.token = null; + this.sequence = 1; + this.connectFailCount = 0; + this.stop = false; + } + + initialize(token: string|null, opts = {}) { + const defaults = { + forceConnection: true, + connectionUrl: this.connectionUrl, + }; + + const {connectionUrl, forceConnection, ...additionalOptions} = Object.assign({}, defaults, opts); + + if (forceConnection) { + this.stop = false; + } + + return new Promise((resolve, reject) => { + if (this.conn) { + resolve(); + return; + } + + if (connectionUrl == null) { + console.log('websocket must have connection url'); //eslint-disable-line no-console + reject(new Error('websocket must have connection url')); + return; + } + + if (this.connectFailCount === 0) { + console.log('websocket connecting to ' + connectionUrl); //eslint-disable-line no-console + } + + if (this.connectingCallback) { + this.connectingCallback(); + } + + const regex = /^(?:https?|wss?):(?:\/\/)?[^/]*/; + const captured = (regex).exec(connectionUrl); + + let origin; + if (captured) { + origin = captured[0]; + + if (Platform.OS === 'android') { + // this is done cause for android having the port 80 or 443 will fail the connection + // the websocket will append them + const split = origin.split(':'); + const port = split[2]; + if (port === '80' || port === '443') { + origin = `${split[0]}:${split[1]}`; + } + } + } else { + // If we're unable to set the origin header, the websocket won't connect, but the URL is likely malformed anyway + const errorMessage = 'websocket failed to parse origin from ' + connectionUrl; + console.warn(errorMessage); // eslint-disable-line no-console + reject(new Error(errorMessage)); + return; + } + + this.conn = new WebSocket(connectionUrl, [], {headers: {origin}, ...(additionalOptions || {})}); + this.connectionUrl = connectionUrl; + this.token = token; + + this.conn!.onopen = () => { + if (token) { + // we check for the platform as a workaround until we fix on the server that further authentications + // are ignored + this.sendMessage('authentication_challenge', {token}); + } + + if (this.connectFailCount > 0) { + console.log('websocket re-established connection'); //eslint-disable-line no-console + if (this.reconnectCallback) { + this.reconnectCallback(); + } + } else if (this.firstConnectCallback) { + this.firstConnectCallback(); + } + + this.connectFailCount = 0; + resolve(); + }; + + this.conn!.onclose = () => { + this.conn = undefined; + this.sequence = 1; + + if (this.connectFailCount === 0) { + console.log('websocket closed'); //eslint-disable-line no-console + } + + this.connectFailCount++; + + if (this.closeCallback) { + this.closeCallback(this.connectFailCount); + } + + let retryTime = MIN_WEBSOCKET_RETRY_TIME; + + // If we've failed a bunch of connections then start backing off + if (this.connectFailCount > MAX_WEBSOCKET_FAILS) { + retryTime = MIN_WEBSOCKET_RETRY_TIME * this.connectFailCount; + if (retryTime > MAX_WEBSOCKET_RETRY_TIME) { + retryTime = MAX_WEBSOCKET_RETRY_TIME; + } + } + + if (this.connectionTimeout) { + clearTimeout(this.connectionTimeout); + } + + this.connectionTimeout = setTimeout( + () => { + if (this.stop) { + clearTimeout(this.connectionTimeout); + return; + } + this.initialize(token, opts); + }, + retryTime, + ); + }; + + this.conn!.onerror = (evt: any) => { + if (this.connectFailCount <= 1) { + console.log('websocket error'); //eslint-disable-line no-console + console.log(evt); //eslint-disable-line no-console + } + + if (this.errorCallback) { + this.errorCallback(evt); + } + }; + + this.conn!.onmessage = (evt: any) => { + const msg = JSON.parse(evt.data); + if (msg.seq_reply) { + if (msg.error) { + console.warn(msg); //eslint-disable-line no-console + } + } else if (this.eventCallback) { + this.eventCallback(msg); + } + }; + }); + } + + setConnectingCallback(callback: Function) { + this.connectingCallback = callback; + } + + setEventCallback(callback: Function) { + this.eventCallback = callback; + } + + setFirstConnectCallback(callback: Function) { + this.firstConnectCallback = callback; + } + + setReconnectCallback(callback: Function) { + this.reconnectCallback = callback; + } + + setErrorCallback(callback: Function) { + this.errorCallback = callback; + } + + setCloseCallback(callback: Function) { + this.closeCallback = callback; + } + + close(stop = false) { + this.stop = stop; + this.connectFailCount = 0; + this.sequence = 1; + if (this.conn && this.conn.readyState === WebSocket.OPEN) { + this.conn.onclose = () => {}; //eslint-disable-line no-empty-function + this.conn.close(); + this.conn = undefined; + console.log('websocket closed'); //eslint-disable-line no-console + } + } + + sendMessage(action: string, data: any) { + const msg = { + action, + seq: this.sequence++, + data, + }; + + if (this.conn && this.conn.readyState === WebSocket.OPEN) { + this.conn.send(JSON.stringify(msg)); + } else if (!this.conn || this.conn.readyState === WebSocket.CLOSED) { + this.conn = undefined; + this.initialize(this.token); + } + } + + userTyping(channelId: string, parentId: string) { + this.sendMessage('user_typing', { + channel_id: channelId, + parent_id: parentId, + }); + } + + getStatuses() { + this.sendMessage('get_statuses', null); + } + + getStatusesByIds(userIds: string[]) { + this.sendMessage('get_statuses_by_ids', { + user_ids: userIds, + }); + } +} + +export default new WebSocketClient(); diff --git a/app/components/channel_loader/channel_loader.test.js b/app/components/channel_loader/channel_loader.test.js index 1591d1fac..5ae88aaa7 100644 --- a/app/components/channel_loader/channel_loader.test.js +++ b/app/components/channel_loader/channel_loader.test.js @@ -9,7 +9,7 @@ import Preferences from 'mattermost-redux/constants/preferences'; import ChannelLoader from './channel_loader'; jest.mock('rn-placeholder', () => ({ - ImageContent: () => {}, + ImageContent: () => null, })); describe('ChannelLoader', () => { diff --git a/app/components/formatted_markdown_text.js b/app/components/formatted_markdown_text.js index 01898d9af..4068a7bda 100644 --- a/app/components/formatted_markdown_text.js +++ b/app/components/formatted_markdown_text.js @@ -92,7 +92,7 @@ class FormattedMarkdownText extends React.PureComponent { } renderLink = ({children, href}) => { - var url = href[0] === TARGET_BLANK_URL_PREFIX ? href.substring(1, href.length) : href; + const url = href[0] === TARGET_BLANK_URL_PREFIX ? href.substring(1, href.length) : href; return {children}; } diff --git a/app/components/network_indicator/index.js b/app/components/network_indicator/index.js index 64c0d3b4a..a0455efcb 100644 --- a/app/components/network_indicator/index.js +++ b/app/components/network_indicator/index.js @@ -5,7 +5,7 @@ import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; import {stopPeriodicStatusUpdates, startPeriodicStatusUpdates} from 'mattermost-redux/actions/users'; -import {init as initWebSocket, close as closeWebSocket} from 'mattermost-redux/actions/websocket'; +import {init as initWebSocket, close as closeWebSocket} from '@actions/websocket'; import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'; import {connection} from 'app/actions/device'; diff --git a/app/components/network_indicator/network_indicator.js b/app/components/network_indicator/network_indicator.js index e94e04807..757ac2691 100644 --- a/app/components/network_indicator/network_indicator.js +++ b/app/components/network_indicator/network_indicator.js @@ -169,7 +169,6 @@ export default class NetworkIndicator extends PureComponent { }; connected = () => { - this.props.actions.setChannelRetryFailed(false); Animated.sequence([ Animated.timing( this.backgroundColor, { @@ -302,7 +301,7 @@ export default class NetworkIndicator extends PureComponent { certificate = await mattermostBucket.getPreference('cert'); } - initWebSocket(platform, null, null, null, {certificate, forceConnection: true}).catch(() => { + initWebSocket({certificate, forceConnection: true}).catch(() => { // we should dispatch a failure and show the app as disconnected Alert.alert( formatMessage({id: 'mobile.authentication_error.title', defaultMessage: 'Authentication Error'}), diff --git a/app/components/sidebars/drawer_layout.js b/app/components/sidebars/drawer_layout.js index b4f4f3691..6fdacfbe0 100644 --- a/app/components/sidebars/drawer_layout.js +++ b/app/components/sidebars/drawer_layout.js @@ -2,7 +2,6 @@ // Original work: https://github.com/react-native-community/react-native-drawer-layout . // Modified work: Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -// @flow import React, { Component } from 'react'; import { @@ -17,6 +16,7 @@ import { View, I18nManager, } from 'react-native'; +import PropTypes from 'prop-types'; import telemetry from 'app/telemetry'; @@ -31,62 +31,31 @@ const emptyObject = {}; export const DRAWER_INITIAL_OFFSET = 40; export const TABLET_WIDTH = 250; -export type PropType = { - children: any, - drawerBackgroundColor?: string, - drawerLockMode?: 'unlocked' | 'locked-closed' | 'locked-open', - drawerPosition: 'left' | 'right', - drawerWidth: number, - keyboardDismissMode?: 'none' | 'on-drag', - onDrawerClose?: Function, - onDrawerOpen?: Function, - onDrawerSlide?: Function, - onDrawerStateChanged?: Function, - renderNavigationView: () => any, - statusBarBackgroundColor?: string, - useNativeAnimations?: boolean, - isTablet?: boolean, -}; - -export type StateType = { - accessibilityViewIsModal: boolean, - deviceWidth: number, - drawerShown: boolean, - openValue: any, - threshold: number, -}; - -export type EventType = { - stopPropagation: Function, -}; - -export type PanResponderEventType = { - dx: number, - dy: number, - moveX: number, - moveY: number, - vx: number, - vy: number, -}; - -export type DrawerMovementOptionType = { - velocity?: number, -}; export default class DrawerLayout extends Component { - props: PropType; - state: StateType; - _lastOpenValue: number; - _panResponder: any; - _isClosing: boolean; - _closingAnchorValue: number; - canClose: boolean; + static propTypes = { + children: PropTypes.any, + drawerBackgroundColor: PropTypes.string, + drawerLockMode: PropTypes.oneOf(['unlocked', 'locked-closed', 'locked-open']), + drawerPosition: PropTypes.oneOf(['left', 'right']), + drawerWidth: PropTypes.number, + keyboardDismissMode: PropTypes.oneOf(['none', 'on-drag']), + onDrawerClose: PropTypes.func, + onDrawerOpen: PropTypes.func, + onDrawerSlide: PropTypes.func, + onDrawerStateChanged: PropTypes.func, + renderNavigationView: PropTypes.func, + statusBarBackgroundColor: PropTypes.string, + useNativeAnimations: PropTypes.bool, + isTablet: PropTypes.bool, + } static defaultProps = { drawerWidth: 0, drawerPosition: 'left', useNativeAnimations: false, isTablet: false, + renderNavigationView: () => true, }; static positions = { @@ -94,7 +63,7 @@ export default class DrawerLayout extends Component { Right: 'right', }; - constructor(props: PropType, context: any) { + constructor(props, context) { super(props, context); this._panResponder = PanResponder.create({ @@ -316,20 +285,20 @@ export default class DrawerLayout extends Component { ); } - _onOverlayClick = (e: EventType) => { + _onOverlayClick = (e) => { e.stopPropagation(); if (!this._isLockedClosed() && !this._isLockedOpen()) { this.closeDrawer(); } }; - _emitStateChanged = (newState: string) => { + _emitStateChanged = (newState) => { if (this.props.onDrawerStateChanged) { this.props.onDrawerStateChanged(newState); } }; - openDrawer = (options: DrawerMovementOptionType = {}) => { + openDrawer = (options = {}) => { if (!this.props.isTablet) { this._emitStateChanged(SETTLING); Animated.timing(this.openValue, { @@ -351,7 +320,7 @@ export default class DrawerLayout extends Component { } }; - closeDrawer = (options: DrawerMovementOptionType = {}) => { + closeDrawer = (options = {}) => { this._emitStateChanged(SETTLING); Animated.timing(this.openValue, { toValue: 0, @@ -383,10 +352,7 @@ export default class DrawerLayout extends Component { } }; - _shouldSetPanResponder = ( - e: EventType, - { moveX, dx, dy }: PanResponderEventType, - ) => { + _shouldSetPanResponder = (e, { moveX, dx, dy }) => { if (!dx || !dy || Math.abs(dx) < MIN_SWIPE_DISTANCE) { return false; } @@ -443,7 +409,7 @@ export default class DrawerLayout extends Component { this._emitStateChanged(DRAGGING); }; - _panResponderMove = (e: EventType, { moveX, dx }: PanResponderEventType) => { + _panResponderMove = (e, { moveX, dx }) => { const useDx = this.getDrawerPosition() === 'left' && !this._isClosing; let openValue = this._getOpenValueForX(useDx ? dx : moveX); @@ -460,10 +426,7 @@ export default class DrawerLayout extends Component { this.openValue.setValue(openValue); }; - _panResponderRelease = ( - e: EventType, - { moveX, vx }: PanResponderEventType, - ) => { + _panResponderRelease = (e, { moveX, vx }) => { const {threshold} = this.state; const previouslyOpen = this._isClosing; const isWithinVelocityThreshold = vx < VX_MAX && vx > -VX_MAX; @@ -521,7 +484,7 @@ export default class DrawerLayout extends Component { this.state.drawerShown; }; - _getOpenValueForX(x: number): number { + _getOpenValueForX(x) { const { drawerWidth } = this.props; const { deviceWidth } = this.state; diff --git a/app/components/sidebars/main/channels_list/channel_item/channel_item.test.js b/app/components/sidebars/main/channels_list/channel_item/channel_item.test.js index 9f825d7f5..9722f4cd6 100644 --- a/app/components/sidebars/main/channels_list/channel_item/channel_item.test.js +++ b/app/components/sidebars/main/channels_list/channel_item/channel_item.test.js @@ -31,7 +31,7 @@ describe('ChannelItem', () => { isUnread: true, hasDraft: false, mentions: 0, - onSelectChannel: () => {}, // eslint-disable-line no-empty-function + onSelectChannel: () => true, shouldHideChannel: false, showUnreadForMsgs: true, theme: Preferences.THEMES.default, diff --git a/app/components/sidebars/main/main_sidebar_base.js b/app/components/sidebars/main/main_sidebar_base.js index c1fb66983..389b16ce6 100644 --- a/app/components/sidebars/main/main_sidebar_base.js +++ b/app/components/sidebars/main/main_sidebar_base.js @@ -10,11 +10,11 @@ import { View, } from 'react-native'; -import {General, WebsocketEvents} from 'mattermost-redux/constants'; +import {General} from 'mattermost-redux/constants'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; import SafeAreaView from 'app/components/safe_area_view'; -import {NavigationTypes} from 'app/constants'; +import {NavigationTypes, WebsocketEvents} from 'app/constants'; import tracker from 'app/utils/time_tracker'; import {t} from 'app/utils/i18n'; diff --git a/app/components/sidebars/main/teams_list/teams_list.js b/app/components/sidebars/main/teams_list/teams_list.js index fd10a1d68..8920fcfb2 100644 --- a/app/components/sidebars/main/teams_list/teams_list.js +++ b/app/components/sidebars/main/teams_list/teams_list.js @@ -79,9 +79,9 @@ export default class TeamsList extends PureComponent { tracker.teamSwitch = Date.now(); actions.handleTeamChange(teamId); } - - closeMainSidebar(); }); + + closeMainSidebar(); }; goToSelectTeam = preventDoubleTap(async () => { diff --git a/app/constants/index.js b/app/constants/index.js index c059bf3ce..6678203b0 100644 --- a/app/constants/index.js +++ b/app/constants/index.js @@ -7,6 +7,7 @@ import ListTypes from './list'; import NavigationTypes from './navigation'; import Types from './types'; import ViewTypes, {UpgradeTypes} from './view'; +import WebsocketEvents from './websocket'; export { DeepLinkTypes, @@ -16,4 +17,5 @@ export { UpgradeTypes, Types, ViewTypes, + WebsocketEvents, }; diff --git a/app/constants/websocket.ts b/app/constants/websocket.ts new file mode 100644 index 000000000..7adb29c5d --- /dev/null +++ b/app/constants/websocket.ts @@ -0,0 +1,45 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +const WebsocketEvents = { + POSTED: 'posted', + POST_EDITED: 'post_edited', + POST_DELETED: 'post_deleted', + POST_UNREAD: 'post_unread', + CHANNEL_CONVERTED: 'channel_converted', + CHANNEL_CREATED: 'channel_created', + CHANNEL_DELETED: 'channel_deleted', + CHANNEL_UNARCHIVED: 'channel_restored', + CHANNEL_UPDATED: 'channel_updated', + CHANNEL_VIEWED: 'channel_viewed', + CHANNEL_MEMBER_UPDATED: 'channel_member_updated', + CHANNEL_SCHEME_UPDATED: 'channel_scheme_updated', + DIRECT_ADDED: 'direct_added', + ADDED_TO_TEAM: 'added_to_team', + LEAVE_TEAM: 'leave_team', + UPDATE_TEAM: 'update_team', + USER_ADDED: 'user_added', + USER_REMOVED: 'user_removed', + USER_UPDATED: 'user_updated', + USER_ROLE_UPDATED: 'user_role_updated', + ROLE_ADDED: 'role_added', + ROLE_REMOVED: 'role_removed', + ROLE_UPDATED: 'role_updated', + TYPING: 'typing', + STOP_TYPING: 'stop_typing', + PREFERENCE_CHANGED: 'preference_changed', + PREFERENCES_CHANGED: 'preferences_changed', + PREFERENCES_DELETED: 'preferences_deleted', + EPHEMERAL_MESSAGE: 'ephemeral_message', + STATUS_CHANGED: 'status_change', + HELLO: 'hello', + WEBRTC: 'webrtc', + REACTION_ADDED: 'reaction_added', + REACTION_REMOVED: 'reaction_removed', + EMOJI_ADDED: 'emoji_added', + LICENSE_CHANGED: 'license_changed', + CONFIG_CHANGED: 'config_changed', + PLUGIN_STATUSES_CHANGED: 'plugin_statuses_changed', + OPEN_DIALOG: 'open_dialog', + INCREASE_POST_VISIBILITY_BY_ONE: 'increase_post_visibility_by_one', +}; +export default WebsocketEvents; diff --git a/app/init/fetch.test.js b/app/init/fetch.test.js index 4255d5d27..87d5e179a 100644 --- a/app/init/fetch.test.js +++ b/app/init/fetch.test.js @@ -12,6 +12,10 @@ import { } from 'app/init/fetch'; describe('Fetch', () => { + beforeAll(() => { + global.fetch = jest.fn(() => Promise.resolve()); + }); + test('doFetchWithResponse handles empty headers', async () => { const setToken = jest.spyOn(Client4, 'setToken'); const response = { diff --git a/app/init/global_event_handler.js b/app/init/global_event_handler.js index cc1f6e7c2..ede30b617 100644 --- a/app/init/global_event_handler.js +++ b/app/init/global_event_handler.js @@ -11,7 +11,7 @@ import semver from 'semver/preload'; import {setAppState, setServerVersion} from 'mattermost-redux/actions/general'; import {autoUpdateTimezone} from 'mattermost-redux/actions/timezone'; -import {close as closeWebSocket} from 'mattermost-redux/actions/websocket'; +import {close as closeWebSocket} from '@actions/websocket'; import {GeneralTypes} from 'mattermost-redux/action_types'; import {Client4} from 'mattermost-redux/client'; import {General} from 'mattermost-redux/constants'; @@ -141,7 +141,9 @@ class GlobalEventHandler { onDeepLink = (event) => { const {url} = event; - this.store.dispatch(setDeepLinkURL(url)); + if (url) { + this.store.dispatch(setDeepLinkURL(url)); + } }; onManagedConfigurationChange = () => { @@ -288,7 +290,7 @@ class GlobalEventHandler { type: GeneralTypes.RECEIVED_APP_DEVICE_TOKEN, data: state.entities.general.deviceToken, }, - ])); + ], 'BATCH_RESET_STATE')); } catch (e) { // clear error } diff --git a/app/initial_state.js b/app/initial_state.js index e9215a0e5..6ec7e4b70 100644 --- a/app/initial_state.js +++ b/app/initial_state.js @@ -155,6 +155,11 @@ const state = { drafts: {}, }, }, + websocket: { + connected: false, + lastConnectAt: 0, + lastDisconnectAt: 0, + }, }; export default state; diff --git a/app/mattermost.js b/app/mattermost.js index 9e2fc2e28..fe7d06a11 100644 --- a/app/mattermost.js +++ b/app/mattermost.js @@ -63,7 +63,9 @@ const launchApp = (credentials) => { EphemeralStore.appStarted = true; Linking.getInitialURL().then((url) => { - store.dispatch(setDeepLinkURL(url)); + if (url) { + store.dispatch(setDeepLinkURL(url)); + } }); }; diff --git a/app/screens/error_teams_list/error_teams_list.test.js b/app/screens/error_teams_list/error_teams_list.test.js index fa072f0f2..e6fcaf04c 100644 --- a/app/screens/error_teams_list/error_teams_list.test.js +++ b/app/screens/error_teams_list/error_teams_list.test.js @@ -18,10 +18,10 @@ describe('ErrorTeamsList', () => { const baseProps = { actions: { - loadMe: () => {}, // eslint-disable-line no-empty-function - connection: () => {}, // eslint-disable-line no-empty-function - logout: () => {}, // eslint-disable-line no-empty-function - selectDefaultTeam: () => {}, // eslint-disable-line no-empty-function + loadMe: () => true, + connection: () => true, + logout: () => true, + selectDefaultTeam: () => true, }, componentId: 'component-id', theme: Preferences.THEMES.default, diff --git a/app/screens/flagged_posts/flagged_posts.test.js b/app/screens/flagged_posts/flagged_posts.test.js index a9b750ef6..ae731a9d2 100644 --- a/app/screens/flagged_posts/flagged_posts.test.js +++ b/app/screens/flagged_posts/flagged_posts.test.js @@ -11,7 +11,7 @@ import {shallowWithIntl} from 'test/intl-test-helper'; import FlaggedPosts from './flagged_posts'; jest.mock('rn-placeholder', () => ({ - ImageContent: () => {}, + ImageContent: () => null, })); describe('FlaggedPosts', () => { diff --git a/app/screens/pinned_posts/pinned_posts.test.js b/app/screens/pinned_posts/pinned_posts.test.js index bba24703f..f7f889ba2 100644 --- a/app/screens/pinned_posts/pinned_posts.test.js +++ b/app/screens/pinned_posts/pinned_posts.test.js @@ -10,7 +10,7 @@ import {shallowWithIntl} from 'test/intl-test-helper'; import PinnedPosts from './pinned_posts'; jest.mock('rn-placeholder', () => ({ - ImageContent: () => {}, + ImageContent: () => null, })); describe('PinnedPosts', () => { diff --git a/app/screens/recent_mentions/recent_mentions.test.js b/app/screens/recent_mentions/recent_mentions.test.js index d604ce2fc..c85e222ca 100644 --- a/app/screens/recent_mentions/recent_mentions.test.js +++ b/app/screens/recent_mentions/recent_mentions.test.js @@ -11,7 +11,7 @@ import {shallowWithIntl} from 'test/intl-test-helper'; import RecentMentions from './recent_mentions'; jest.mock('rn-placeholder', () => ({ - ImageContent: () => {}, + ImageContent: () => null, })); describe('RecentMentions', () => { diff --git a/app/screens/user_profile/user_profile.js b/app/screens/user_profile/user_profile.js index 79bfe5c89..4412c99c8 100644 --- a/app/screens/user_profile/user_profile.js +++ b/app/screens/user_profile/user_profile.js @@ -217,7 +217,7 @@ export default class UserProfile extends PureComponent { const email = this.props.user.email; return () => { - var hydrated = link.replace(/{email}/, email); + let hydrated = link.replace(/{email}/, email); hydrated = hydrated.replace(/{username}/, username); Linking.openURL(hydrated); }; @@ -244,7 +244,7 @@ export default class UserProfile extends PureComponent { const profileLinks = Config.ExperimentalProfileLinks; const additionalOptions = profileLinks.map((l) => { - var action; + let action; if (l.type === 'link') { action = this.handleLinkPress(l.url); } diff --git a/app/store/middleware.js b/app/store/middleware.js index 05a8cbbb7..1e09e69b3 100644 --- a/app/store/middleware.js +++ b/app/store/middleware.js @@ -44,6 +44,15 @@ export function messageRetention(store) { return next(nextAction); } + /* Uncomment the following lines to log the actions being dispatched */ + // if (action.type === 'BATCHING_REDUCER.BATCH') { + // action.payload.forEach((p) => { + // console.log('BATCHED ACTIONS', p.type); + // }); + // } else { + // console.log('ACTION', action.type); + // } + return next(action); }; } diff --git a/app/utils/channels.js b/app/utils/channels.js index 8a4d8a3b0..68a3cbfa1 100644 --- a/app/utils/channels.js +++ b/app/utils/channels.js @@ -20,17 +20,6 @@ export function isFavoriteChannel(preferences, channelId) { return fav ? fav.value === 'true' : false; } -// New to replace the ones above -export function isDirectMessageVisible(preferences, channelId) { - const dm = preferences[`${Preferences.CATEGORY_DIRECT_CHANNEL_SHOW}--${channelId}`]; - return dm ? dm.value === 'true' : true; -} - -export function isGroupMessageVisible(preferences, channelId) { - const gm = preferences[`${Preferences.CATEGORY_GROUP_CHANNEL_SHOW}--${channelId}`]; - return gm ? gm.value === 'true' : true; -} - export function isDirectChannelAutoClosed(config, preferences, channelId, channelActivity, channelArchiveTime = 0, currentChannelId = '') { // When the config is not set or is a favorite channel if (config.CloseUnusedDirectMessages !== 'true' || isFavoriteChannel(preferences, channelId)) { diff --git a/app/utils/error_handling.js b/app/utils/error_handling.js index 091e41c81..69db424dc 100644 --- a/app/utils/error_handling.js +++ b/app/utils/error_handling.js @@ -11,7 +11,7 @@ import { import {Client4} from 'mattermost-redux/client'; import {logError} from 'mattermost-redux/actions/errors'; -import {close as closeWebSocket} from 'mattermost-redux/actions/websocket'; +import {close as closeWebSocket} from '@actions/websocket'; import {purgeOfflineStore} from 'app/actions/views/root'; import {DEFAULT_LOCALE, getTranslations} from 'app/i18n'; diff --git a/app/utils/latinise.js b/app/utils/latinise.js index d05da6759..ea0e2b3ab 100644 --- a/app/utils/latinise.js +++ b/app/utils/latinise.js @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. // Credit to http://semplicewebsites.com/removing-accents-javascript -var latinMap = { +const latinMap = { Á: 'A', // LATIN CAPITAL LETTER A WITH ACUTE Ă: 'A', // LATIN CAPITAL LETTER A WITH BREVE Ắ: 'A', // LATIN CAPITAL LETTER A WITH BREVE AND ACUTE diff --git a/app/utils/url.js b/app/utils/url.js index a472a0e86..41fdae19f 100644 --- a/app/utils/url.js +++ b/app/utils/url.js @@ -84,7 +84,7 @@ export function getShortenedURL(url = '', getLength = 27) { } export function cleanUpUrlable(input) { - var cleaned = latinise(input); + let cleaned = latinise(input); cleaned = cleaned.trim().replace(/-/g, ' ').replace(/[^\w\s]/gi, '').toLowerCase().replace(/\s/g, '-'); cleaned = cleaned.replace(/-{2,}/, '-'); cleaned = cleaned.replace(/^-+/, ''); diff --git a/app/utils/users.js b/app/utils/users.js index c376c9a56..672a4a955 100644 --- a/app/utils/users.js +++ b/app/utils/users.js @@ -4,8 +4,8 @@ export function isInRole(roles, inRole) { if (roles) { - var parts = roles.split(' '); - for (var i = 0; i < parts.length; i++) { + const parts = roles.split(' '); + for (let i = 0; i < parts.length; i++) { if (parts[i] === inRole) { return true; } diff --git a/babel.config.js b/babel.config.js index c19cf693f..d409dce8c 100644 --- a/babel.config.js +++ b/babel.config.js @@ -13,6 +13,12 @@ module.exports = { root: ['.'], alias: { assets: './dist/assets', + '@actions': './app/actions', + '@constants': './app/constants', + '@selectors': './app/selectors', + '@telemetry': './app/telemetry', + '@utils': './app/utils', + '@websocket': './app/client/websocket', }, }], ], diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 000000000..9eeeccc61 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,33 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +module.exports = { + preset: '@testing-library/react-native', + verbose: true, + globals: { + 'ts-jest': { + tsConfigFile: 'tsconfig.jest.json', + }, + }, + clearMocks: true, + setupFilesAfterEnv: [ + '/test/setup.js', + '/node_modules/jest-enzyme/lib/index.js', + ], + collectCoverageFrom: [ + 'app/**/*.{js,jsx,ts,tsx}', + ], + coverageReporters: [ + 'lcov', + 'text-summary', + ], + testPathIgnorePatterns: [ + '/node_modules/', + ], + moduleNameMapper: { + 'assets/images/video_player/(.*).png': '/dist/assets/images/video_player/$1@2x.png', + }, + transformIgnorePatterns: [ + 'node_modules/(?!react-native|jail-monkey|@sentry/react-native|react-navigation|@react-native-community/cameraroll)', + ], +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index fdf751449..6fcf226c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -514,128 +514,120 @@ } }, "@babel/helper-remap-async-to-generator": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.0.tgz", - "integrity": "sha512-+aKyBd4oHAaIZgOLq/uLjkUz7ExZ0ppdNBc8Qr72BmtKNAy3A6EJa/ifjj0//CIzQtUDPs3E6HjKM2cV6bnXsQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", + "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", "requires": { - "@babel/helper-annotate-as-pure": "^7.8.0", - "@babel/helper-wrap-function": "^7.8.0", - "@babel/template": "^7.8.0", - "@babel/traverse": "^7.8.0", - "@babel/types": "^7.8.0" + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-wrap-function": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" }, "dependencies": { "@babel/code-frame": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.0.tgz", - "integrity": "sha512-AN2IR/wCUYsM+PdErq6Bp3RFTXl8W0p9Nmymm7zkpsCmh+r/YYcckaCGpU8Q/mEKmST19kkGRaG42A/jxOWwBA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "requires": { - "@babel/highlight": "^7.8.0" + "@babel/highlight": "^7.8.3" } }, "@babel/generator": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.0.tgz", - "integrity": "sha512-2Lp2e02CV2C7j/H4n4D9YvsvdhPVVg9GDIamr6Tu4tU35mL3mzOrzl1lZ8ZJtysfZXh+y+AGORc2rPS7yHxBUg==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.4.tgz", + "integrity": "sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA==", "requires": { - "@babel/types": "^7.8.0", + "@babel/types": "^7.9.0", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" } }, "@babel/helper-annotate-as-pure": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.0.tgz", - "integrity": "sha512-WWj+1amBdowU2g18p3/KUc1Y5kWnaNm1paohq2tT4/RreeMNssYkv6ul9wkE2iIqjwLBwNMZGH4pTGlMSUqMMg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", + "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", "requires": { - "@babel/types": "^7.8.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-function-name": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.0.tgz", - "integrity": "sha512-x9psucuU0Xalw+0Vpr2FYJMLB7/KnPSLZhlkUyOGbYAWRDfmtZBrguYpJYiaNCRV7vGkYjO/gF6/J6yMvdWTDw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", "requires": { - "@babel/helper-get-function-arity": "^7.8.0", - "@babel/template": "^7.8.0", - "@babel/types": "^7.8.0" + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-get-function-arity": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.0.tgz", - "integrity": "sha512-eUP5grliToMapQiTaYS2AAO/WwaCG7cuJztR1v/a1aPzUzUeGt+AaI9OvLATc/AfFkF8SLJ10d5ugGt/AQ9d6w==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "requires": { - "@babel/types": "^7.8.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-split-export-declaration": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.0.tgz", - "integrity": "sha512-YhYFhH4T6DlbT6CPtVgLfC1Jp2gbCawU/ml7WJvUpBg01bCrXSzTYMZZXbbIGjq/kHmK8YUATxTppcRGzj31pA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", "requires": { - "@babel/types": "^7.8.0" + "@babel/types": "^7.8.3" } }, "@babel/highlight": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.0.tgz", - "integrity": "sha512-OsdTJbHlPtIk2mmtwXItYrdmalJ8T0zpVzNAbKSkHshuywj7zb29Y09McV/jQsQunc/nEyHiPV2oy9llYMLqxw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", "requires": { + "@babel/helper-validator-identifier": "^7.9.0", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.0.tgz", - "integrity": "sha512-VVtsnUYbd1+2A2vOVhm4P2qNXQE8L/W859GpUHfUcdhX8d3pEKThZuIr6fztocWx9HbK+00/CR0tXnhAggJ4CA==" + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==" }, "@babel/template": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.0.tgz", - "integrity": "sha512-0NNMDsY2t3ltAVVK1WHNiaePo3tXPUeJpCX4I3xSKFoEl852wJHG8mrgHVADf8Lz1y+8al9cF7cSSfzSnFSYiw==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", "requires": { - "@babel/code-frame": "^7.8.0", - "@babel/parser": "^7.8.0", - "@babel/types": "^7.8.0" + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" } }, "@babel/traverse": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.0.tgz", - "integrity": "sha512-d/6sPXFLGlJHZO/zWDtgFaKyalCOHLedzxpVJn6el1cw+f2TZa7xZEszeXdOw6EUemqRFBAn106BWBvtSck9Qw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", + "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", "requires": { - "@babel/code-frame": "^7.8.0", - "@babel/generator": "^7.8.0", - "@babel/helper-function-name": "^7.8.0", - "@babel/helper-split-export-declaration": "^7.8.0", - "@babel/parser": "^7.8.0", - "@babel/types": "^7.8.0", + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.9.0", + "@babel/types": "^7.9.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.0.tgz", - "integrity": "sha512-1RF84ehyx9HH09dMMwGWl3UTWlVoCPtqqJPjGuC4JzMe1ZIVDJ2DT8mv3cPv/A7veLD6sgR7vi95lJqm+ZayIg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", + "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", "requires": { - "esutils": "^2.0.2", + "@babel/helper-validator-identifier": "^7.9.0", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } } } }, @@ -667,120 +659,117 @@ "@babel/types": "^7.7.4" } }, + "@babel/helper-validator-identifier": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", + "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==" + }, "@babel/helper-wrap-function": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.0.tgz", - "integrity": "sha512-2j6idN2jt8Y+8nJ4UPN/6AZa53DAkcETMVmroJQh50qZc59PuQKVjgOIIqmrLoQf6Ia9bs90MHRcID1OW5tfag==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", + "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", "requires": { - "@babel/helper-function-name": "^7.8.0", - "@babel/template": "^7.8.0", - "@babel/traverse": "^7.8.0", - "@babel/types": "^7.8.0" + "@babel/helper-function-name": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" }, "dependencies": { "@babel/code-frame": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.0.tgz", - "integrity": "sha512-AN2IR/wCUYsM+PdErq6Bp3RFTXl8W0p9Nmymm7zkpsCmh+r/YYcckaCGpU8Q/mEKmST19kkGRaG42A/jxOWwBA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "requires": { - "@babel/highlight": "^7.8.0" + "@babel/highlight": "^7.8.3" } }, "@babel/generator": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.0.tgz", - "integrity": "sha512-2Lp2e02CV2C7j/H4n4D9YvsvdhPVVg9GDIamr6Tu4tU35mL3mzOrzl1lZ8ZJtysfZXh+y+AGORc2rPS7yHxBUg==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.4.tgz", + "integrity": "sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA==", "requires": { - "@babel/types": "^7.8.0", + "@babel/types": "^7.9.0", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.0.tgz", - "integrity": "sha512-x9psucuU0Xalw+0Vpr2FYJMLB7/KnPSLZhlkUyOGbYAWRDfmtZBrguYpJYiaNCRV7vGkYjO/gF6/J6yMvdWTDw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", "requires": { - "@babel/helper-get-function-arity": "^7.8.0", - "@babel/template": "^7.8.0", - "@babel/types": "^7.8.0" + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-get-function-arity": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.0.tgz", - "integrity": "sha512-eUP5grliToMapQiTaYS2AAO/WwaCG7cuJztR1v/a1aPzUzUeGt+AaI9OvLATc/AfFkF8SLJ10d5ugGt/AQ9d6w==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "requires": { - "@babel/types": "^7.8.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-split-export-declaration": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.0.tgz", - "integrity": "sha512-YhYFhH4T6DlbT6CPtVgLfC1Jp2gbCawU/ml7WJvUpBg01bCrXSzTYMZZXbbIGjq/kHmK8YUATxTppcRGzj31pA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", "requires": { - "@babel/types": "^7.8.0" + "@babel/types": "^7.8.3" } }, "@babel/highlight": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.0.tgz", - "integrity": "sha512-OsdTJbHlPtIk2mmtwXItYrdmalJ8T0zpVzNAbKSkHshuywj7zb29Y09McV/jQsQunc/nEyHiPV2oy9llYMLqxw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", "requires": { + "@babel/helper-validator-identifier": "^7.9.0", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.0.tgz", - "integrity": "sha512-VVtsnUYbd1+2A2vOVhm4P2qNXQE8L/W859GpUHfUcdhX8d3pEKThZuIr6fztocWx9HbK+00/CR0tXnhAggJ4CA==" + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==" }, "@babel/template": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.0.tgz", - "integrity": "sha512-0NNMDsY2t3ltAVVK1WHNiaePo3tXPUeJpCX4I3xSKFoEl852wJHG8mrgHVADf8Lz1y+8al9cF7cSSfzSnFSYiw==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", "requires": { - "@babel/code-frame": "^7.8.0", - "@babel/parser": "^7.8.0", - "@babel/types": "^7.8.0" + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" } }, "@babel/traverse": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.0.tgz", - "integrity": "sha512-d/6sPXFLGlJHZO/zWDtgFaKyalCOHLedzxpVJn6el1cw+f2TZa7xZEszeXdOw6EUemqRFBAn106BWBvtSck9Qw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", + "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", "requires": { - "@babel/code-frame": "^7.8.0", - "@babel/generator": "^7.8.0", - "@babel/helper-function-name": "^7.8.0", - "@babel/helper-split-export-declaration": "^7.8.0", - "@babel/parser": "^7.8.0", - "@babel/types": "^7.8.0", + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.9.0", + "@babel/types": "^7.9.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.0.tgz", - "integrity": "sha512-1RF84ehyx9HH09dMMwGWl3UTWlVoCPtqqJPjGuC4JzMe1ZIVDJ2DT8mv3cPv/A7veLD6sgR7vi95lJqm+ZayIg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", + "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", "requires": { - "esutils": "^2.0.2", + "@babel/helper-validator-identifier": "^7.9.0", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } } } }, @@ -916,17 +905,17 @@ "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" }, "@babel/plugin-external-helpers": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-external-helpers/-/plugin-external-helpers-7.8.0.tgz", - "integrity": "sha512-ySUOoVdHYpMJvn5rzdSMAb9dHRqml9XBXjgmzZQC511j5HWZ+zVZcNFhcaqROT9fkvbROakaxy7rpOtVLG0a3A==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-external-helpers/-/plugin-external-helpers-7.8.3.tgz", + "integrity": "sha512-mx0WXDDiIl5DwzMtzWGRSPugXi9BxROS05GQrhLNbEamhBiicgn994ibwkyiBH+6png7bm/yA7AUsvHyCXi4Vw==", "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.8.3" }, "dependencies": { "@babel/helper-plugin-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.0.tgz", - "integrity": "sha512-+hAlRGdf8fHQAyNnDBqTHQhwdLURLdrCROoWaEQYiQhk2sV9Rhs+GoFZZfMJExTq9HG8o2NX3uN2G90bFtmFdA==" + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" } } }, @@ -1248,17 +1237,17 @@ } }, "@babel/plugin-syntax-class-properties": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.8.0.tgz", - "integrity": "sha512-Ygw7R0yM6MjoqPFG4tlX2/p0ZlPAF7mvSrVASsvsRPi5Exad0quQ5tRPFTQI/2baPHlfeGZp7Qv5SMaWk3Lpyg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.8.3.tgz", + "integrity": "sha512-UcAyQWg2bAN647Q+O811tG9MrJ38Z10jjhQdKNAL8fsyPzE3cCN/uT+f55cFVY4aGO4jqJAvmqsuY3GQDwAoXg==", "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.8.3" }, "dependencies": { "@babel/helper-plugin-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.0.tgz", - "integrity": "sha512-+hAlRGdf8fHQAyNnDBqTHQhwdLURLdrCROoWaEQYiQhk2sV9Rhs+GoFZZfMJExTq9HG8o2NX3uN2G90bFtmFdA==" + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" } } }, @@ -1398,34 +1387,34 @@ } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.0.tgz", - "integrity": "sha512-9dvBvJnEdsDWYMrykoMyLNVRPGoub6SFlARtsYgSQ1riTjnyBjhctihSME4XsSku86F59PDeFpC9PCU+9I154w==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", + "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", "requires": { - "@babel/helper-module-imports": "^7.8.0", - "@babel/helper-plugin-utils": "^7.8.0", - "@babel/helper-remap-async-to-generator": "^7.8.0" + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3" }, "dependencies": { "@babel/helper-module-imports": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.0.tgz", - "integrity": "sha512-ylY9J6ZxEcjmJaJ4P6aVs/fZdrZVctCGnxxfYXwCnSMapqd544zT8lWK2qI/vBPjE5gS0o2jILnH+AkpsPauEQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", + "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", "requires": { - "@babel/types": "^7.8.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-plugin-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.0.tgz", - "integrity": "sha512-+hAlRGdf8fHQAyNnDBqTHQhwdLURLdrCROoWaEQYiQhk2sV9Rhs+GoFZZfMJExTq9HG8o2NX3uN2G90bFtmFdA==" + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" }, "@babel/types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.0.tgz", - "integrity": "sha512-1RF84ehyx9HH09dMMwGWl3UTWlVoCPtqqJPjGuC4JzMe1ZIVDJ2DT8mv3cPv/A7veLD6sgR7vi95lJqm+ZayIg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", + "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", "requires": { - "esutils": "^2.0.2", + "@babel/helper-validator-identifier": "^7.9.0", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } @@ -1433,17 +1422,17 @@ } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.0.tgz", - "integrity": "sha512-bim6gUfHq2kPN+aQst33ZEMeglpaUXAo6PWTZvOA8BOnWpNKgZcUzBvpZhh2ofL6YhZgzGoRwVVfzwynDEf47g==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", + "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.8.3" }, "dependencies": { "@babel/helper-plugin-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.0.tgz", - "integrity": "sha512-+hAlRGdf8fHQAyNnDBqTHQhwdLURLdrCROoWaEQYiQhk2sV9Rhs+GoFZZfMJExTq9HG8o2NX3uN2G90bFtmFdA==" + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" } } }, @@ -1573,17 +1562,17 @@ } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.0.tgz", - "integrity": "sha512-lJSdaWR56wmrosCiyqKFRVnLrFYoVAk2mtZAyegt7akeJky/gguv0Rukx9GV3XwHGuM1ZPE06cZMjNlcLp8LrQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz", + "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==", "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.8.3" }, "dependencies": { "@babel/helper-plugin-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.0.tgz", - "integrity": "sha512-+hAlRGdf8fHQAyNnDBqTHQhwdLURLdrCROoWaEQYiQhk2sV9Rhs+GoFZZfMJExTq9HG8o2NX3uN2G90bFtmFdA==" + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" } } }, @@ -2243,149 +2232,141 @@ } }, "@babel/plugin-transform-object-super": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.0.tgz", - "integrity": "sha512-2DYqQ811nRlFVlni6iqfxBVVGqkBgfvEv/lcvmdNu2CaG+EA7zSP1hqYUsqamR+uCdDbsrV7uY6/0rkXbJo5YQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", + "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", "requires": { - "@babel/helper-plugin-utils": "^7.8.0", - "@babel/helper-replace-supers": "^7.8.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.3" }, "dependencies": { "@babel/code-frame": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.0.tgz", - "integrity": "sha512-AN2IR/wCUYsM+PdErq6Bp3RFTXl8W0p9Nmymm7zkpsCmh+r/YYcckaCGpU8Q/mEKmST19kkGRaG42A/jxOWwBA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "requires": { - "@babel/highlight": "^7.8.0" + "@babel/highlight": "^7.8.3" } }, "@babel/generator": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.0.tgz", - "integrity": "sha512-2Lp2e02CV2C7j/H4n4D9YvsvdhPVVg9GDIamr6Tu4tU35mL3mzOrzl1lZ8ZJtysfZXh+y+AGORc2rPS7yHxBUg==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.4.tgz", + "integrity": "sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA==", "requires": { - "@babel/types": "^7.8.0", + "@babel/types": "^7.9.0", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.0.tgz", - "integrity": "sha512-x9psucuU0Xalw+0Vpr2FYJMLB7/KnPSLZhlkUyOGbYAWRDfmtZBrguYpJYiaNCRV7vGkYjO/gF6/J6yMvdWTDw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", "requires": { - "@babel/helper-get-function-arity": "^7.8.0", - "@babel/template": "^7.8.0", - "@babel/types": "^7.8.0" + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-get-function-arity": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.0.tgz", - "integrity": "sha512-eUP5grliToMapQiTaYS2AAO/WwaCG7cuJztR1v/a1aPzUzUeGt+AaI9OvLATc/AfFkF8SLJ10d5ugGt/AQ9d6w==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "requires": { - "@babel/types": "^7.8.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.0.tgz", - "integrity": "sha512-0m1QabGrdXuoxX/g+KOAGndoHwskC70WweqHRQyCsaO67KOEELYh4ECcGw6ZGKjDKa5Y7SW4Qbhw6ly4Fah/jQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", + "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", "requires": { - "@babel/types": "^7.8.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-optimise-call-expression": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.0.tgz", - "integrity": "sha512-aiJt1m+K57y0n10fTw+QXcCXzmpkG+o+NoQmAZqlZPstkTE0PZT+Z27QSd/6Gf00nuXJQO4NiJ0/YagSW5kC2A==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", + "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", "requires": { - "@babel/types": "^7.8.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-plugin-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.0.tgz", - "integrity": "sha512-+hAlRGdf8fHQAyNnDBqTHQhwdLURLdrCROoWaEQYiQhk2sV9Rhs+GoFZZfMJExTq9HG8o2NX3uN2G90bFtmFdA==" + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" }, "@babel/helper-replace-supers": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.0.tgz", - "integrity": "sha512-R2CyorW4tcO3YzdkClLpt6MS84G+tPkOi0MmiCn1bvYVnmDpdl9R15XOi3NQW2mhOAEeBnuQ4g1Bh7pT2sX8fg==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", + "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.0", - "@babel/helper-optimise-call-expression": "^7.8.0", - "@babel/traverse": "^7.8.0", - "@babel/types": "^7.8.0" + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.6" } }, "@babel/helper-split-export-declaration": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.0.tgz", - "integrity": "sha512-YhYFhH4T6DlbT6CPtVgLfC1Jp2gbCawU/ml7WJvUpBg01bCrXSzTYMZZXbbIGjq/kHmK8YUATxTppcRGzj31pA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", "requires": { - "@babel/types": "^7.8.0" + "@babel/types": "^7.8.3" } }, "@babel/highlight": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.0.tgz", - "integrity": "sha512-OsdTJbHlPtIk2mmtwXItYrdmalJ8T0zpVzNAbKSkHshuywj7zb29Y09McV/jQsQunc/nEyHiPV2oy9llYMLqxw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", "requires": { + "@babel/helper-validator-identifier": "^7.9.0", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.0.tgz", - "integrity": "sha512-VVtsnUYbd1+2A2vOVhm4P2qNXQE8L/W859GpUHfUcdhX8d3pEKThZuIr6fztocWx9HbK+00/CR0tXnhAggJ4CA==" + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==" }, "@babel/template": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.0.tgz", - "integrity": "sha512-0NNMDsY2t3ltAVVK1WHNiaePo3tXPUeJpCX4I3xSKFoEl852wJHG8mrgHVADf8Lz1y+8al9cF7cSSfzSnFSYiw==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", "requires": { - "@babel/code-frame": "^7.8.0", - "@babel/parser": "^7.8.0", - "@babel/types": "^7.8.0" + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" } }, "@babel/traverse": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.0.tgz", - "integrity": "sha512-d/6sPXFLGlJHZO/zWDtgFaKyalCOHLedzxpVJn6el1cw+f2TZa7xZEszeXdOw6EUemqRFBAn106BWBvtSck9Qw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", + "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", "requires": { - "@babel/code-frame": "^7.8.0", - "@babel/generator": "^7.8.0", - "@babel/helper-function-name": "^7.8.0", - "@babel/helper-split-export-declaration": "^7.8.0", - "@babel/parser": "^7.8.0", - "@babel/types": "^7.8.0", + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.9.0", + "@babel/types": "^7.9.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.0.tgz", - "integrity": "sha512-1RF84ehyx9HH09dMMwGWl3UTWlVoCPtqqJPjGuC4JzMe1ZIVDJ2DT8mv3cPv/A7veLD6sgR7vi95lJqm+ZayIg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", + "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", "requires": { - "esutils": "^2.0.2", + "@babel/helper-validator-identifier": "^7.9.0", "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } } } }, @@ -2400,17 +2381,17 @@ } }, "@babel/plugin-transform-property-literals": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.0.tgz", - "integrity": "sha512-vjZaQlojnZIahu5ofEW+hPJfDI5A6r2Sbi5C0RuCaAOFj7viDIR5kOR7ul3Fz5US8V1sVk5Zd2yuPaz7iBeysg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz", + "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==", "requires": { - "@babel/helper-plugin-utils": "^7.8.0" + "@babel/helper-plugin-utils": "^7.8.3" }, "dependencies": { "@babel/helper-plugin-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.0.tgz", - "integrity": "sha512-+hAlRGdf8fHQAyNnDBqTHQhwdLURLdrCROoWaEQYiQhk2sV9Rhs+GoFZZfMJExTq9HG8o2NX3uN2G90bFtmFdA==" + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" } } }, @@ -3568,63 +3549,63 @@ } }, "@jest/core": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-25.1.0.tgz", - "integrity": "sha512-iz05+NmwCmZRzMXvMo6KFipW7nzhbpEawrKrkkdJzgytavPse0biEnCNr2wRlyCsp3SmKaEY+SGv7YWYQnIdig==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-25.2.1.tgz", + "integrity": "sha512-Pe7CVcysOmm69BgdgAuMjRCp6vmcCJy32PxPtArQDgiizIBQElHhE9P34afGwPgSb3+e3WC6XtEm4de7d9BtfQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/reporters": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/reporters": "^25.2.1", + "@jest/test-result": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "ansi-escapes": "^4.2.1", "chalk": "^3.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.3", - "jest-changed-files": "^25.1.0", - "jest-config": "^25.1.0", - "jest-haste-map": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-resolve": "^25.1.0", - "jest-resolve-dependencies": "^25.1.0", - "jest-runner": "^25.1.0", - "jest-runtime": "^25.1.0", - "jest-snapshot": "^25.1.0", - "jest-util": "^25.1.0", - "jest-validate": "^25.1.0", - "jest-watcher": "^25.1.0", + "jest-changed-files": "^25.2.1", + "jest-config": "^25.2.1", + "jest-haste-map": "^25.2.1", + "jest-message-util": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-resolve": "^25.2.1", + "jest-resolve-dependencies": "^25.2.1", + "jest-runner": "^25.2.1", + "jest-runtime": "^25.2.1", + "jest-snapshot": "^25.2.1", + "jest-util": "^25.2.1", + "jest-validate": "^25.2.1", + "jest-watcher": "^25.2.1", "micromatch": "^4.0.2", "p-each-series": "^2.1.0", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "rimraf": "^3.0.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, "dependencies": { "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.1.tgz", + "integrity": "sha512-v3tkMr5AeVm6R23wnZdC5dzXdHPFa6j2uiTC15iHISYkGIilE9O1qmAYKELHPXZifDbz9c8WwzsqoN8K8uG4jg==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", + "@jest/source-map": "^25.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "slash": "^3.0.0" } }, "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -3633,46 +3614,46 @@ } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.1.tgz", + "integrity": "sha512-E0tlWh2iOELRLbbPEngs3Dsx88vGBQOs6O3w46YeXfMHlwwqzWrlvoeUq6kRlHRm1O8H+EBr60Wtrwh20C+zWQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -3834,39 +3815,40 @@ } }, "jest-get-type": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", - "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.1.tgz", + "integrity": "sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w==", "dev": true }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" + "walker": "^1.0.7", + "which": "^2.0.2" } }, "jest-message-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", - "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.2.1.tgz", + "integrity": "sha512-pxwehr9uPEuCI9bPjBiZxpFMN0+3wny5p7/E3hbV9XjsqREhJJAMf0czvHtgNeUBo2iAiAI9yi9ICKHPOKePEw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/test-result": "^25.2.1", + "@jest/types": "^25.2.1", "@types/stack-utils": "^1.0.1", "chalk": "^3.0.0", "micromatch": "^4.0.2", @@ -3875,53 +3857,62 @@ } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" } }, "jest-validate": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.1.0.tgz", - "integrity": "sha512-kGbZq1f02/zVO2+t1KQGSVoCTERc5XeObLwITqC6BTRH3Adv7NZdYqCpKIZLUgpLXf2yISzQ465qOZpul8abXA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.2.1.tgz", + "integrity": "sha512-vGtNFPyvylFfTFFfptzqCy5S3cP/N5JJVwm8gsXeZq8jMmvUngfWtuw+Tr5Wjo+dqOle23td8BE0ruGnsONDmw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "camelcase": "^5.3.1", "chalk": "^3.0.0", - "jest-get-type": "^25.1.0", + "jest-get-type": "^25.2.1", "leven": "^3.1.0", - "pretty-format": "^25.1.0" + "pretty-format": "^25.2.1" } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -3945,17 +3936,23 @@ "dev": true }, "pretty-format": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", - "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.1.tgz", + "integrity": "sha512-YS+e9oGYIbEeAFgqTU8qeZ3DN2Pz0iaD81ox+iUjLIXVJWeB7Ro/2AnfxRnl/yJJ5R674d7E3jLPuh6bwg0+qw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" } }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -4027,6 +4024,15 @@ "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", "dev": true }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -4064,17 +4070,16 @@ } }, "@jest/reporters": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.1.0.tgz", - "integrity": "sha512-ORLT7hq2acJQa8N+NKfs68ZtHFnJPxsGqmofxW7v7urVhzJvpKZG9M7FAcgh9Ee1ZbCteMrirHA3m5JfBtAaDg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.2.1.tgz", + "integrity": "sha512-jAnIECIIFVHiASKLpPBpZ9fIgWolKdMwUuyjSlNVixmtX6G83fyiGaOquaAU1ukAxnlKdCLjvH6BYdY+GGbd5Q==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^25.1.0", - "@jest/environment": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/test-result": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", @@ -4084,11 +4089,10 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.0.0", - "jest-haste-map": "^25.1.0", - "jest-resolve": "^25.1.0", - "jest-runtime": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-resolve": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "node-notifier": "^6.0.0", "slash": "^3.0.0", "source-map": "^0.6.0", @@ -4098,51 +4102,27 @@ }, "dependencies": { "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.1.tgz", + "integrity": "sha512-v3tkMr5AeVm6R23wnZdC5dzXdHPFa6j2uiTC15iHISYkGIilE9O1qmAYKELHPXZifDbz9c8WwzsqoN8K8uG4jg==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", + "@jest/source-map": "^25.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "slash": "^3.0.0" } }, - "@jest/environment": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", - "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", - "dev": true, - "requires": { - "@jest/fake-timers": "^25.1.0", - "@jest/types": "^25.1.0", - "jest-mock": "^25.1.0" - } - }, - "@jest/fake-timers": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", - "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", - "dev": true, - "requires": { - "@jest/types": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-util": "^25.1.0", - "lolex": "^5.0.0" - } - }, "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -4151,46 +4131,46 @@ } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.1.tgz", + "integrity": "sha512-E0tlWh2iOELRLbbPEngs3Dsx88vGBQOs6O3w46YeXfMHlwwqzWrlvoeUq6kRlHRm1O8H+EBr60Wtrwh20C+zWQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -4338,83 +4318,68 @@ } }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" - } - }, - "jest-message-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", - "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-mock": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", - "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", - "dev": true, - "requires": { - "@jest/types": "^25.1.0" + "walker": "^1.0.7", + "which": "^2.0.2" } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4443,6 +4408,18 @@ "semver": "^6.3.0", "shellwords": "^0.1.1", "which": "^1.3.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "normalize-path": { @@ -4451,6 +4428,12 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -4498,6 +4481,15 @@ "is-number": "^7.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -4545,39 +4537,39 @@ } }, "@jest/test-sequencer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.1.0.tgz", - "integrity": "sha512-WgZLRgVr2b4l/7ED1J1RJQBOharxS11EFhmwDqknpknE0Pm87HLZVS2Asuuw+HQdfQvm2aXL2FvvBLxOD1D0iw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.2.1.tgz", + "integrity": "sha512-yEhVlBRS7pg3MGBIQQafYfm2NT5trFa/qoxtLftQoZmyzKx3rPy0oJ+d/8QljK4X2RGY/i7mmQDxE6sGR9UqeQ==", "dev": true, "requires": { - "@jest/test-result": "^25.1.0", - "jest-haste-map": "^25.1.0", - "jest-runner": "^25.1.0", - "jest-runtime": "^25.1.0" + "@jest/test-result": "^25.2.1", + "jest-haste-map": "^25.2.1", + "jest-runner": "^25.2.1", + "jest-runtime": "^25.2.1" }, "dependencies": { "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.1.tgz", + "integrity": "sha512-v3tkMr5AeVm6R23wnZdC5dzXdHPFa6j2uiTC15iHISYkGIilE9O1qmAYKELHPXZifDbz9c8WwzsqoN8K8uG4jg==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", + "@jest/source-map": "^25.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "slash": "^3.0.0" } }, "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -4586,46 +4578,46 @@ } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.1.tgz", + "integrity": "sha512-E0tlWh2iOELRLbbPEngs3Dsx88vGBQOs6O3w46YeXfMHlwwqzWrlvoeUq6kRlHRm1O8H+EBr60Wtrwh20C+zWQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -4766,58 +4758,68 @@ } }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" + "walker": "^1.0.7", + "which": "^2.0.2" } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -4840,6 +4842,12 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -4887,6 +4895,15 @@ "is-number": "^7.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -4973,9 +4990,9 @@ } }, "@react-native-community/cli-platform-android": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-3.0.3.tgz", - "integrity": "sha512-rNO9DmRiVhB6aP2DVUjEJv7ecriTARDZND88ny3xNVUkrD1Y+zwF6aZu3eoT52VXOxLCSLiJzz19OiyGmfqxYg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-3.1.4.tgz", + "integrity": "sha512-ClSdY20F0gzWVLTqCv7vHjnUqOcuq10jd9GgHX6lGSc2GI+Ql3/aQg3tmG4uY3KXNNwAv3U8QCoYgg1WGfwiHA==", "requires": { "@react-native-community/cli-tools": "^3.0.0", "chalk": "^2.4.2", @@ -4994,9 +5011,9 @@ } }, "@react-native-community/cli-platform-ios": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-3.0.0.tgz", - "integrity": "sha512-QoNVlDj8eMXRZk9uktPFsctHurQpv9jKmiu6mQii4NEtT2npE7g1hbWpRNojutBsfgmCdQGDHd9uB54eeCnYgg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-3.2.0.tgz", + "integrity": "sha512-pzEnx68H6+mHBq5jsMrr3UmAmkrLSMlC9BZ4yoUdfUXCQq6/R70zNYvH4hjUw8h2Al7Kgq53UzHUsM0ph8TSWQ==", "requires": { "@react-native-community/cli-tools": "^3.0.0", "chalk": "^2.4.2", @@ -5085,16 +5102,23 @@ } }, "@sentry/cli": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.51.1.tgz", - "integrity": "sha512-JKYdoE5Pz8AQaupVQW3XOFTuff1UyLtxhcFzQPwQXiZEyXabo00dosX3YkqPKUwMv7LgxTOH23SJU8HwStAmFw==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.52.0.tgz", + "integrity": "sha512-8Yr90eCTdIuT/awt0VbeArnXswAozEFNTUluxUdqP4TJSpzR+10fP9JlJnf1cjmq3aQtWBmxCgJWMOgDXmJ3Zw==", "requires": { "fs-copy-file-sync": "^1.1.1", "https-proxy-agent": "^4.0.0", - "mkdirp": "^0.5.1", + "mkdirp": "^1.0.0", "node-fetch": "^2.1.2", "progress": "2.0.0", "proxy-from-env": "^1.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz", + "integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==" + } } }, "@sentry/core": { @@ -5243,10 +5267,43 @@ "@babel/types": "^7.3.0" } }, + "@types/cheerio": { + "version": "0.22.16", + "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.16.tgz", + "integrity": "sha512-bSbnU/D4yzFdzLpp3+rcDj0aQQMIRUBNJU7azPxdqMpnexjUSvGJyDuOBQBHeOZh1mMKgsJm6Dy+LLh80Ew4tQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, + "@types/enzyme": { + "version": "3.10.5", + "resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.5.tgz", + "integrity": "sha512-R+phe509UuUYy9Tk0YlSbipRpfVtIzb/9BHn5pTEtjJTF5LXvUjrIQcZvNyANNEyFrd2YGs196PniNT1fgvOQA==", + "dev": true, + "requires": { + "@types/cheerio": "*", + "@types/react": "*" + } + }, + "@types/enzyme-adapter-react-16": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.6.tgz", + "integrity": "sha512-VonDkZ15jzqDWL8mPFIQnnLtjwebuL9YnDkqeCDYnB4IVgwUm0mwKkqhrxLL6mb05xm7qqa3IE95m8CZE9imCg==", + "dev": true, + "requires": { + "@types/enzyme": "*" + } + }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", "dev": true }, "@types/hammerjs": { @@ -5276,6 +5333,107 @@ "@types/istanbul-lib-report": "*" } }, + "@types/jest": { + "version": "25.1.4", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.1.4.tgz", + "integrity": "sha512-QDDY2uNAhCV7TMCITrxz+MRk1EizcsevzfeS6LykIlq2V1E5oO4wXG8V2ZEd9w7Snxeeagk46YbMgZ8ESHx3sw==", + "dev": true, + "requires": { + "jest-diff": "^25.1.0", + "pretty-format": "^25.1.0" + }, + "dependencies": { + "@jest/types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", + "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "@types/json-schema": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", @@ -5288,15 +5446,21 @@ "integrity": "sha512-Jg1F+bmxcpENHP23sVKkNuU3uaxPnsBMW0cLjleiikFKomJQbsn0Cqk2yDvQArqzZN6ABfBkZ0To7pQ8sLdWDg==", "dev": true }, + "@types/prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==", + "dev": true + }, "@types/prop-types": { "version": "15.7.3", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, "@types/react": { - "version": "16.9.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.14.tgz", - "integrity": "sha512-Q4tW4RGmR+u/CgzR8VqZcsUWjP4Pz/LcHfs9AzSG+aBnwq8As3Bid3vG1eGGsXg/xuR2k2tqNlI8pzyV8kxe0g==", + "version": "16.9.23", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.23.tgz", + "integrity": "sha512-SsGVT4E7L2wLN3tPYLiF20hmZTPGuzaayVunfgXzUn1x4uHVsKH6QDJQ/TdpHqwsTLd4CwrmQ2vOgxN7gE24gw==", "requires": { "@types/prop-types": "*", "csstype": "^2.2.0" @@ -5319,6 +5483,15 @@ "@types/react-native": "*" } }, + "@types/react-test-renderer": { + "version": "16.9.2", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.9.2.tgz", + "integrity": "sha512-4eJr1JFLIAlWhzDkBCkhrOIWOvOxcCAfQh+jiKg7l/nNZcCIL2MHl2dZhogIFKyHzedVWHaVP1Yydq/Ruu4agw==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -5337,6 +5510,68 @@ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.1.0.tgz", "integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==" }, + "@typescript-eslint/eslint-plugin": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.25.0.tgz", + "integrity": "sha512-W2YyMtjmlrOjtXc+FtTelVs9OhuR6OlYc4XKIslJ8PUJOqgYYAPRJhAqkYRQo3G4sjvG8jSodsNycEn4W2gHUw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "2.25.0", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + }, + "dependencies": { + "@typescript-eslint/experimental-utils": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.25.0.tgz", + "integrity": "sha512-0IZ4ZR5QkFYbaJk+8eJ2kYeA+1tzOE1sBjbwwtSV85oNWYUBep+EyhlZ7DLUCyhMUGuJpcCCFL0fDtYAP1zMZw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.25.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.25.0.tgz", + "integrity": "sha512-VUksmx5lDxSi6GfmwSK7SSoIKSw9anukWWNitQPqt58LuYrKalzsgeuignbqnB+rK/xxGlSsCy8lYnwFfB6YJg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^6.3.0", + "tsutils": "^3.17.1" + } + }, + "eslint-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", + "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "regexpp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "@typescript-eslint/experimental-utils": { "version": "2.22.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.22.0.tgz", @@ -5348,6 +5583,62 @@ "eslint-scope": "^5.0.0" } }, + "@typescript-eslint/parser": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.25.0.tgz", + "integrity": "sha512-mccBLaBSpNVgp191CP5W+8U1crTyXsRziWliCqzj02kpxdjKMvFHGJbK33NroquH3zB/gZ8H511HEsJBa2fNEg==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.25.0", + "@typescript-eslint/typescript-estree": "2.25.0", + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "@typescript-eslint/experimental-utils": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.25.0.tgz", + "integrity": "sha512-0IZ4ZR5QkFYbaJk+8eJ2kYeA+1tzOE1sBjbwwtSV85oNWYUBep+EyhlZ7DLUCyhMUGuJpcCCFL0fDtYAP1zMZw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.25.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.25.0.tgz", + "integrity": "sha512-VUksmx5lDxSi6GfmwSK7SSoIKSw9anukWWNitQPqt58LuYrKalzsgeuignbqnB+rK/xxGlSsCy8lYnwFfB6YJg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^6.3.0", + "tsutils": "^3.17.1" + } + }, + "eslint-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", + "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "@typescript-eslint/typescript-estree": { "version": "2.22.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.22.0.tgz", @@ -5909,27 +6200,47 @@ "dev": true }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + } } }, "@jest/types": { @@ -6070,58 +6381,96 @@ } }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" + "walker": "^1.0.7", + "which": "^2.0.2" + }, + "dependencies": { + "@jest/types": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + } } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + } } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -6191,6 +6540,15 @@ "is-number": "^7.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -6521,6 +6879,15 @@ "node-releases": "^1.1.50" } }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, "bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -7156,9 +7523,9 @@ } }, "csstype": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz", - "integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==" + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz", + "integrity": "sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q==" }, "dashdash": { "version": "1.14.1", @@ -7194,9 +7561,9 @@ } }, "dayjs": { - "version": "1.8.19", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.19.tgz", - "integrity": "sha512-7kqOoj3oQSmqbvtvGFLU5iYqies+SqUiEGNT0UtUPPxcPYgY1BrkXR0Cq2R9HYSimBXN+xHkEN4Hi399W+Ovlg==" + "version": "1.8.23", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.23.tgz", + "integrity": "sha512-NmYHMFONftoZbeOhVz6jfiXI4zSiPN6NoVWJgC0aZQfYVwzy/ZpESPHuCcI0B8BUMpSJQ08zenHDbofOLKq8hQ==" }, "debounce": { "version": "1.2.0", @@ -7451,6 +7818,11 @@ "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", "dev": true }, + "didyoumean": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz", + "integrity": "sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=" + }, "diff-sequences": { "version": "25.1.0", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.1.0.tgz", @@ -8476,41 +8848,41 @@ } }, "expect": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-25.1.0.tgz", - "integrity": "sha512-wqHzuoapQkhc3OKPlrpetsfueuEiMf3iWh0R8+duCu9PIjXoP7HgD5aeypwTnXUAjC8aMsiVDaWwlbJ1RlQ38g==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-25.2.1.tgz", + "integrity": "sha512-mRvuu0xujdgYuS0S2dZ489PGAcXl60blmsLofaq7heqn+ZcUOox+VWQvrCee/x+/0WBpxDs7pBWuFYNO5U+txQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "ansi-styles": "^4.0.0", - "jest-get-type": "^25.1.0", - "jest-matcher-utils": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-regex-util": "^25.1.0" + "jest-get-type": "^25.2.1", + "jest-matcher-utils": "^25.2.1", + "jest-message-util": "^25.2.1", + "jest-regex-util": "^25.2.1" }, "dependencies": { "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.1.tgz", + "integrity": "sha512-v3tkMr5AeVm6R23wnZdC5dzXdHPFa6j2uiTC15iHISYkGIilE9O1qmAYKELHPXZifDbz9c8WwzsqoN8K8uG4jg==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", + "@jest/source-map": "^25.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "slash": "^3.0.0" } }, "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -8519,46 +8891,46 @@ } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.1.tgz", + "integrity": "sha512-E0tlWh2iOELRLbbPEngs3Dsx88vGBQOs6O3w46YeXfMHlwwqzWrlvoeUq6kRlHRm1O8H+EBr60Wtrwh20C+zWQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -8699,39 +9071,40 @@ } }, "jest-get-type": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", - "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.1.tgz", + "integrity": "sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w==", "dev": true }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" + "walker": "^1.0.7", + "which": "^2.0.2" } }, "jest-message-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", - "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.2.1.tgz", + "integrity": "sha512-pxwehr9uPEuCI9bPjBiZxpFMN0+3wny5p7/E3hbV9XjsqREhJJAMf0czvHtgNeUBo2iAiAI9yi9ICKHPOKePEw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/test-result": "^25.2.1", + "@jest/types": "^25.2.1", "@types/stack-utils": "^1.0.1", "chalk": "^3.0.0", "micromatch": "^4.0.2", @@ -8740,39 +9113,48 @@ } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -8795,6 +9177,12 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -8842,6 +9230,15 @@ "is-number": "^7.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -11050,15 +11447,6 @@ "source-map": "^0.6.1" }, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, "istanbul-lib-coverage": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", @@ -11170,20 +11558,20 @@ } }, "jest-changed-files": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-25.1.0.tgz", - "integrity": "sha512-bdL1aHjIVy3HaBO3eEQeemGttsq1BDlHgWcOjEOIAcga7OOEGWHD2WSu8HhL7I1F0mFFyci8VKU4tRNk+qtwDA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-25.2.1.tgz", + "integrity": "sha512-BB4XjM/dJNUAUtchZ2yJq50VK8XXbmgvt1MUD6kzgzoyz9F0+1ZDQ1yNvLl6pfDwKrMBG9GBY1lzaIBO3JByMg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "execa": "^3.2.0", "throat": "^5.0.0" }, "dependencies": { "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -11391,27 +11779,27 @@ }, "dependencies": { "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.1.tgz", + "integrity": "sha512-v3tkMr5AeVm6R23wnZdC5dzXdHPFa6j2uiTC15iHISYkGIilE9O1qmAYKELHPXZifDbz9c8WwzsqoN8K8uG4jg==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", + "@jest/source-map": "^25.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "slash": "^3.0.0" } }, "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -11420,46 +11808,54 @@ } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.1.tgz", + "integrity": "sha512-E0tlWh2iOELRLbbPEngs3Dsx88vGBQOs6O3w46YeXfMHlwwqzWrlvoeUq6kRlHRm1O8H+EBr60Wtrwh20C+zWQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + } } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -11655,72 +12051,73 @@ } }, "jest-get-type": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", - "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.1.tgz", + "integrity": "sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w==", "dev": true }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" + "walker": "^1.0.7", + "which": "^2.0.2" } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" } }, "jest-validate": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.1.0.tgz", - "integrity": "sha512-kGbZq1f02/zVO2+t1KQGSVoCTERc5XeObLwITqC6BTRH3Adv7NZdYqCpKIZLUgpLXf2yISzQ465qOZpul8abXA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.2.1.tgz", + "integrity": "sha512-vGtNFPyvylFfTFFfptzqCy5S3cP/N5JJVwm8gsXeZq8jMmvUngfWtuw+Tr5Wjo+dqOle23td8BE0ruGnsONDmw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "camelcase": "^5.3.1", "chalk": "^3.0.0", - "jest-get-type": "^25.1.0", + "jest-get-type": "^25.2.1", "leven": "^3.1.0", - "pretty-format": "^25.1.0" + "pretty-format": "^25.2.1" } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", @@ -11736,6 +12133,15 @@ "p-locate": "^4.1.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -11783,12 +12189,12 @@ } }, "pretty-format": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", - "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.1.tgz", + "integrity": "sha512-YS+e9oGYIbEeAFgqTU8qeZ3DN2Pz0iaD81ox+iUjLIXVJWeB7Ro/2AnfxRnl/yJJ5R674d7E3jLPuh6bwg0+qw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -11882,6 +12288,15 @@ "is-number": "^7.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -11906,9 +12321,9 @@ } }, "yargs": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.0.tgz", - "integrity": "sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -11921,13 +12336,13 @@ "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^18.1.0" + "yargs-parser": "^18.1.1" } }, "yargs-parser": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.0.tgz", - "integrity": "sha512-o/Jr6JBOv6Yx3pL+5naWSoIA2jJ+ZkMYQG/ie9qFbukBe4uzmBatlXFOiu/tNKRWEtyf+n5w7jc/O16ufqOTdQ==", + "version": "18.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.1.tgz", + "integrity": "sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -11937,76 +12352,77 @@ } }, "jest-config": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-25.1.0.tgz", - "integrity": "sha512-tLmsg4SZ5H7tuhBC5bOja0HEblM0coS3Wy5LTCb2C8ZV6eWLewHyK+3qSq9Bi29zmWQ7ojdCd3pxpx4l4d2uGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-25.2.1.tgz", + "integrity": "sha512-Kh6a3stGSCtVwucvD9wSMaEQBmU0CfqVjHvf0X0iLfCrZfsezvV+sGgRWQAidaTIvo51yAaL217xOwEETMqh6w==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^25.1.0", - "@jest/types": "^25.1.0", - "babel-jest": "^25.1.0", + "@jest/test-sequencer": "^25.2.1", + "@jest/types": "^25.2.1", + "babel-jest": "^25.2.1", "chalk": "^3.0.0", + "deepmerge": "^4.2.2", "glob": "^7.1.1", - "jest-environment-jsdom": "^25.1.0", - "jest-environment-node": "^25.1.0", - "jest-get-type": "^25.1.0", - "jest-jasmine2": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-resolve": "^25.1.0", - "jest-util": "^25.1.0", - "jest-validate": "^25.1.0", + "jest-environment-jsdom": "^25.2.1", + "jest-environment-node": "^25.2.1", + "jest-get-type": "^25.2.1", + "jest-jasmine2": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-resolve": "^25.2.1", + "jest-util": "^25.2.1", + "jest-validate": "^25.2.1", "micromatch": "^4.0.2", - "pretty-format": "^25.1.0", - "realpath-native": "^1.1.0" + "pretty-format": "^25.2.1", + "realpath-native": "^2.0.0" }, "dependencies": { "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.1.tgz", + "integrity": "sha512-v3tkMr5AeVm6R23wnZdC5dzXdHPFa6j2uiTC15iHISYkGIilE9O1qmAYKELHPXZifDbz9c8WwzsqoN8K8uG4jg==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", + "@jest/source-map": "^25.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "slash": "^3.0.0" } }, "@jest/environment": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", - "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.2.1.tgz", + "integrity": "sha512-aeA3UlUmpblmv2CHBcNA7LvcXlcCtRpXaKKFVooRy9/Jk8B4IZAZMfrML/d+1cG5FpF17s4JVdu1kx0mbnaqTQ==", "dev": true, "requires": { - "@jest/fake-timers": "^25.1.0", - "@jest/types": "^25.1.0", - "jest-mock": "^25.1.0" + "@jest/fake-timers": "^25.2.1", + "@jest/types": "^25.2.1", + "jest-mock": "^25.2.1" } }, "@jest/fake-timers": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", - "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.2.1.tgz", + "integrity": "sha512-H1OC8AktrGTD10NHBauICkRCv7VOOrsgI8xokifAsxJMYhqoKBtJZbk2YpbrtnmdTUnk+qoxPUk+Mufwnl44iQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-util": "^25.1.0", + "@jest/types": "^25.2.1", + "jest-message-util": "^25.2.1", + "jest-mock": "^25.2.1", + "jest-util": "^25.2.1", "lolex": "^5.0.0" } }, "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -12015,46 +12431,46 @@ } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.1.tgz", + "integrity": "sha512-E0tlWh2iOELRLbbPEngs3Dsx88vGBQOs6O3w46YeXfMHlwwqzWrlvoeUq6kRlHRm1O8H+EBr60Wtrwh20C+zWQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -12098,6 +12514,21 @@ "picomatch": "^2.0.4" } }, + "babel-jest": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-25.2.1.tgz", + "integrity": "sha512-OiBpQGYtV4rWMuFneIaEsqJB0VdoOBw4SqwO4hA2EhDY/O8RylQ20JwALkxv8iv+CYnyrZZfF+DELPgrdrkRIw==", + "dev": true, + "requires": { + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^25.2.1", + "chalk": "^3.0.0", + "slash": "^3.0.0" + } + }, "babel-plugin-istanbul": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", @@ -12111,6 +12542,26 @@ "test-exclude": "^6.0.0" } }, + "babel-plugin-jest-hoist": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.2.1.tgz", + "integrity": "sha512-HysbCQfJhxLlyxDbKcB2ucGYV0LjqK4h6dBoI3RtFuOxTiTWK6XGZMsHb0tGh8iJdV4hC6Z2GCHzVvDeh9i0lQ==", + "dev": true, + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-25.2.1.tgz", + "integrity": "sha512-zXHJBM5iR8oEO4cvdF83AQqqJf3tJrXy3x8nfu2Nlqvn4cneg4Ca8M7cQvC5S9BzDDy1O0tZ9iXru9J6E3ym+A==", + "dev": true, + "requires": { + "@babel/plugin-syntax-bigint": "^7.0.0", + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^25.2.1" + } + }, "braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", @@ -12230,53 +12681,54 @@ } }, "jest-environment-jsdom": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.1.0.tgz", - "integrity": "sha512-ILb4wdrwPAOHX6W82GGDUiaXSSOE274ciuov0lztOIymTChKFtC02ddyicRRCdZlB5YSrv3vzr1Z5xjpEe1OHQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.2.1.tgz", + "integrity": "sha512-bUhhhXtgrOgLhsFQFXgao8CQPYAEwtaVvhsF6O0A7Ie2uPONtAKCwuxyOM9WJaz9ag2ci5Pg7i2V2PRfGLl95w==", "dev": true, "requires": { - "@jest/environment": "^25.1.0", - "@jest/fake-timers": "^25.1.0", - "@jest/types": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-util": "^25.1.0", - "jsdom": "^15.1.1" + "@jest/environment": "^25.2.1", + "@jest/fake-timers": "^25.2.1", + "@jest/types": "^25.2.1", + "jest-mock": "^25.2.1", + "jest-util": "^25.2.1", + "jsdom": "^15.2.1" } }, "jest-get-type": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", - "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.1.tgz", + "integrity": "sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w==", "dev": true }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" + "walker": "^1.0.7", + "which": "^2.0.2" } }, "jest-message-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", - "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.2.1.tgz", + "integrity": "sha512-pxwehr9uPEuCI9bPjBiZxpFMN0+3wny5p7/E3hbV9XjsqREhJJAMf0czvHtgNeUBo2iAiAI9yi9ICKHPOKePEw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/test-result": "^25.2.1", + "@jest/types": "^25.2.1", "@types/stack-utils": "^1.0.1", "chalk": "^3.0.0", "micromatch": "^4.0.2", @@ -12285,56 +12737,56 @@ } }, "jest-mock": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", - "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.2.1.tgz", + "integrity": "sha512-ZXcmqpCTG1MEm2AP2q9XiJzdbQ655Pnssj+xQMP1thrW2ptEFrd4vSkxTpxk6rnluLPRKagaHmzUpWNxShMvqQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0" + "@jest/types": "^25.2.1" } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" } }, "jest-validate": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.1.0.tgz", - "integrity": "sha512-kGbZq1f02/zVO2+t1KQGSVoCTERc5XeObLwITqC6BTRH3Adv7NZdYqCpKIZLUgpLXf2yISzQ465qOZpul8abXA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.2.1.tgz", + "integrity": "sha512-vGtNFPyvylFfTFFfptzqCy5S3cP/N5JJVwm8gsXeZq8jMmvUngfWtuw+Tr5Wjo+dqOle23td8BE0ruGnsONDmw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "camelcase": "^5.3.1", "chalk": "^3.0.0", - "jest-get-type": "^25.1.0", + "jest-get-type": "^25.2.1", "leven": "^3.1.0", - "pretty-format": "^25.1.0" + "pretty-format": "^25.2.1" } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", @@ -12375,6 +12827,15 @@ "xml-name-validator": "^3.0.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -12404,17 +12865,23 @@ "dev": true }, "pretty-format": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", - "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.1.tgz", + "integrity": "sha512-YS+e9oGYIbEeAFgqTU8qeZ3DN2Pz0iaD81ox+iUjLIXVJWeB7Ro/2AnfxRnl/yJJ5R674d7E3jLPuh6bwg0+qw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" } }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -12484,6 +12951,15 @@ "webidl-conversions": "^4.0.2" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -12608,9 +13084,9 @@ } }, "jest-docblock": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.1.0.tgz", - "integrity": "sha512-370P/mh1wzoef6hUKiaMcsPtIapY25suP6JqM70V9RJvdKLrV4GaGbfUseUVk4FZJw4oTZ1qSCJNdrClKt5JQA==", + "version": "25.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.2.0.tgz", + "integrity": "sha512-M7ZDbghaxFd2unWkyDFGLZDjPpIbDtEbICXSzwGrUBccFwVG/1dhLLAYX3D+98bFksaJuM0iMZGuIQUzKgnkQw==", "dev": true, "requires": { "detect-newline": "^3.0.0" @@ -12625,22 +13101,22 @@ } }, "jest-each": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.1.0.tgz", - "integrity": "sha512-R9EL8xWzoPySJ5wa0DXFTj7NrzKpRD40Jy+zQDp3Qr/2QmevJgkN9GqioCGtAJ2bW9P/MQRznQHQQhoeAyra7A==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.2.1.tgz", + "integrity": "sha512-2vWAaf11IJsSwkEzGph3un4OMSG4v/3hpM2UqJdeU3peGUgUSn75TlXZGQnT0smbnAr4eE+URW1OpE8U9wl0TA==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", - "jest-get-type": "^25.1.0", - "jest-util": "^25.1.0", - "pretty-format": "^25.1.0" + "jest-get-type": "^25.2.1", + "jest-util": "^25.2.1", + "pretty-format": "^25.2.1" }, "dependencies": { "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -12706,35 +13182,50 @@ "dev": true }, "jest-get-type": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", - "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.1.tgz", + "integrity": "sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" + } + }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" } }, "pretty-format": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", - "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.1.tgz", + "integrity": "sha512-YS+e9oGYIbEeAFgqTU8qeZ3DN2Pz0iaD81ox+iUjLIXVJWeB7Ro/2AnfxRnl/yJJ5R674d7E3jLPuh6bwg0+qw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" } }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -12770,64 +13261,64 @@ } }, "jest-environment-node": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-25.1.0.tgz", - "integrity": "sha512-U9kFWTtAPvhgYY5upnH9rq8qZkj6mYLup5l1caAjjx9uNnkLHN2xgZy5mo4SyLdmrh/EtB9UPpKFShvfQHD0Iw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-25.2.1.tgz", + "integrity": "sha512-HiAAwx4HrkaV9YAyuI56dmPDuTDckJyPpO0BwCu7+Ht2fmlMDhX13HZyyuIGTAIjUrjJiM3paB8tht+0mXtiIA==", "dev": true, "requires": { - "@jest/environment": "^25.1.0", - "@jest/fake-timers": "^25.1.0", - "@jest/types": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-util": "^25.1.0" + "@jest/environment": "^25.2.1", + "@jest/fake-timers": "^25.2.1", + "@jest/types": "^25.2.1", + "jest-mock": "^25.2.1", + "jest-util": "^25.2.1" }, "dependencies": { "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.1.tgz", + "integrity": "sha512-v3tkMr5AeVm6R23wnZdC5dzXdHPFa6j2uiTC15iHISYkGIilE9O1qmAYKELHPXZifDbz9c8WwzsqoN8K8uG4jg==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", + "@jest/source-map": "^25.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "slash": "^3.0.0" } }, "@jest/environment": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", - "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.2.1.tgz", + "integrity": "sha512-aeA3UlUmpblmv2CHBcNA7LvcXlcCtRpXaKKFVooRy9/Jk8B4IZAZMfrML/d+1cG5FpF17s4JVdu1kx0mbnaqTQ==", "dev": true, "requires": { - "@jest/fake-timers": "^25.1.0", - "@jest/types": "^25.1.0", - "jest-mock": "^25.1.0" + "@jest/fake-timers": "^25.2.1", + "@jest/types": "^25.2.1", + "jest-mock": "^25.2.1" } }, "@jest/fake-timers": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", - "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.2.1.tgz", + "integrity": "sha512-H1OC8AktrGTD10NHBauICkRCv7VOOrsgI8xokifAsxJMYhqoKBtJZbk2YpbrtnmdTUnk+qoxPUk+Mufwnl44iQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-util": "^25.1.0", + "@jest/types": "^25.2.1", + "jest-message-util": "^25.2.1", + "jest-mock": "^25.2.1", + "jest-util": "^25.2.1", "lolex": "^5.0.0" } }, "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -12836,46 +13327,46 @@ } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.1.tgz", + "integrity": "sha512-E0tlWh2iOELRLbbPEngs3Dsx88vGBQOs6O3w46YeXfMHlwwqzWrlvoeUq6kRlHRm1O8H+EBr60Wtrwh20C+zWQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -13016,33 +13507,34 @@ } }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" + "walker": "^1.0.7", + "which": "^2.0.2" } }, "jest-message-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", - "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.2.1.tgz", + "integrity": "sha512-pxwehr9uPEuCI9bPjBiZxpFMN0+3wny5p7/E3hbV9XjsqREhJJAMf0czvHtgNeUBo2iAiAI9yi9ICKHPOKePEw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/test-result": "^25.2.1", + "@jest/types": "^25.2.1", "@types/stack-utils": "^1.0.1", "chalk": "^3.0.0", "micromatch": "^4.0.2", @@ -13051,48 +13543,57 @@ } }, "jest-mock": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", - "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.2.1.tgz", + "integrity": "sha512-ZXcmqpCTG1MEm2AP2q9XiJzdbQ655Pnssj+xQMP1thrW2ptEFrd4vSkxTpxk6rnluLPRKagaHmzUpWNxShMvqQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0" + "@jest/types": "^25.2.1" } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -13115,6 +13616,12 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -13162,6 +13669,15 @@ "is-number": "^7.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -13212,76 +13728,76 @@ } }, "jest-jasmine2": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.1.0.tgz", - "integrity": "sha512-GdncRq7jJ7sNIQ+dnXvpKO2MyP6j3naNK41DTTjEAhLEdpImaDA9zSAZwDhijjSF/D7cf4O5fdyUApGBZleaEg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.2.1.tgz", + "integrity": "sha512-He8HdO9jx1LdEaof2vjnvKeJeRPYnn+zXz32X8Z/iOUgAgmP7iZActUkiCCiTazSZlaGlY1iK+LOrqnpGQ0+UA==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", - "@jest/environment": "^25.1.0", - "@jest/source-map": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/environment": "^25.2.1", + "@jest/source-map": "^25.2.1", + "@jest/test-result": "^25.2.1", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "co": "^4.6.0", - "expect": "^25.1.0", + "expect": "^25.2.1", "is-generator-fn": "^2.0.0", - "jest-each": "^25.1.0", - "jest-matcher-utils": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-runtime": "^25.1.0", - "jest-snapshot": "^25.1.0", - "jest-util": "^25.1.0", - "pretty-format": "^25.1.0", + "jest-each": "^25.2.1", + "jest-matcher-utils": "^25.2.1", + "jest-message-util": "^25.2.1", + "jest-runtime": "^25.2.1", + "jest-snapshot": "^25.2.1", + "jest-util": "^25.2.1", + "pretty-format": "^25.2.1", "throat": "^5.0.0" }, "dependencies": { "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.1.tgz", + "integrity": "sha512-v3tkMr5AeVm6R23wnZdC5dzXdHPFa6j2uiTC15iHISYkGIilE9O1qmAYKELHPXZifDbz9c8WwzsqoN8K8uG4jg==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", + "@jest/source-map": "^25.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "slash": "^3.0.0" } }, "@jest/environment": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", - "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.2.1.tgz", + "integrity": "sha512-aeA3UlUmpblmv2CHBcNA7LvcXlcCtRpXaKKFVooRy9/Jk8B4IZAZMfrML/d+1cG5FpF17s4JVdu1kx0mbnaqTQ==", "dev": true, "requires": { - "@jest/fake-timers": "^25.1.0", - "@jest/types": "^25.1.0", - "jest-mock": "^25.1.0" + "@jest/fake-timers": "^25.2.1", + "@jest/types": "^25.2.1", + "jest-mock": "^25.2.1" } }, "@jest/fake-timers": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", - "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.2.1.tgz", + "integrity": "sha512-H1OC8AktrGTD10NHBauICkRCv7VOOrsgI8xokifAsxJMYhqoKBtJZbk2YpbrtnmdTUnk+qoxPUk+Mufwnl44iQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-util": "^25.1.0", + "@jest/types": "^25.2.1", + "jest-message-util": "^25.2.1", + "jest-mock": "^25.2.1", + "jest-util": "^25.2.1", "lolex": "^5.0.0" } }, "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -13290,46 +13806,46 @@ } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.1.tgz", + "integrity": "sha512-E0tlWh2iOELRLbbPEngs3Dsx88vGBQOs6O3w46YeXfMHlwwqzWrlvoeUq6kRlHRm1O8H+EBr60Wtrwh20C+zWQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -13476,33 +13992,34 @@ } }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" + "walker": "^1.0.7", + "which": "^2.0.2" } }, "jest-message-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", - "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.2.1.tgz", + "integrity": "sha512-pxwehr9uPEuCI9bPjBiZxpFMN0+3wny5p7/E3hbV9XjsqREhJJAMf0czvHtgNeUBo2iAiAI9yi9ICKHPOKePEw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/test-result": "^25.2.1", + "@jest/types": "^25.2.1", "@types/stack-utils": "^1.0.1", "chalk": "^3.0.0", "micromatch": "^4.0.2", @@ -13511,48 +14028,57 @@ } }, "jest-mock": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", - "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.2.1.tgz", + "integrity": "sha512-ZXcmqpCTG1MEm2AP2q9XiJzdbQ655Pnssj+xQMP1thrW2ptEFrd4vSkxTpxk6rnluLPRKagaHmzUpWNxShMvqQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0" + "@jest/types": "^25.2.1" } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -13576,17 +14102,23 @@ "dev": true }, "pretty-format": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", - "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.1.tgz", + "integrity": "sha512-YS+e9oGYIbEeAFgqTU8qeZ3DN2Pz0iaD81ox+iUjLIXVJWeB7Ro/2AnfxRnl/yJJ5R674d7E3jLPuh6bwg0+qw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" } }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -13640,6 +14172,15 @@ "is-number": "^7.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -13655,19 +14196,19 @@ } }, "jest-leak-detector": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.1.0.tgz", - "integrity": "sha512-3xRI264dnhGaMHRvkFyEKpDeaRzcEBhyNrOG5oT8xPxOyUAblIAQnpiR3QXu4wDor47MDTiHbiFcbypdLcLW5w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.2.1.tgz", + "integrity": "sha512-bsxjjFksjLWNqC8aLsN0KO2KQ3tiqPqmFpYt+0y4RLHc1dqaThQL68jra5y1f/yhX3dNC8ugksDvqnGxwxjo4w==", "dev": true, "requires": { - "jest-get-type": "^25.1.0", - "pretty-format": "^25.1.0" + "jest-get-type": "^25.2.1", + "pretty-format": "^25.2.1" }, "dependencies": { "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -13733,18 +14274,18 @@ "dev": true }, "jest-get-type": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", - "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.1.tgz", + "integrity": "sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w==", "dev": true }, "pretty-format": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", - "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.1.tgz", + "integrity": "sha512-YS+e9oGYIbEeAFgqTU8qeZ3DN2Pz0iaD81ox+iUjLIXVJWeB7Ro/2AnfxRnl/yJJ5R674d7E3jLPuh6bwg0+qw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -13762,21 +14303,21 @@ } }, "jest-matcher-utils": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz", - "integrity": "sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.2.1.tgz", + "integrity": "sha512-uuoYY8W6eeVxHUEOvrKIVVTl9X6RP+ohQn2Ta2W8OOLMN6oA8pZUKQEPGxLsSqB3RKfpTueurMLrxDTEZGllsA==", "dev": true, "requires": { "chalk": "^3.0.0", - "jest-diff": "^25.1.0", - "jest-get-type": "^25.1.0", - "pretty-format": "^25.1.0" + "jest-diff": "^25.2.1", + "jest-get-type": "^25.2.1", + "pretty-format": "^25.2.1" }, "dependencies": { "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -13835,25 +14376,43 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "diff-sequences": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.1.tgz", + "integrity": "sha512-foe7dXnGlSh3jR1ovJmdv+77VQj98eKCHHwJPbZ2eEf0fHwKbkZicpPxEch9smZ+n2dnF6QFwkOQdLq9hpeJUg==", + "dev": true + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "jest-diff": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.2.1.tgz", + "integrity": "sha512-e/TU8VLBBGQQS9tXA5B5LeT806jh7CHUeHbBfrU9UvA2zTbOTRz71UD6fAP1HAhzUEyCVLU2ZP5e8X16A9b0Fg==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "diff-sequences": "^25.2.1", + "jest-get-type": "^25.2.1", + "pretty-format": "^25.2.1" + } + }, "jest-get-type": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", - "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.1.tgz", + "integrity": "sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w==", "dev": true }, "pretty-format": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", - "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.1.tgz", + "integrity": "sha512-YS+e9oGYIbEeAFgqTU8qeZ3DN2Pz0iaD81ox+iUjLIXVJWeB7Ro/2AnfxRnl/yJJ5R674d7E3jLPuh6bwg0+qw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" @@ -13906,22 +14465,23 @@ "dev": true }, "jest-resolve": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.1.0.tgz", - "integrity": "sha512-XkBQaU1SRCHj2Evz2Lu4Czs+uIgJXWypfO57L7JYccmAXv4slXA6hzNblmcRmf7P3cQ1mE7fL3ABV6jAwk4foQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.2.1.tgz", + "integrity": "sha512-5rVc6khEckNH62adcR+jlYd34/jBO/U22VHf+elmyO6UBHNWXSbfy63+spJRN4GQ/0dbu6Hi6ZVdR58bmNG2Eg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "browser-resolve": "^1.11.3", "chalk": "^3.0.0", "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" + "realpath-native": "^2.0.0", + "resolve": "^1.15.1" }, "dependencies": { "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -13980,6 +14540,21 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -13992,20 +14567,20 @@ } }, "jest-resolve-dependencies": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-25.1.0.tgz", - "integrity": "sha512-Cu/Je38GSsccNy4I2vL12ZnBlD170x2Oh1devzuM9TLH5rrnLW1x51lN8kpZLYTvzx9j+77Y5pqBaTqfdzVzrw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-25.2.1.tgz", + "integrity": "sha512-fnct/NyrBpBAVUIMa0M876ubufHP/2Rrc038+gCpVT1s7kazV7ZPFlmGfInahCIthbMr644uzt6pnSvmQgTPGg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-snapshot": "^25.1.0" + "@jest/types": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-snapshot": "^25.2.1" }, "dependencies": { "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -14065,9 +14640,9 @@ "dev": true }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "supports-color": { @@ -14082,78 +14657,78 @@ } }, "jest-runner": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-25.1.0.tgz", - "integrity": "sha512-su3O5fy0ehwgt+e8Wy7A8CaxxAOCMzL4gUBftSs0Ip32S0epxyZPDov9Znvkl1nhVOJNf4UwAsnqfc3plfQH9w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-25.2.1.tgz", + "integrity": "sha512-eONqmMQ2vvKh9BsmJmPmv22DqezFSnwX97rj0L5LvPxQNXTz+rpx7nWiKA7xlvOykLFcspw6worK3+AzhwHWhQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/environment": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/environment": "^25.2.1", + "@jest/test-result": "^25.2.1", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.3", - "jest-config": "^25.1.0", - "jest-docblock": "^25.1.0", - "jest-haste-map": "^25.1.0", - "jest-jasmine2": "^25.1.0", - "jest-leak-detector": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-resolve": "^25.1.0", - "jest-runtime": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-config": "^25.2.1", + "jest-docblock": "^25.2.0", + "jest-haste-map": "^25.2.1", + "jest-jasmine2": "^25.2.1", + "jest-leak-detector": "^25.2.1", + "jest-message-util": "^25.2.1", + "jest-resolve": "^25.2.1", + "jest-runtime": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "source-map-support": "^0.5.6", "throat": "^5.0.0" }, "dependencies": { "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.1.tgz", + "integrity": "sha512-v3tkMr5AeVm6R23wnZdC5dzXdHPFa6j2uiTC15iHISYkGIilE9O1qmAYKELHPXZifDbz9c8WwzsqoN8K8uG4jg==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", + "@jest/source-map": "^25.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "slash": "^3.0.0" } }, "@jest/environment": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", - "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.2.1.tgz", + "integrity": "sha512-aeA3UlUmpblmv2CHBcNA7LvcXlcCtRpXaKKFVooRy9/Jk8B4IZAZMfrML/d+1cG5FpF17s4JVdu1kx0mbnaqTQ==", "dev": true, "requires": { - "@jest/fake-timers": "^25.1.0", - "@jest/types": "^25.1.0", - "jest-mock": "^25.1.0" + "@jest/fake-timers": "^25.2.1", + "@jest/types": "^25.2.1", + "jest-mock": "^25.2.1" } }, "@jest/fake-timers": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", - "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.2.1.tgz", + "integrity": "sha512-H1OC8AktrGTD10NHBauICkRCv7VOOrsgI8xokifAsxJMYhqoKBtJZbk2YpbrtnmdTUnk+qoxPUk+Mufwnl44iQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-util": "^25.1.0", + "@jest/types": "^25.2.1", + "jest-message-util": "^25.2.1", + "jest-mock": "^25.2.1", + "jest-util": "^25.2.1", "lolex": "^5.0.0" } }, "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -14162,46 +14737,46 @@ } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.1.tgz", + "integrity": "sha512-E0tlWh2iOELRLbbPEngs3Dsx88vGBQOs6O3w46YeXfMHlwwqzWrlvoeUq6kRlHRm1O8H+EBr60Wtrwh20C+zWQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -14342,33 +14917,34 @@ } }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" + "walker": "^1.0.7", + "which": "^2.0.2" } }, "jest-message-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", - "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.2.1.tgz", + "integrity": "sha512-pxwehr9uPEuCI9bPjBiZxpFMN0+3wny5p7/E3hbV9XjsqREhJJAMf0czvHtgNeUBo2iAiAI9yi9ICKHPOKePEw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/test-result": "^25.2.1", + "@jest/types": "^25.2.1", "@types/stack-utils": "^1.0.1", "chalk": "^3.0.0", "micromatch": "^4.0.2", @@ -14377,48 +14953,57 @@ } }, "jest-mock": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", - "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.2.1.tgz", + "integrity": "sha512-ZXcmqpCTG1MEm2AP2q9XiJzdbQ655Pnssj+xQMP1thrW2ptEFrd4vSkxTpxk6rnluLPRKagaHmzUpWNxShMvqQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0" + "@jest/types": "^25.2.1" } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -14441,6 +15026,12 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -14494,6 +15085,15 @@ "is-number": "^7.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -14509,84 +15109,84 @@ } }, "jest-runtime": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.1.0.tgz", - "integrity": "sha512-mpPYYEdbExKBIBB16ryF6FLZTc1Rbk9Nx0ryIpIMiDDkOeGa0jQOKVI/QeGvVGlunKKm62ywcioeFVzIbK03bA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.2.1.tgz", + "integrity": "sha512-eHEnMrOVeGe8mGDoTZWqCdbsM3RwxsMKVMAj1RTZ4LtRyWqQHKec3RiJiJST5LVj3Mw72clr0U21yoE4p5Mq3w==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/environment": "^25.1.0", - "@jest/source-map": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/environment": "^25.2.1", + "@jest/source-map": "^25.2.1", + "@jest/test-result": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/yargs": "^15.0.0", "chalk": "^3.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.3", - "jest-config": "^25.1.0", - "jest-haste-map": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-resolve": "^25.1.0", - "jest-snapshot": "^25.1.0", - "jest-util": "^25.1.0", - "jest-validate": "^25.1.0", - "realpath-native": "^1.1.0", + "jest-config": "^25.2.1", + "jest-haste-map": "^25.2.1", + "jest-message-util": "^25.2.1", + "jest-mock": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-resolve": "^25.2.1", + "jest-snapshot": "^25.2.1", + "jest-util": "^25.2.1", + "jest-validate": "^25.2.1", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "strip-bom": "^4.0.0", - "yargs": "^15.0.0" + "yargs": "^15.3.1" }, "dependencies": { "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.1.tgz", + "integrity": "sha512-v3tkMr5AeVm6R23wnZdC5dzXdHPFa6j2uiTC15iHISYkGIilE9O1qmAYKELHPXZifDbz9c8WwzsqoN8K8uG4jg==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", + "@jest/source-map": "^25.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "slash": "^3.0.0" } }, "@jest/environment": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", - "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.2.1.tgz", + "integrity": "sha512-aeA3UlUmpblmv2CHBcNA7LvcXlcCtRpXaKKFVooRy9/Jk8B4IZAZMfrML/d+1cG5FpF17s4JVdu1kx0mbnaqTQ==", "dev": true, "requires": { - "@jest/fake-timers": "^25.1.0", - "@jest/types": "^25.1.0", - "jest-mock": "^25.1.0" + "@jest/fake-timers": "^25.2.1", + "@jest/types": "^25.2.1", + "jest-mock": "^25.2.1" } }, "@jest/fake-timers": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", - "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.2.1.tgz", + "integrity": "sha512-H1OC8AktrGTD10NHBauICkRCv7VOOrsgI8xokifAsxJMYhqoKBtJZbk2YpbrtnmdTUnk+qoxPUk+Mufwnl44iQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-mock": "^25.1.0", - "jest-util": "^25.1.0", + "@jest/types": "^25.2.1", + "jest-message-util": "^25.2.1", + "jest-mock": "^25.2.1", + "jest-util": "^25.2.1", "lolex": "^5.0.0" } }, "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -14595,46 +15195,46 @@ } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.1.tgz", + "integrity": "sha512-E0tlWh2iOELRLbbPEngs3Dsx88vGBQOs6O3w46YeXfMHlwwqzWrlvoeUq6kRlHRm1O8H+EBr60Wtrwh20C+zWQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -14820,39 +15420,40 @@ } }, "jest-get-type": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", - "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.1.tgz", + "integrity": "sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w==", "dev": true }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" + "walker": "^1.0.7", + "which": "^2.0.2" } }, "jest-message-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", - "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.2.1.tgz", + "integrity": "sha512-pxwehr9uPEuCI9bPjBiZxpFMN0+3wny5p7/E3hbV9XjsqREhJJAMf0czvHtgNeUBo2iAiAI9yi9ICKHPOKePEw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/test-result": "^25.2.1", + "@jest/types": "^25.2.1", "@types/stack-utils": "^1.0.1", "chalk": "^3.0.0", "micromatch": "^4.0.2", @@ -14861,56 +15462,56 @@ } }, "jest-mock": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", - "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.2.1.tgz", + "integrity": "sha512-ZXcmqpCTG1MEm2AP2q9XiJzdbQ655Pnssj+xQMP1thrW2ptEFrd4vSkxTpxk6rnluLPRKagaHmzUpWNxShMvqQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0" + "@jest/types": "^25.2.1" } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" } }, "jest-validate": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.1.0.tgz", - "integrity": "sha512-kGbZq1f02/zVO2+t1KQGSVoCTERc5XeObLwITqC6BTRH3Adv7NZdYqCpKIZLUgpLXf2yISzQ465qOZpul8abXA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.2.1.tgz", + "integrity": "sha512-vGtNFPyvylFfTFFfptzqCy5S3cP/N5JJVwm8gsXeZq8jMmvUngfWtuw+Tr5Wjo+dqOle23td8BE0ruGnsONDmw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "camelcase": "^5.3.1", "chalk": "^3.0.0", - "jest-get-type": "^25.1.0", + "jest-get-type": "^25.2.1", "leven": "^3.1.0", - "pretty-format": "^25.1.0" + "pretty-format": "^25.2.1" } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", @@ -14926,6 +15527,15 @@ "p-locate": "^4.1.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -14964,17 +15574,23 @@ "dev": true }, "pretty-format": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", - "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.1.tgz", + "integrity": "sha512-YS+e9oGYIbEeAFgqTU8qeZ3DN2Pz0iaD81ox+iUjLIXVJWeB7Ro/2AnfxRnl/yJJ5R674d7E3jLPuh6bwg0+qw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" } }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -15054,6 +15670,15 @@ "is-number": "^7.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -15078,9 +15703,9 @@ } }, "yargs": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.0.tgz", - "integrity": "sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -15093,13 +15718,13 @@ "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^18.1.0" + "yargs-parser": "^18.1.1" } }, "yargs-parser": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.0.tgz", - "integrity": "sha512-o/Jr6JBOv6Yx3pL+5naWSoIA2jJ+ZkMYQG/ie9qFbukBe4uzmBatlXFOiu/tNKRWEtyf+n5w7jc/O16ufqOTdQ==", + "version": "18.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.1.tgz", + "integrity": "sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -15114,48 +15739,49 @@ "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==" }, "jest-snapshot": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.1.0.tgz", - "integrity": "sha512-xZ73dFYN8b/+X2hKLXz4VpBZGIAn7muD/DAg+pXtDzDGw3iIV10jM7WiHqhCcpDZfGiKEj7/2HXAEPtHTj0P2A==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.2.1.tgz", + "integrity": "sha512-5Wd8SEJVTXqQvzkQpuYqQt1QTlRj2XVUV/iaEzO+AeSVg6g5pQWu0z2iLdSBlVeWRrX0MyZn6dhxYGwEq4wW0w==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", + "@types/prettier": "^1.19.0", "chalk": "^3.0.0", - "expect": "^25.1.0", - "jest-diff": "^25.1.0", - "jest-get-type": "^25.1.0", - "jest-matcher-utils": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-resolve": "^25.1.0", - "mkdirp": "^0.5.1", + "expect": "^25.2.1", + "jest-diff": "^25.2.1", + "jest-get-type": "^25.2.1", + "jest-matcher-utils": "^25.2.1", + "jest-message-util": "^25.2.1", + "jest-resolve": "^25.2.1", + "make-dir": "^3.0.0", "natural-compare": "^1.4.0", - "pretty-format": "^25.1.0", - "semver": "^7.1.1" + "pretty-format": "^25.2.1", + "semver": "^6.3.0" }, "dependencies": { "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.1.tgz", + "integrity": "sha512-v3tkMr5AeVm6R23wnZdC5dzXdHPFa6j2uiTC15iHISYkGIilE9O1qmAYKELHPXZifDbz9c8WwzsqoN8K8uG4jg==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", + "@jest/source-map": "^25.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "slash": "^3.0.0" } }, "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -15164,46 +15790,46 @@ } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.1.tgz", + "integrity": "sha512-E0tlWh2iOELRLbbPEngs3Dsx88vGBQOs6O3w46YeXfMHlwwqzWrlvoeUq6kRlHRm1O8H+EBr60Wtrwh20C+zWQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -15300,6 +15926,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "diff-sequences": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.1.tgz", + "integrity": "sha512-foe7dXnGlSh3jR1ovJmdv+77VQj98eKCHHwJPbZ2eEf0fHwKbkZicpPxEch9smZ+n2dnF6QFwkOQdLq9hpeJUg==", + "dev": true + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -15347,50 +15979,55 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + } + }, + "jest-diff": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.2.1.tgz", + "integrity": "sha512-e/TU8VLBBGQQS9tXA5B5LeT806jh7CHUeHbBfrU9UvA2zTbOTRz71UD6fAP1HAhzUEyCVLU2ZP5e8X16A9b0Fg==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "diff-sequences": "^25.2.1", + "jest-get-type": "^25.2.1", + "pretty-format": "^25.2.1" } }, "jest-get-type": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", - "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.1.tgz", + "integrity": "sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w==", "dev": true }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" + "walker": "^1.0.7", + "which": "^2.0.2" } }, "jest-message-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", - "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.2.1.tgz", + "integrity": "sha512-pxwehr9uPEuCI9bPjBiZxpFMN0+3wny5p7/E3hbV9XjsqREhJJAMf0czvHtgNeUBo2iAiAI9yi9ICKHPOKePEw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/test-result": "^25.2.1", + "@jest/types": "^25.2.1", "@types/stack-utils": "^1.0.1", "chalk": "^3.0.0", "micromatch": "^4.0.2", @@ -15399,39 +16036,48 @@ } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -15455,17 +16101,29 @@ "dev": true }, "pretty-format": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", - "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.1.tgz", + "integrity": "sha512-YS+e9oGYIbEeAFgqTU8qeZ3DN2Pz0iaD81ox+iUjLIXVJWeB7Ro/2AnfxRnl/yJJ5R674d7E3jLPuh6bwg0+qw==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", "react-is": "^16.12.0" } }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -15507,6 +16165,15 @@ "is-number": "^7.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -15573,41 +16240,41 @@ } }, "jest-watcher": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-25.1.0.tgz", - "integrity": "sha512-Q9eZ7pyaIr6xfU24OeTg4z1fUqBF/4MP6J801lyQfg7CsnZ/TCzAPvCfckKdL5dlBBEKBeHV0AdyjFZ5eWj4ig==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-25.2.1.tgz", + "integrity": "sha512-m35rftCYE2EEh01+IIpQMpdB9VXBAjITZvgP4drd/LI3JEJIdd0Pkf/qJZ3oiMQJdqmuwYcTqE+BL40MxVv83Q==", "dev": true, "requires": { - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/test-result": "^25.2.1", + "@jest/types": "^25.2.1", "ansi-escapes": "^4.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "string-length": "^3.1.0" }, "dependencies": { "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", "dev": true }, "@jest/console": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", - "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.1.tgz", + "integrity": "sha512-v3tkMr5AeVm6R23wnZdC5dzXdHPFa6j2uiTC15iHISYkGIilE9O1qmAYKELHPXZifDbz9c8WwzsqoN8K8uG4jg==", "dev": true, "requires": { - "@jest/source-map": "^25.1.0", + "@jest/source-map": "^25.2.1", "chalk": "^3.0.0", - "jest-util": "^25.1.0", + "jest-util": "^25.2.1", "slash": "^3.0.0" } }, "@jest/source-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", - "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz", + "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -15616,46 +16283,46 @@ } }, "@jest/test-result": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", - "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.1.tgz", + "integrity": "sha512-E0tlWh2iOELRLbbPEngs3Dsx88vGBQOs6O3w46YeXfMHlwwqzWrlvoeUq6kRlHRm1O8H+EBr60Wtrwh20C+zWQ==", "dev": true, "requires": { - "@jest/console": "^25.1.0", - "@jest/transform": "^25.1.0", - "@jest/types": "^25.1.0", + "@jest/console": "^25.2.1", + "@jest/transform": "^25.2.1", + "@jest/types": "^25.2.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", - "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.1.tgz", + "integrity": "sha512-puoD5EfqPeZ5m0dV9l8+PMdOVdRjeWcaEjGkH+eG45l0nPJ2vRcxu8J6CRl/6nQ5ZTHgg7LuM9C6FauNpdRpUA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.1.0", - "jest-regex-util": "^25.1.0", - "jest-util": "^25.1.0", + "jest-haste-map": "^25.2.1", + "jest-regex-util": "^25.2.1", + "jest-util": "^25.2.1", "micromatch": "^4.0.2", "pirates": "^4.0.1", - "realpath-native": "^1.1.0", + "realpath-native": "^2.0.0", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", - "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.1.tgz", + "integrity": "sha512-WuGFGJ3Rrycg+5ZwQTWKjr21M9psANPAWYD28K42hSeUzhv1H591VXIoq0tjs00mydhNOgVOkKSpzRS3CrOYFw==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", @@ -15805,58 +16472,68 @@ } }, "jest-haste-map": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", - "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.1.tgz", + "integrity": "sha512-svz3KbQmv9qeomR0LlRjQfoi7lQbZQkC39m7uHFKhqyEuX4F6DH6HayNPSEbTCZDx6d9/ljxfAcxlPpgQvrpvQ==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", "graceful-fs": "^4.2.3", - "jest-serializer": "^25.1.0", - "jest-util": "^25.1.0", - "jest-worker": "^25.1.0", + "jest-serializer": "^25.2.1", + "jest-util": "^25.2.1", + "jest-worker": "^25.2.1", "micromatch": "^4.0.2", "sane": "^4.0.3", - "walker": "^1.0.7" + "walker": "^1.0.7", + "which": "^2.0.2" } }, "jest-regex-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", - "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz", + "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==", "dev": true }, "jest-serializer": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", - "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz", + "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==", "dev": true }, "jest-util": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", - "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.1.tgz", + "integrity": "sha512-oFVMSY/7flrSgEE/B+RApaBZOdLURXRnXCf4COV5td9uRidxudyjA64I1xk2h9Pf3jloSArm96e2FKAbFs0DYg==", "dev": true, "requires": { - "@jest/types": "^25.1.0", + "@jest/types": "^25.2.1", "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1" + "make-dir": "^3.0.0" } }, "jest-worker": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", - "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz", + "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==", "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -15879,6 +16556,12 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -15932,6 +16615,15 @@ "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", "dev": true }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -16000,6 +16692,11 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, + "jsc-android": { + "version": "245459.0.0", + "resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-245459.0.0.tgz", + "integrity": "sha512-wkjURqwaB1daNkDi2OYYbsLnIdC/lUM2nPXQKRs5pqEU9chDg435bjvo+LSaHotDENygHQDHe+ntUkkw2gwMtg==" + }, "jsdom": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", @@ -16403,6 +17100,12 @@ "lodash.isarray": "^3.0.0" } }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, "lodash.pickby": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", @@ -16489,6 +17192,12 @@ } } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "makeerror": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", @@ -17460,6 +18169,15 @@ } } }, + "mock-socket": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.0.3.tgz", + "integrity": "sha512-SxIiD2yE/By79p3cNAAXyLQWTvEFNEzcAO7PH+DzRqKSFaplAPFjiQLmw8ofmpCsZf+Rhfn2/xCJagpdGmYdTw==", + "dev": true, + "requires": { + "url-parse": "^1.4.4" + } + }, "moment": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", @@ -17480,15 +18198,15 @@ "dev": true }, "morgan": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", - "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", "requires": { - "basic-auth": "~2.0.0", + "basic-auth": "~2.0.1", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "~2.0.0", "on-finished": "~2.3.0", - "on-headers": "~1.0.1" + "on-headers": "~1.0.2" }, "dependencies": { "debug": { @@ -17499,6 +18217,11 @@ "ms": "2.0.0" } }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -17582,6 +18305,18 @@ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, + "nock": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/nock/-/nock-12.0.3.tgz", + "integrity": "sha512-QNb/j8kbFnKCiyqi9C5DD0jH/FubFGj5rt9NQFONXwQm3IPB0CULECg/eS3AU1KgZb/6SwUa4/DTRKhVxkGABw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.13", + "propagate": "^2.0.0" + } + }, "node-fetch": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", @@ -18762,9 +19497,9 @@ } }, "prompts": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.1.tgz", - "integrity": "sha512-qIP2lQyCwYbdzcqHIUi2HAxiWixhoM9OdLCWf8txXsapC/X9YdsCoeyRIXE/GP+Q0J37Q7+XN/MFqbUa7IzXNA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", + "integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==", "dev": true, "requires": { "kleur": "^3.0.3", @@ -18792,6 +19527,12 @@ "reflect.ownkeys": "^0.2.0" } }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, "proper-lockfile": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-1.2.0.tgz", @@ -19034,10 +19775,32 @@ "whatwg-fetch": "^3.0.0" }, "dependencies": { + "@jest/types": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.3.tgz", + "integrity": "sha512-6oLQwO9mKif3Uph3RX5J1i3S7X7xtDHWBaaaoeKw8hOzV6YUd0qDcYcHZ6QXMHDIzSr7zzrEa51o2Ovlj6AtKQ==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, "@react-native-community/cli": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-3.0.4.tgz", - "integrity": "sha512-kt+ENtC+eRUSfWPbbpx3r7fAQDcFwgM03VW/lBdVAUjkNxffPFT2GGdK23CJSBOXTjRSiGuwhvwH4Z28PdrlRA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-3.2.1.tgz", + "integrity": "sha512-bZ/bfZ+9r1gQSxp6t7+00DcpC6vmbVYSvzUCFM/yo5k8bhsDdcy8aocscIaXXVGG+v9Edri/Q7hH9ks7L18/Rg==", "requires": { "@hapi/joi": "^15.0.3", "@react-native-community/cli-debugger-ui": "^3.0.0", @@ -19050,6 +19813,7 @@ "connect": "^3.6.5", "cosmiconfig": "^5.1.0", "deepmerge": "^3.2.0", + "didyoumean": "^1.2.1", "envinfo": "^7.1.0", "errorhandler": "^1.5.0", "execa": "^1.0.0", @@ -19070,6 +19834,7 @@ "open": "^6.2.0", "ora": "^3.4.0", "plist": "^3.0.0", + "pretty-format": "^25.1.0", "semver": "^6.3.0", "serve-static": "^1.13.1", "shell-quote": "1.6.1", @@ -19077,6 +19842,41 @@ "sudo-prompt": "^9.0.0", "wcwidth": "^1.0.1", "ws": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "pretty-format": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.3.tgz", + "integrity": "sha512-IP4+5UOAVGoyqC/DiomOeHBUKN6q00gfyT2qpAsRH64tgOKB2yF7FHJXC18OCiU0/YFierACup/zdCOWw0F/0w==", + "requires": { + "@jest/types": "^25.2.3", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + } + } + }, + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, "chardet": { @@ -19084,6 +19884,19 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "deepmerge": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz", @@ -19108,6 +19921,11 @@ "path-exists": "^4.0.0" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, "inquirer": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", @@ -19139,11 +19957,6 @@ } } }, - "jsc-android": { - "version": "245459.0.0", - "resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-245459.0.0.tgz", - "integrity": "sha512-wkjURqwaB1daNkDi2OYYbsLnIdC/lUM2nPXQKRs5pqEU9chDg435bjvo+LSaHotDENygHQDHe+ntUkkw2gwMtg==" - }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -19175,6 +19988,14 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" + } + }, "ws": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", @@ -19921,9 +20742,9 @@ } }, "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" }, "regenerator-transform": { "version": "0.14.2", @@ -20667,9 +21488,9 @@ } }, "sisteransi": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.4.tgz", - "integrity": "sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, "slash": { @@ -21026,9 +21847,9 @@ "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==" }, "stacktrace-parser": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.8.tgz", - "integrity": "sha512-ig5rHJSdJrAsVqdb3oAI/8C6aQ7dEwJXoy/TIEIOTzdJHssmn12o6RsFoeQSLHoKjq0lX+kqhmnLDpyQTuWiJA==", + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.9.tgz", + "integrity": "sha512-DRy03ljj0367Ud3OAJHD6eVS/+CvMK2u/djVYuU37fHYcYHoZ8tkFyhbRf7PNG1h3bWLsw+SNTSXrPFe07A7aQ==", "requires": { "type-fest": "^0.7.1" }, @@ -21630,6 +22451,42 @@ "punycode": "^2.1.0" } }, + "ts-jest": { + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.2.1.tgz", + "integrity": "sha512-TnntkEEjuXq/Gxpw7xToarmHbAafgCaAzOpnajnFC6jI7oo1trMzAHA04eWpc3MhV6+yvhE8uUBAmN+teRJh0A==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "mkdirp": "0.x", + "resolve": "1.x", + "semver": "^5.5", + "yargs-parser": "^16.1.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "yargs-parser": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.1.0.tgz", + "integrity": "sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", @@ -21697,6 +22554,12 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "dev": true + }, "ua-parser-js": { "version": "0.7.21", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", diff --git a/package.json b/package.json index c3f0d5dc2..31a810541 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,14 @@ "@babel/preset-env": "7.8.7", "@babel/register": "7.8.6", "@testing-library/react-native": "5.0.3", + "@types/enzyme": "3.10.5", + "@types/enzyme-adapter-react-16": "1.0.6", + "@types/jest": "25.1.4", + "@types/react": "16.9.23", + "@types/react-native": "0.61.23", + "@types/react-test-renderer": "16.9.2", + "@typescript-eslint/eslint-plugin": "2.25.0", + "@typescript-eslint/parser": "2.25.0", "babel-eslint": "10.1.0", "babel-jest": "25.1.0", "babel-plugin-module-resolver": "4.0.0", @@ -104,6 +112,7 @@ "eslint-plugin-mattermost": "github:mattermost/eslint-plugin-mattermost#070ce792d105482ffb2b27cfc0b7e78b3d20acee", "eslint-plugin-react": "7.19.0", "harmony-reflect": "1.6.1", + "isomorphic-fetch": "2.2.1", "jest": "25.1.0", "jest-cli": "25.1.0", "jest-enzyme": "7.1.2", @@ -112,49 +121,31 @@ "metro-react-native-babel-preset": "0.58.0", "mmjstool": "github:mattermost/mattermost-utilities#086f4ffdca4e31a0be22f6bcdfa093ed83fb29e", "mock-async-storage": "2.2.0", + "mock-socket": "9.0.3", + "nock": "12.0.3", "nyc": "15.0.0", "patch-package": "6.2.1", "react-dom": "16.13.0", "react-test-renderer": "16.13.0", "redux-mock-store": "1.5.4", + "redux-persist-node-storage": "2.0.0", "remote-redux-devtools": "0.5.16", "socketcluster": "16.0.1", + "ts-jest": "25.2.1", + "typescript": "3.8.3", "underscore": "1.9.2", "util": "0.12.2" }, "scripts": { "start": "node ./node_modules/react-native/local-cli/cli.js start", - "check": "eslint --ignore-path .gitignore --ignore-pattern node_modules --quiet .", + "check": "eslint --ignore-path .gitignore --ignore-pattern node_modules --quiet . && npm run tsc", "fix": "eslint --ignore-path .gitignore --ignore-pattern node_modules --quiet . --fix", "postinstall": "make post-install", + "tsc": "tsc --noEmit --skipLibCheck", "test": "jest --forceExit --detectOpenHandles", "test:watch": "jest --watch", "test:coverage": "jest --coverage", "updatesnapshot": "jest --updateSnapshot", "mmjstool": "mmjstool" - }, - "jest": { - "clearMocks": true, - "collectCoverageFrom": [ - "app/**/*.{js,jsx}" - ], - "coverageReporters": [ - "lcov", - "text-summary" - ], - "preset": "@testing-library/react-native", - "setupFilesAfterEnv": [ - "/test/setup.js", - "/node_modules/jest-enzyme/lib/index.js" - ], - "testPathIgnorePatterns": [ - "/node_modules/" - ], - "moduleNameMapper": { - "assets/images/video_player/(.*).png": "/dist/assets/images/video_player/$1@2x.png" - }, - "transformIgnorePatterns": [ - "node_modules/(?!react-native|jail-monkey|@sentry/react-native|react-navigation|@react-native-community/cameraroll)" - ] } } diff --git a/packager/moduleNames.js b/packager/moduleNames.js index d3465b109..fdfcc2d91 100644 --- a/packager/moduleNames.js +++ b/packager/moduleNames.js @@ -426,11 +426,9 @@ module.exports = [ 'node_modules/mattermost-redux/actions/teams.js', 'node_modules/mattermost-redux/actions/timezone.js', 'node_modules/mattermost-redux/actions/users.js', - 'node_modules/mattermost-redux/actions/websocket.js', 'node_modules/mattermost-redux/client/client4.js', 'node_modules/mattermost-redux/client/fetch_etag.js', 'node_modules/mattermost-redux/client/index.js', - 'node_modules/mattermost-redux/client/websocket_client.js', 'node_modules/mattermost-redux/constants/alerts.js', 'node_modules/mattermost-redux/constants/emoji.js', 'node_modules/mattermost-redux/constants/files.js', diff --git a/packager/modulePaths.js b/packager/modulePaths.js index 8942e574e..f7ff9dc4e 100644 --- a/packager/modulePaths.js +++ b/packager/modulePaths.js @@ -415,11 +415,9 @@ module.exports = [ './node_modules/node_modules/mattermost-redux/actions/teams.js', './node_modules/node_modules/mattermost-redux/actions/timezone.js', './node_modules/node_modules/mattermost-redux/actions/users.js', - './node_modules/node_modules/mattermost-redux/actions/websocket.js', './node_modules/node_modules/mattermost-redux/client/client4.js', './node_modules/node_modules/mattermost-redux/client/fetch_etag.js', './node_modules/node_modules/mattermost-redux/client/index.js', - './node_modules/node_modules/mattermost-redux/client/websocket_client.js', './node_modules/node_modules/mattermost-redux/constants/alerts.js', './node_modules/node_modules/mattermost-redux/constants/emoji.js', './node_modules/node_modules/mattermost-redux/constants/files.js', diff --git a/scripts/make-dist-assets.js b/scripts/make-dist-assets.js index 3674fbd3c..d9f82ac58 100644 --- a/scripts/make-dist-assets.js +++ b/scripts/make-dist-assets.js @@ -3,14 +3,14 @@ /* eslint-disable no-console */ -var fs = require('fs'); +const fs = require('fs'); // Takes the files in rootA/path, overwrites or merges them with the corresponding file in rootB/path, and places the // resulting file in dest/path. JSON files that exist in both places are shallowly (TODO maybe deeply) merged and all // other types of files are overwritten. function leftMergeDirs(rootA, rootB, dest, path) { - var pathA = rootA + path; - var pathB = rootB + path; + const pathA = rootA + path; + const pathB = rootB + path; try { fs.mkdirSync(dest + path); @@ -22,11 +22,11 @@ function leftMergeDirs(rootA, rootB, dest, path) { } } - for (var file of fs.readdirSync(pathA)) { - var filePathA = pathA + file; - var filePathB = pathB + file; + for (const file of fs.readdirSync(pathA)) { + const filePathA = pathA + file; + const filePathB = pathB + file; - var stat; + let stat; try { stat = fs.statSync(filePathA); } catch (e) { @@ -37,7 +37,7 @@ function leftMergeDirs(rootA, rootB, dest, path) { if (stat.isDirectory()) { leftMergeDirs(rootA, rootB, dest, path + file + '/'); } else { - var fileA; + let fileA; try { fileA = fs.readFileSync(filePathA); } catch (e) { @@ -45,8 +45,8 @@ function leftMergeDirs(rootA, rootB, dest, path) { throw e; } - var outPath = dest + path + file; - var out; + const outPath = dest + path + file; + let out; try { out = fs.createWriteStream(outPath); } catch (e) { @@ -54,7 +54,7 @@ function leftMergeDirs(rootA, rootB, dest, path) { throw e; } - var fileB = null; + let fileB = null; try { fileB = fs.readFileSync(filePathB); } catch (e) { @@ -64,8 +64,8 @@ function leftMergeDirs(rootA, rootB, dest, path) { if (fileB) { if (file.endsWith('.json')) { // We need to merge these files - var objA = JSON.parse(fileA); - var objB = JSON.parse(fileB); + const objA = JSON.parse(fileA); + const objB = JSON.parse(fileB); console.log('Merging ' + filePathA + ' with ' + filePathB + ' into ' + outPath); out.write(JSON.stringify(Object.assign({}, objA, objB))); diff --git a/share_extension/android/actions/index.js b/share_extension/android/actions/index.js index 001fdb6dc..0d182cf36 100644 --- a/share_extension/android/actions/index.js +++ b/share_extension/android/actions/index.js @@ -5,13 +5,12 @@ import {fetchMyChannelsAndMembers} from 'mattermost-redux/actions/channels'; import {getRedirectChannelNameForTeam, getChannelsNameMapInTeam} from 'mattermost-redux/selectors/entities/channels'; import {getChannelByName} from 'mattermost-redux/utils/channel_utils'; -import {loadProfilesAndTeamMembersForDMSidebar} from 'app/actions/views/channel'; +import {loadChannelsForTeam} from 'app/actions/views/channel'; import {ViewTypes} from 'app/constants'; export function getTeamChannels(teamId) { return async (dispatch, getState) => { - await dispatch(fetchMyChannelsAndMembers(teamId)); - dispatch(loadProfilesAndTeamMembersForDMSidebar(teamId)); + await dispatch(loadChannelsForTeam(teamId)); const state = getState(); const channelsInTeam = getChannelsNameMapInTeam(state, teamId); diff --git a/share_extension/android/extension_post/extension_post.test.js b/share_extension/android/extension_post/extension_post.test.js index 147e3ad84..3170ed912 100644 --- a/share_extension/android/extension_post/extension_post.test.js +++ b/share_extension/android/extension_post/extension_post.test.js @@ -10,7 +10,7 @@ import ExtensionPost from './extension_post'; jest.spyOn(Alert, 'alert').mockReturnValue(true); jest.spyOn(PermissionsAndroid, 'check').mockReturnValue(PermissionsAndroid.RESULTS.GRANTED); -jest.mock('react-navigation-stack/lib/module/views/TouchableItem', () => {}); +jest.mock('react-navigation-stack/lib/module/views/TouchableItem', () => null); jest.mock('app/mattermost_managed', () => ({ getConfig: jest.fn().mockReturnValue(false), diff --git a/test/setup.js b/test/setup.js index ed1962968..78e6b3e70 100644 --- a/test/setup.js +++ b/test/setup.js @@ -5,12 +5,12 @@ import * as ReactNative from 'react-native'; import MockAsyncStorage from 'mock-async-storage'; import {configure} from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; + configure({adapter: new Adapter()}); const mockImpl = new MockAsyncStorage(); jest.mock('@react-native-community/async-storage', () => mockImpl); global.window = {}; -global.fetch = jest.fn(() => Promise.resolve()); /* eslint-disable no-console */ @@ -165,9 +165,9 @@ jest.mock('app/actions/navigation', () => ({ dismissOverlay: jest.fn(() => Promise.resolve()), })); -let logs; -let warns; -let errors; +let logs = []; +let warns = []; +let errors = []; beforeAll(() => { console.originalLog = console.log; console.log = jest.fn((...params) => { diff --git a/test/test_helper.js b/test/test_helper.js index 8bfe368ff..b85cef79f 100644 --- a/test/test_helper.js +++ b/test/test_helper.js @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import assert from 'assert'; +import nock from 'nock'; import Config from 'assets/config.json'; @@ -19,11 +20,139 @@ class TestHelper { this.basicPost = null; } + activateMocking() { + if (!nock.isActive()) { + nock.activate(); + } + } + assertStatusOkay = (data) => { assert(data); assert(data.status === 'OK'); }; + createClient = () => { + const client = new Client(); + + client.setUrl(Config.DefaultServerUrl); + + return client; + }; + + fakeChannel = (teamId) => { + const name = this.generateId(); + + return { + name, + team_id: teamId, + display_name: `Unit Test ${name}`, + type: 'O', + }; + }; + + fakeChannelWithId = (teamId) => { + return { + ...this.fakeChannel(teamId), + id: this.generateId(), + create_at: 1507840900004, + update_at: 1507840900004, + delete_at: 0, + }; + }; + + fakeChannelMember = (userId, channelId) => { + return { + user_id: userId, + channel_id: channelId, + notify_props: {}, + roles: 'system_user', + }; + }; + + fakeEmail = () => { + return 'success' + this.generateId() + '@simulator.amazonses.com'; + }; + + fakePost = (channelId) => { + const time = Date.now(); + + return { + id: this.generateId(), + channel_id: channelId, + create_at: time, + update_at: time, + message: `Unit Test ${this.generateId()}`, + }; + }; + + fakePostWithId = (channelId) => { + return { + ...this.fakePost(channelId), + id: this.generateId(), + create_at: 1507840900004, + update_at: 1507840900004, + delete_at: 0, + }; + }; + + fakeTeam = () => { + const name = this.generateId(); + let inviteId = this.generateId(); + if (inviteId.length > 32) { + inviteId = inviteId.substring(0, 32); + } + + return { + name, + display_name: `Unit Test ${name}`, + type: 'O', + email: this.fakeEmail(), + allowed_domains: '', + invite_id: inviteId, + }; + }; + + fakeTeamWithId = () => { + return { + ...this.fakeTeam(), + id: this.generateId(), + create_at: 1507840900004, + update_at: 1507840900004, + delete_at: 0, + }; + }; + + fakeTeamMember = (userId, teamId) => { + return { + user_id: userId, + team_id: teamId, + roles: 'team_user', + delete_at: 0, + scheme_user: false, + scheme_admin: false, + }; + }; + + fakeUser = () => { + return { + email: this.fakeEmail(), + allow_marketing: true, + password: PASSWORD, + username: this.generateId(), + roles: 'system_user', + }; + }; + + fakeUserWithId = (id = this.generateId()) => { + return { + ...this.fakeUser(), + id, + create_at: 1507840900004, + update_at: 1507840900004, + delete_at: 0, + }; + }; + generateId = () => { // Implementation taken from http://stackoverflow.com/a/2117523 let id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; @@ -44,87 +173,91 @@ class TestHelper { return 'uid' + id; }; - createClient = () => { - const client = new Client(); - - client.setUrl(Config.DefaultServerUrl); - - return client; - }; - - fakeEmail = () => { - return 'success' + this.generateId() + '@simulator.amazonses.com'; - }; - - fakeUser = () => { - return { - email: this.fakeEmail(), - allow_marketing: true, - password: PASSWORD, - username: this.generateId(), + initMockEntities = () => { + this.basicUser = this.fakeUserWithId(); + this.basicUser.roles = 'system_user system_admin'; + this.basicTeam = this.fakeTeamWithId(); + this.basicTeamMember = this.fakeTeamMember(this.basicUser.id, this.basicTeam.id); + this.basicChannel = this.fakeChannelWithId(this.basicTeam.id); + this.basicChannelMember = this.fakeChannelMember(this.basicUser.id, this.basicChannel.id); + this.basicPost = {...this.fakePostWithId(this.basicChannel.id), create_at: 1507841118796}; + this.basicRoles = { + system_admin: { + id: this.generateId(), + name: 'system_admin', + display_name: 'authentication.roles.global_admin.name', + description: 'authentication.roles.global_admin.description', + permissions: [ + 'system_admin_permission', + ], + scheme_managed: true, + built_in: true, + }, + system_user: { + id: this.generateId(), + name: 'system_user', + display_name: 'authentication.roles.global_user.name', + description: 'authentication.roles.global_user.description', + permissions: [ + 'system_user_permission', + ], + scheme_managed: true, + built_in: true, + }, + team_admin: { + id: this.generateId(), + name: 'team_admin', + display_name: 'authentication.roles.team_admin.name', + description: 'authentication.roles.team_admin.description', + permissions: [ + 'team_admin_permission', + ], + scheme_managed: true, + built_in: true, + }, + team_user: { + id: this.generateId(), + name: 'team_user', + display_name: 'authentication.roles.team_user.name', + description: 'authentication.roles.team_user.description', + permissions: [ + 'team_user_permission', + ], + scheme_managed: true, + built_in: true, + }, + channel_admin: { + id: this.generateId(), + name: 'channel_admin', + display_name: 'authentication.roles.channel_admin.name', + description: 'authentication.roles.channel_admin.description', + permissions: [ + 'channel_admin_permission', + ], + scheme_managed: true, + built_in: true, + }, + channel_user: { + id: this.generateId(), + name: 'channel_user', + display_name: 'authentication.roles.channel_user.name', + description: 'authentication.roles.channel_user.description', + permissions: [ + 'channel_user_permission', + ], + scheme_managed: true, + built_in: true, + }, }; - }; - - fakeTeam = () => { - const name = this.generateId(); - let inviteId = this.generateId(); - if (inviteId.length > 32) { - inviteId = inviteId.substring(0, 32); - } - - return { - name, - display_name: `Unit Test ${name}`, - type: 'O', - email: this.fakeEmail(), - allowed_domains: '', - invite_id: inviteId, - }; - }; - - fakeChannel = (teamId) => { - const name = this.generateId(); - - return { - name, - team_id: teamId, - display_name: `Unit Test ${name}`, - type: 'O', - }; - }; - - fakeChannelMember = (userId, channelId) => { - return { - user_id: userId, - channel_id: channelId, - notify_props: {}, - roles: 'system_user', - }; - }; - - fakePost = (channelId) => { - const time = Date.now(); - - return { - id: this.generateId(), - channel_id: channelId, - create_at: time, - update_at: time, - message: `Unit Test ${this.generateId()}`, - }; - }; + this.basicScheme = this.mockSchemeWithId(); + } initBasic = async (client = this.createClient()) => { client.setUrl(Config.TestServerUrl || Config.DefaultServerUrl); this.basicClient = client; - this.basicUser = await client.createUser(this.fakeUser()); - await client.login(this.basicUser.email, PASSWORD); - - this.basicTeam = await client.createTeam(this.fakeTeam()); - - this.basicChannel = await client.createChannel(this.fakeChannel(this.basicTeam.id)); - this.basicPost = await client.createPost(this.basicTeam.id, this.fakePost(this.basicChannel.id)); + this.initMockEntities(); + this.activateMocking(); return { client: this.basicClient, @@ -135,11 +268,39 @@ class TestHelper { }; }; - wait = () => { - return new Promise((resolve) => { - setTimeout(() => resolve(), 1000); - }); + mockScheme = () => { + return { + name: this.generateId(), + description: this.generateId(), + scope: 'channel', + defaultchanneladminrole: false, + defaultchanneluserrole: false, + }; + }; + + mockSchemeWithId = () => { + return { + ...this.mockScheme(), + id: this.generateId(), + create_at: 1507840900004, + update_at: 1507840900004, + delete_at: 0, + }; + }; + + tearDown = async () => { + nock.restore(); + + this.basicClient4 = null; + this.basicUser = null; + this.basicTeam = null; + this.basicTeamMember = null; + this.basicChannel = null; + this.basicChannelMember = null; + this.basicPost = null; } + + wait = (time) => new Promise((resolve) => setTimeout(resolve, time)) } export default new TestHelper(); diff --git a/test/test_store.js b/test/test_store.js new file mode 100644 index 000000000..3986be3ca --- /dev/null +++ b/test/test_store.js @@ -0,0 +1,55 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import {AsyncNodeStorage} from 'redux-persist-node-storage'; +import {createTransform, persistStore} from 'redux-persist'; + +import configureStore from 'mattermost-redux/store'; + +export default async function testConfigureStore(preloadedState) { + const storageTransform = createTransform( + () => ({}), + () => ({}), + ); + + const offlineConfig = { + detectNetwork: (callback) => callback(true), + persist: (store, options) => { + return persistStore(store, {storage: new AsyncNodeStorage('./.tmp'), ...options}); + }, + persistOptions: { + debounce: 1000, + transforms: [ + storageTransform, + ], + whitelist: [], + }, + retry: (action, retries) => 200 * (retries + 1), + discard: (error, action, retries) => { + if (action.meta && action.meta.offline.hasOwnProperty('maxRetry')) { + return retries >= action.meta.offline.maxRetry; + } + + return retries >= 1; + }, + }; + + const store = configureStore(preloadedState, {}, offlineConfig, () => ({}), {enableBuffer: false}); + + const wait = () => new Promise((resolve) => setTimeout(resolve), 300); //eslint-disable-line + await wait(); + + return store; +} + +// This should probably be replaced by redux-mock-store like the web app +export function mockDispatch(dispatch) { + const mocked = (action) => { + dispatch(action); + + mocked.actions.push(action); + }; + + mocked.actions = []; + + return mocked; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..720d45b6c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,50 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": [ + "es6" + ], + "allowJs": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "isolatedModules": true, + "jsx": "react", + "moduleResolution": "node", + "noEmit": false, + "strict": false, + "importHelpers": true, + "declaration": true, + "sourceMap": false, + "rootDir": "./", + "outDir": "./", + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": false, + "strictPropertyInitialization": true, + "noImplicitThis": false, + "alwaysStrict": true, + "noUnusedLocals": false, + "downlevelIteration": true, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@actions/*": ["app/actions/*"], + "@constants/*": ["app/constants/*"], + "@constants": ["app/constants/index"], + "@selectors/*": ["app/selectors/*"], + "@telemetry/*": ["/app/telemetry/*"], + "@utils/*": ["app/utils/*"], + "@websocket": ["app/client/websocket"], + "*": ["./*", "node_modules/*"] + } + }, + "exclude": [ + "node_modules", + "build", + "babel.config.js", + "metro.config.js", + "jest.config.js", + ] +} \ No newline at end of file diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 000000000..69d69d92c --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "jsx": "react", + "module": "commonjs" + } + } \ No newline at end of file